]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add sipcc to tree for sdp parser
authorMichael Jerris <mike@jerris.com>
Tue, 18 Dec 2012 01:14:30 +0000 (20:14 -0500)
committerMichael Jerris <mike@jerris.com>
Tue, 18 Dec 2012 01:15:23 +0000 (20:15 -0500)
369 files changed:
libs/sipcc/core/ccapp/CCProvider.h [new file with mode: 0755]
libs/sipcc/core/ccapp/call_logger.c [new file with mode: 0644]
libs/sipcc/core/ccapp/call_logger.h [new file with mode: 0644]
libs/sipcc/core/ccapp/capability_set.c [new file with mode: 0644]
libs/sipcc/core/ccapp/capability_set.h [new file with mode: 0644]
libs/sipcc/core/ccapp/cc_blf.c [new file with mode: 0644]
libs/sipcc/core/ccapp/cc_call_feature.c [new file with mode: 0644]
libs/sipcc/core/ccapp/cc_config.c [new file with mode: 0644]
libs/sipcc/core/ccapp/cc_device_feature.c [new file with mode: 0644]
libs/sipcc/core/ccapp/cc_device_manager.c [new file with mode: 0644]
libs/sipcc/core/ccapp/cc_device_manager.h [new file with mode: 0644]
libs/sipcc/core/ccapp/cc_info.c [new file with mode: 0644]
libs/sipcc/core/ccapp/cc_service.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_call.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_call_info.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_config.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_device.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_device_info.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_feature_info.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_line.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_line_info.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_service.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_snapshot.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapi_snapshot.h [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapp_task.c [new file with mode: 0644]
libs/sipcc/core/ccapp/ccapp_task.h [new file with mode: 0644]
libs/sipcc/core/ccapp/ccprovider.c [new file with mode: 0755]
libs/sipcc/core/ccapp/conf_roster.c [new file with mode: 0644]
libs/sipcc/core/ccapp/conf_roster.h [new file with mode: 0644]
libs/sipcc/core/ccapp/sessionHash.c [new file with mode: 0755]
libs/sipcc/core/ccapp/sessionHash.h [new file with mode: 0755]
libs/sipcc/core/common/cfgfile_utils.c [new file with mode: 0755]
libs/sipcc/core/common/cfgfile_utils.h [new file with mode: 0755]
libs/sipcc/core/common/config_api.c [new file with mode: 0755]
libs/sipcc/core/common/config_parser.c [new file with mode: 0644]
libs/sipcc/core/common/config_parser.h [new file with mode: 0644]
libs/sipcc/core/common/init.c [new file with mode: 0755]
libs/sipcc/core/common/logger.c [new file with mode: 0755]
libs/sipcc/core/common/logger.h [new file with mode: 0644]
libs/sipcc/core/common/logmsg.h [new file with mode: 0644]
libs/sipcc/core/common/misc.c [new file with mode: 0644]
libs/sipcc/core/common/plat.c [new file with mode: 0644]
libs/sipcc/core/common/platform_api.c [new file with mode: 0755]
libs/sipcc/core/common/prot_cfgmgr_private.h [new file with mode: 0755]
libs/sipcc/core/common/prot_configmgr.c [new file with mode: 0755]
libs/sipcc/core/common/prot_configmgr.h [new file with mode: 0755]
libs/sipcc/core/common/resource_manager.c [new file with mode: 0644]
libs/sipcc/core/common/resource_manager.h [new file with mode: 0644]
libs/sipcc/core/common/sip_socket_api.c [new file with mode: 0755]
libs/sipcc/core/common/subscription_handler.c [new file with mode: 0755]
libs/sipcc/core/common/subscription_handler.h [new file with mode: 0755]
libs/sipcc/core/common/text_strings.c [new file with mode: 0755]
libs/sipcc/core/common/text_strings.h [new file with mode: 0755]
libs/sipcc/core/common/ui.c [new file with mode: 0755]
libs/sipcc/core/gsm/ccapi.c [new file with mode: 0755]
libs/sipcc/core/gsm/ccapi_strings.c [new file with mode: 0644]
libs/sipcc/core/gsm/dcsm.c [new file with mode: 0755]
libs/sipcc/core/gsm/fim.c [new file with mode: 0755]
libs/sipcc/core/gsm/fsm.c [new file with mode: 0755]
libs/sipcc/core/gsm/fsmb2bcnf.c [new file with mode: 0755]
libs/sipcc/core/gsm/fsmcac.c [new file with mode: 0755]
libs/sipcc/core/gsm/fsmcnf.c [new file with mode: 0755]
libs/sipcc/core/gsm/fsmdef.c [new file with mode: 0755]
libs/sipcc/core/gsm/fsmxfr.c [new file with mode: 0755]
libs/sipcc/core/gsm/gsm.c [new file with mode: 0755]
libs/sipcc/core/gsm/gsm_sdp.c [new file with mode: 0644]
libs/sipcc/core/gsm/gsm_sdp_crypto.c [new file with mode: 0644]
libs/sipcc/core/gsm/h/fim.h [new file with mode: 0755]
libs/sipcc/core/gsm/h/fsm.h [new file with mode: 0755]
libs/sipcc/core/gsm/h/gsm.h [new file with mode: 0755]
libs/sipcc/core/gsm/h/gsm_sdp.h [new file with mode: 0644]
libs/sipcc/core/gsm/h/lsm.h [new file with mode: 0755]
libs/sipcc/core/gsm/h/lsm_private.h [new file with mode: 0644]
libs/sipcc/core/gsm/h/sm.h [new file with mode: 0755]
libs/sipcc/core/gsm/lsm.c [new file with mode: 0755]
libs/sipcc/core/gsm/media_cap_tbl.c [new file with mode: 0644]
libs/sipcc/core/gsm/sm.c [new file with mode: 0755]
libs/sipcc/core/gsm/subapi.c [new file with mode: 0755]
libs/sipcc/core/includes/ccSession.h [new file with mode: 0755]
libs/sipcc/core/includes/ccapi.h [new file with mode: 0755]
libs/sipcc/core/includes/check_sync.h [new file with mode: 0644]
libs/sipcc/core/includes/ci.h [new file with mode: 0644]
libs/sipcc/core/includes/config.h [new file with mode: 0755]
libs/sipcc/core/includes/configapp.h [new file with mode: 0644]
libs/sipcc/core/includes/configmgr.h [new file with mode: 0755]
libs/sipcc/core/includes/debug.h [new file with mode: 0644]
libs/sipcc/core/includes/dialplan.h [new file with mode: 0755]
libs/sipcc/core/includes/dialplanint.h [new file with mode: 0755]
libs/sipcc/core/includes/digcalc.h [new file with mode: 0644]
libs/sipcc/core/includes/dns_utils.h [new file with mode: 0644]
libs/sipcc/core/includes/dtmf.h [new file with mode: 0644]
libs/sipcc/core/includes/embedded.h [new file with mode: 0644]
libs/sipcc/core/includes/intelpentiumtypes.h [new file with mode: 0644]
libs/sipcc/core/includes/kpml_common_util.h [new file with mode: 0755]
libs/sipcc/core/includes/kpmlmap.h [new file with mode: 0755]
libs/sipcc/core/includes/md5.h [new file with mode: 0644]
libs/sipcc/core/includes/memory.h [new file with mode: 0755]
libs/sipcc/core/includes/misc_apps_task.h [new file with mode: 0644]
libs/sipcc/core/includes/misc_util.h [new file with mode: 0644]
libs/sipcc/core/includes/phntask.h [new file with mode: 0644]
libs/sipcc/core/includes/phone.h [new file with mode: 0644]
libs/sipcc/core/includes/phone_debug.h [new file with mode: 0644]
libs/sipcc/core/includes/phone_platform_constants.h [new file with mode: 0755]
libs/sipcc/core/includes/phone_types.h [new file with mode: 0644]
libs/sipcc/core/includes/platform_api.h [new file with mode: 0644]
libs/sipcc/core/includes/pres_sub_not_handler.h [new file with mode: 0644]
libs/sipcc/core/includes/publish_int.h [new file with mode: 0644]
libs/sipcc/core/includes/rcc_int_types.h [new file with mode: 0755]
libs/sipcc/core/includes/regexp.h [new file with mode: 0644]
libs/sipcc/core/includes/ringlist.h [new file with mode: 0755]
libs/sipcc/core/includes/rtp_defs.h [new file with mode: 0644]
libs/sipcc/core/includes/scSession.h [new file with mode: 0755]
libs/sipcc/core/includes/session.h [new file with mode: 0755]
libs/sipcc/core/includes/sessionConstants.h [new file with mode: 0755]
libs/sipcc/core/includes/sessionTypes.h [new file with mode: 0755]
libs/sipcc/core/includes/sessuri.h [new file with mode: 0644]
libs/sipcc/core/includes/singly_link_list.h [new file with mode: 0644]
libs/sipcc/core/includes/sip_socket_api.h [new file with mode: 0755]
libs/sipcc/core/includes/sntp.h [new file with mode: 0644]
libs/sipcc/core/includes/string_lib.h [new file with mode: 0755]
libs/sipcc/core/includes/subapi.h [new file with mode: 0755]
libs/sipcc/core/includes/task.h [new file with mode: 0755]
libs/sipcc/core/includes/time2.h [new file with mode: 0644]
libs/sipcc/core/includes/timer.h [new file with mode: 0755]
libs/sipcc/core/includes/tnpphone.h [new file with mode: 0644]
libs/sipcc/core/includes/uart.h [new file with mode: 0755]
libs/sipcc/core/includes/uiapi.h [new file with mode: 0644]
libs/sipcc/core/includes/upgrade.h [new file with mode: 0644]
libs/sipcc/core/includes/util_ios_queue.h [new file with mode: 0644]
libs/sipcc/core/includes/util_parse.h [new file with mode: 0644]
libs/sipcc/core/includes/util_string.h [new file with mode: 0644]
libs/sipcc/core/includes/www.h [new file with mode: 0644]
libs/sipcc/core/includes/xml_defs.h [new file with mode: 0644]
libs/sipcc/core/sdp/ccsdp.c [new file with mode: 0644]
libs/sipcc/core/sdp/sdp.h [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_access.c [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_attr.c [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_attr_access.c [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_base64.c [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_base64.h [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_config.c [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_main.c [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_os_defs.h [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_private.h [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_services_unix.c [new file with mode: 0755]
libs/sipcc/core/sdp/sdp_services_win32.c [new file with mode: 0755]
libs/sipcc/core/sdp/sdp_token.c [new file with mode: 0644]
libs/sipcc/core/sdp/sdp_utils.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_callinfo.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_cc.c [new file with mode: 0755]
libs/sipcc/core/sipstack/ccsip_common_util.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_core.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_debug.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_info.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_messaging.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_platform.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_platform_tcp.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_platform_timers.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_platform_tls.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_platform_udp.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_pmh.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_publish.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_register.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_reldev.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_sdp.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_spi_utils.c [new file with mode: 0755]
libs/sipcc/core/sipstack/ccsip_subsmanager.c [new file with mode: 0644]
libs/sipcc/core/sipstack/ccsip_task.c [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_callinfo.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_cc.h [new file with mode: 0755]
libs/sipcc/core/sipstack/h/ccsip_common_cb.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_core.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_credentials.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_macros.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_messaging.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_platform.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_platform_tcp.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_platform_timers.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_platform_tls.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_platform_udp.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_pmh.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_protocol.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_publish.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_register.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_reldev.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_sdp.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_sim.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_spi_utils.h [new file with mode: 0755]
libs/sipcc/core/sipstack/h/ccsip_subsmanager.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/ccsip_task.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/httpish.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/httpish_protocol.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/pmhdefs.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/pmhutils.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/regmgrapi.h [new file with mode: 0755]
libs/sipcc/core/sipstack/h/sip_ccm_transport.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/sip_common_regmgr.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/sip_common_transport.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/sip_csps_transport.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/sip_interface_regmgr.h [new file with mode: 0644]
libs/sipcc/core/sipstack/h/sip_platform_task.h [new file with mode: 0644]
libs/sipcc/core/sipstack/httpish.c [new file with mode: 0644]
libs/sipcc/core/sipstack/pmhutils.c [new file with mode: 0644]
libs/sipcc/core/sipstack/sip_common_regmgr.c [new file with mode: 0644]
libs/sipcc/core/sipstack/sip_common_transport.c [new file with mode: 0644]
libs/sipcc/core/sipstack/sip_csps_transport.c [new file with mode: 0644]
libs/sipcc/core/sipstack/sip_interface_regmgr.c [new file with mode: 0644]
libs/sipcc/core/sipstack/sip_platform_task.c [new file with mode: 0644]
libs/sipcc/core/sipstack/sip_platform_win32_task.c [new file with mode: 0755]
libs/sipcc/core/src-common/configapp.c [new file with mode: 0644]
libs/sipcc/core/src-common/dialplan.c [new file with mode: 0755]
libs/sipcc/core/src-common/dialplanint.c [new file with mode: 0755]
libs/sipcc/core/src-common/digcalc.c [new file with mode: 0644]
libs/sipcc/core/src-common/kpml_common_util.c [new file with mode: 0755]
libs/sipcc/core/src-common/kpmlmap.c [new file with mode: 0755]
libs/sipcc/core/src-common/md5.c [new file with mode: 0644]
libs/sipcc/core/src-common/misc_apps_task.c [new file with mode: 0755]
libs/sipcc/core/src-common/pres_sub_not_handler.c [new file with mode: 0755]
libs/sipcc/core/src-common/publish_int.c [new file with mode: 0644]
libs/sipcc/core/src-common/singly_link_list.c [new file with mode: 0644]
libs/sipcc/core/src-common/sll_lite.c [new file with mode: 0644]
libs/sipcc/core/src-common/string_lib.c [new file with mode: 0755]
libs/sipcc/core/src-common/util_ios_queue.c [new file with mode: 0644]
libs/sipcc/core/src-common/util_parse.c [new file with mode: 0644]
libs/sipcc/core/src-common/util_string.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_align.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_assert.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_errno.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_errno.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_in.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_init.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_ipc.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_ipc.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_locks.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_locks.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_private.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_rand.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_socket.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_socket.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_stdio.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_stdio.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_string.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_string.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_strings.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_threads.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_time.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_timers.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_timers_using_select.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_tst.c [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_tst.h [new file with mode: 0644]
libs/sipcc/cpr/android/cpr_android_types.h [new file with mode: 0644]
libs/sipcc/cpr/common/cpr_string.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_align.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_assert.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_errno.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_errno.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_in.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_init.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_ipc.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_ipc.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_locks.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_locks.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_private.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_rand.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_socket.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_socket.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_stdio.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_stdio.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_string.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_string.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_strings.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_threads.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_time.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_timers.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_timers_using_select.c [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_tst.h [new file with mode: 0644]
libs/sipcc/cpr/darwin/cpr_darwin_types.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_assert.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_debug.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_errno.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_in.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_ipc.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_locks.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_memory.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_rand.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_socket.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_stddef.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_stdio.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_stdlib.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_string.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_strings.h [new file with mode: 0755]
libs/sipcc/cpr/include/cpr_threads.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_time.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_timers.h [new file with mode: 0644]
libs/sipcc/cpr/include/cpr_types.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_align.h [new file with mode: 0755]
libs/sipcc/cpr/linux/cpr_linux_assert.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_errno.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_errno.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_in.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_init.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_ipc.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_ipc.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_locks.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_locks.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_private.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_rand.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_socket.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_socket.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_stdio.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_stdio.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_string.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_string.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_strings.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_threads.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_time.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_timers.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_timers_using_select.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_tst.c [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_tst.h [new file with mode: 0644]
libs/sipcc/cpr/linux/cpr_linux_types.h [new file with mode: 0644]
libs/sipcc/include/cc_blf.h [new file with mode: 0644]
libs/sipcc/include/cc_blf_listener.h [new file with mode: 0644]
libs/sipcc/include/cc_call_feature.h [new file with mode: 0644]
libs/sipcc/include/cc_call_listener.h [new file with mode: 0644]
libs/sipcc/include/cc_config.h [new file with mode: 0644]
libs/sipcc/include/cc_constants.h [new file with mode: 0644]
libs/sipcc/include/cc_debug.h [new file with mode: 0644]
libs/sipcc/include/cc_device_feature.h [new file with mode: 0644]
libs/sipcc/include/cc_device_listener.h [new file with mode: 0644]
libs/sipcc/include/cc_info.h [new file with mode: 0644]
libs/sipcc/include/cc_info_listener.h [new file with mode: 0644]
libs/sipcc/include/cc_service.h [new file with mode: 0644]
libs/sipcc/include/cc_service_listener.h [new file with mode: 0644]
libs/sipcc/include/cc_types.h [new file with mode: 0644]
libs/sipcc/include/ccapi_call.h [new file with mode: 0644]
libs/sipcc/include/ccapi_call_info.h [new file with mode: 0644]
libs/sipcc/include/ccapi_call_listener.h [new file with mode: 0644]
libs/sipcc/include/ccapi_calllog.h [new file with mode: 0644]
libs/sipcc/include/ccapi_conf_roster.h [new file with mode: 0644]
libs/sipcc/include/ccapi_device.h [new file with mode: 0644]
libs/sipcc/include/ccapi_device_info.h [new file with mode: 0644]
libs/sipcc/include/ccapi_device_listener.h [new file with mode: 0644]
libs/sipcc/include/ccapi_feature_info.h [new file with mode: 0644]
libs/sipcc/include/ccapi_line.h [new file with mode: 0644]
libs/sipcc/include/ccapi_line_info.h [new file with mode: 0644]
libs/sipcc/include/ccapi_line_listener.h [new file with mode: 0644]
libs/sipcc/include/ccapi_service.h [new file with mode: 0644]
libs/sipcc/include/ccapi_types.h [new file with mode: 0644]
libs/sipcc/include/ccsdp.h [new file with mode: 0644]
libs/sipcc/include/config_api.h [new file with mode: 0644]
libs/sipcc/include/dns_util.h [new file with mode: 0644]
libs/sipcc/include/peer_connection_types.h [new file with mode: 0644]
libs/sipcc/include/plat_api.h [new file with mode: 0644]
libs/sipcc/include/reset_api.h [new file with mode: 0644]
libs/sipcc/include/sll_lite.h [new file with mode: 0644]
libs/sipcc/include/vcm.h [new file with mode: 0755]
libs/sipcc/include/xml_parser_defines.h [new file with mode: 0644]
libs/sipcc/plat/common/dns_utils.c [new file with mode: 0644]
libs/sipcc/plat/common/plat_debug.h [new file with mode: 0644]
libs/sipcc/plat/common/tnp_blf.h [new file with mode: 0755]
libs/sipcc/plat/csf2g/model.c [new file with mode: 0644]
libs/sipcc/plat/csf2g/reset_api.c [new file with mode: 0644]
libs/sipcc/plat/darwin/netif.c [new file with mode: 0644]
libs/sipcc/plat/darwin/plat_api_stub.c [new file with mode: 0755]
libs/sipcc/plat/unix-common/random.c [new file with mode: 0644]
libs/sipcc/stub/cc_blf_stub.c [new file with mode: 0755]
libs/sipcc/stub/vcm_stub.c [new file with mode: 0755]

diff --git a/libs/sipcc/core/ccapp/CCProvider.h b/libs/sipcc/core/ccapp/CCProvider.h
new file mode 100755 (executable)
index 0000000..1053719
--- /dev/null
@@ -0,0 +1,144 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __CCPROVIDER_H__
+#define __CCPROVIDER_H__
+
+#include "cpr_types.h"
+#include "cpr_ipc.h"
+#include "cpr_socket.h"
+#include "cpr_memory.h"
+#include "cpr_errno.h"
+#include "cpr_in.h"
+#include "cpr_rand.h"
+#include "cpr_string.h"
+#include "cpr_threads.h"
+#include "phone_types.h"
+#include "session.h"
+
+#include "cc_constants.h"
+#include "ccapi_types.h"
+#include "conf_roster.h"
+
+#define CC_DEVICE_ID 0
+#include "ccapi_service.h"
+
+typedef enum {
+    FAILOVER,
+    FALLBACK,
+    NO_CUCM_SRST_AVAILABLE,
+    NONE_AVAIL,
+} Cucm_mode_t;
+
+typedef struct
+{
+    cc_reg_state_t state;
+    Cucm_mode_t    cucm_mode;
+    cc_boolean     inPreservation;
+       session_id_t   preservID;
+    unsigned int   mode;
+    unsigned int   cause;
+} CCAppGlobal_t;
+
+typedef struct cc_call_log_t_ {
+  string_t localPartyName;
+  string_t localPartyNumber;
+  string_t remotePartyName[2];
+  string_t remotePartyNumber[2];
+  string_t altPartyNumber[2];
+  cc_log_disposition_t logDisp;
+  cc_call_state_t callState;
+  time_t startTime;
+  cc_uint32_t duration;
+} cc_call_log_t;
+
+typedef struct cc_call_info_t_{
+    uint32_t     ref_count;
+    session_id_t  sess_id;
+    line_t        line;
+    callid_t      id;
+    uint16_t      inst;
+    cc_call_state_t    state;
+    cc_call_attr_t     attr;
+    cc_call_type_t     type;
+    cc_call_security_t security;
+    cc_call_policy_t   policy;
+    unsigned int  callref;
+    int           isSelected;
+    unsigned int  log_disp;
+    string_t      clg_name;
+    string_t      clg_number;
+    string_t      alt_number;
+    string_t      cld_name;
+    string_t      cld_number;
+    string_t      orig_called_name;
+    string_t      orig_called_number;
+    string_t      last_redir_name;
+    string_t      last_redir_number;
+    string_t      plcd_name;
+    string_t      plcd_number;
+    string_t      status;
+    char          gci[CC_MAX_GCID];
+    cc_int32_t    cause;
+    cc_int32_t    vid_dir;
+    cc_int32_t    vid_offer;
+    cc_boolean    is_conf;
+    cc_boolean    ringer_start;
+    cc_boolean    ringer_once;
+    cc_int32_t    ringer_mode;
+    cc_boolean    allowed_features[CCAPI_CALL_CAP_MAX];
+    cc_string_t   info_package;
+    cc_string_t   info_type;
+    cc_string_t   info_body;
+    cc_call_log_t call_log;
+    cc_boolean    audio_mute;
+    cc_boolean    video_mute;
+    cc_call_conference_Info_t call_conference;
+    cc_string_t   sdp;
+    unsigned int  media_stream_track_id;
+    unsigned int  media_stream_id;
+    const cc_media_constraints_t* cc_constraints;
+} session_data_t;
+
+typedef enum {
+    NO_ACTION=0,
+    RESET_ACTION,
+    RESTART_ACTION,
+    RE_REGISTER_ACTION,
+    STOP_ACTION,
+    DESTROY_ACTION
+} cc_action_t;
+
+#define CCAPP_SERVICE_CMD    1
+#define CCAPP_CREATE_SESSION 2
+#define CCAPP_CLOSE_SESSION  3
+#define CCAPP_INVOKE_FEATURE 4
+#define CCAPP_SESSION_UPDATE 5
+#define CCAPP_FEATURE_UPDATE 6
+#define CCAPP_UPDATELINES    7
+#define CCAPP_FAILOVER_IND   8
+#define CCAPP_FALLBACK_IND   9
+#define CCAPP_MODE_NOTIFY    10
+#define CCAPP_SHUTDOWN_ACK   11
+#define CCAPP_REG_ALL_FAIL   12
+#define CCAPP_INVOKEPROVIDER_FEATURE 13
+#define CCAPP_SEND_INFO      14
+#define CCAPP_RCVD_INFO      15
+#define CCAPP_LOGOUT_RESET   16
+#define CCAPP_THREAD_UNLOAD  17
+#define CCAPP_SESSION_MGMT   18
+
+extern cpr_status_e ccappTaskPostMsg(unsigned int msgId, void * data, uint16_t len, int appId);
+extern void ccappSyncSessionMgmt(session_mgmt_t *sessMgmt);
+extern void CCApp_task(void * arg);
+extern void *findhash(unsigned int key);
+extern session_id_t createSessionId(line_t line, callid_t call);
+extern void getLineIdAndCallId (line_t *line_id, callid_t *call_id);
+extern void ccp_handler(void* msg, int type);
+extern session_data_t * getDeepCopyOfSessionData(session_data_t *data);
+extern void cleanSessionData(session_data_t *data);
+extern cc_call_handle_t ccappGetConnectedCall();
+
+#endif
+
diff --git a/libs/sipcc/core/ccapp/call_logger.c b/libs/sipcc/core/ccapp/call_logger.c
new file mode 100644 (file)
index 0000000..e3ec264
--- /dev/null
@@ -0,0 +1,303 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdio.h"
+#include <time.h>
+#include "sessionHash.h"
+#include "CCProvider.h"
+#include "call_logger.h"
+
+
+
+void calllogger_init_call_log (cc_call_log_t *log)
+{
+    log->localPartyName = strlib_empty();
+    log->localPartyNumber = strlib_empty();
+    log->remotePartyName[0] = strlib_empty();
+    log->remotePartyName[1] = strlib_empty();
+    log->remotePartyNumber[0] = strlib_empty();
+    log->remotePartyNumber[1] = strlib_empty();
+    log->altPartyNumber[0] = strlib_empty();
+    log->altPartyNumber[1] = strlib_empty();
+    log->startTime = 0;
+    log->duration = 0;
+
+    log->logDisp = CC_LOGD_UNKNWN;
+    log->callState = MAX_CALL_STATES;
+}
+
+void calllogger_copy_call_log (cc_call_log_t *dest, cc_call_log_t * src)
+{
+    dest->localPartyName = strlib_copy(src->localPartyName);
+    dest->localPartyNumber = strlib_copy(src->localPartyNumber);
+    dest->remotePartyName[0] = strlib_copy(src->remotePartyName[0]);
+    dest->remotePartyName[1] = strlib_copy(src->remotePartyName[1]);
+    dest->remotePartyNumber[0] = strlib_copy(src->remotePartyNumber[0]);
+    dest->remotePartyNumber[1] = strlib_copy(src->remotePartyNumber[1]);
+    dest->altPartyNumber[0] = strlib_copy(src->altPartyNumber[0]);
+    dest->altPartyNumber[1] = strlib_copy(src->altPartyNumber[1]);
+    dest->startTime = src->startTime;
+    dest->duration = src->duration;
+
+    dest->logDisp = src->logDisp;
+    dest->callState = src->callState;
+}
+
+void calllogger_free_call_log (cc_call_log_t *log)
+{
+    strlib_free(log->localPartyName);
+    strlib_free(log->localPartyNumber);
+    strlib_free(log->remotePartyName[0]);
+    strlib_free(log->remotePartyName[1]);
+    strlib_free(log->remotePartyNumber[0]);
+    strlib_free(log->remotePartyNumber[1]);
+    strlib_free(log->altPartyNumber[0]);
+    strlib_free(log->altPartyNumber[1]);
+
+    calllogger_init_call_log(log);
+}
+
+void calllogger_print_call_log(cc_call_log_t *log)
+{
+    static const char *fname = "calllogger_print_call_log";
+
+    CCLOG_DEBUG(DEB_F_PREFIX"Entering...\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+    CCLOG_DEBUG("Remote ID %s:%s %s:%s\n LocalID %s:%s \n alt %s:%s\n",
+           log->remotePartyName[0], log->remotePartyNumber[0],
+           log->remotePartyName[1], log->remotePartyNumber[1],
+           log->localPartyName, log->localPartyNumber,
+           log->altPartyNumber[0], log->altPartyNumber[1] );
+    CCLOG_DEBUG("state %d \n Disp %d\n", log->callState, log->logDisp);
+}
+
+/**
+ * Call logger update to be called placed call num update
+ */
+void calllogger_setPlacedCallInfo (session_data_t *data)
+{
+    static const char *fname = "calllogger_setPlacedCallInfo";
+
+    CCLOG_DEBUG(DEB_F_PREFIX"updating placed number for session %x to %s:%s\n",
+                            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->sess_id,
+                     data->cld_name, data->cld_number);
+    if ( data->call_log.logDisp == CC_LOGD_RCVD ) { return;}
+    data->call_log.remotePartyName[0] = strlib_copy(data->plcd_name);
+    data->call_log.remotePartyNumber[0] = strlib_copy(data->plcd_number);
+    data->call_log.logDisp = CC_LOGD_SENT;
+    data->call_log.startTime = time(NULL);
+}
+
+/**
+ * call logger api to be called for log disp update
+ */
+void calllogger_updateLogDisp (session_data_t *data)
+{
+    static const char *fname = "calllogger_updateLogDisp";
+
+    CCLOG_DEBUG(DEB_F_PREFIX"updating log disposition for session %x to %d\n",
+                            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->sess_id, data->log_disp);
+    data->call_log.logDisp = data->log_disp;
+}
+
+cc_boolean partyInfoPassedTheNumberFilter (cc_string_t partyString)
+{
+    static const char *fname = "partyInfoPassedTheNumberFilter";
+
+    CCLOG_DEBUG(DEB_F_PREFIX"Entering...\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+        // check if partyString is something we should not be logging at this point
+        // Uris: CfwdALL 105
+        // Conference 152
+        if (partyString && strlen(partyString) > 1 &&
+            (partyString[1] == 17 ||
+             partyString[1] == 91 ||
+             partyString[1] == 05 ||
+             partyString[1] == 18 ||
+             partyString[1] == 16 ||
+             partyString[1] == 52 )) {
+
+            CCLOG_DEBUG(DEB_F_PREFIX"Filtering out the partyName=%s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), partyString);
+            return FALSE;
+        }
+        return TRUE;
+}
+
+/**
+ * partyInfoPassedTheNameFilter
+ *
+ * @param partyString String
+ * @return boolean
+ */
+cc_boolean partyInfoPassedTheNameFilter(cc_string_t partyString) {
+    static const char *fname = "partyInfoPassedTheNameFilter";
+
+    CCLOG_DEBUG(DEB_F_PREFIX"Entering...\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+    // If the name String has Conference, filter it out
+    if (partyString && strlen(partyString) > 1 &&
+        (partyString[1] == 52 || partyString[1] == 53)) {
+        CCLOG_DEBUG(DEB_F_PREFIX"Filtering out the partyName=%s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), partyString);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static cc_string_t missedCallMask=NULL;
+void calllogger_setMissedCallLoggingConfig(cc_string_t mask) {
+    static const char *fname = "calllogger_setMissedCallLoggingConfig";
+
+
+    CCLOG_DEBUG(DEB_F_PREFIX"Entering... mask=%s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), mask);
+    if ( missedCallMask == NULL) {
+        missedCallMask = strlib_empty();
+    }
+    missedCallMask = strlib_update(missedCallMask, mask);
+}
+
+cc_boolean isMissedCallLoggingEnabled (unsigned int line)
+{
+    static const char *fname = "isMissedCallLoggingEnabled";
+
+    CCLOG_DEBUG(DEB_F_PREFIX"Entering... mask=%s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), missedCallMask);
+    if (missedCallMask == NULL) {
+       return TRUE;
+    }
+
+    if ((line > 0) && strlen(missedCallMask) > (line-1)) {
+        if ( missedCallMask[line-1] == '0' ) {
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+
+
+void handlePlacedCall (session_data_t *data)
+{
+    static const char *fname = "handlePlacedCall";
+
+    CCLOG_DEBUG(DEB_F_PREFIX"Entering...\n",DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    //populate calling party if not already populated
+    if ( data->call_log.localPartyNumber == strlib_empty() ) {
+       data->call_log.localPartyNumber = strlib_update(data->call_log.localPartyNumber, data->clg_number);
+       data->call_log.localPartyName = strlib_update(data->call_log.localPartyName, data->clg_name);
+    }
+
+    // first update or update with the same number we update name and number
+    if ( data->call_log.remotePartyNumber[0] == strlib_empty() ||
+            (data->cld_number[0] != 0 && strncmp(data->call_log.remotePartyNumber[0],
+                        data->cld_number, strlen(data->cld_number)) == 0 )) {
+       if ( partyInfoPassedTheNameFilter(data->cld_name) &&
+            partyInfoPassedTheNumberFilter(data->cld_number) )
+           data->call_log.remotePartyNumber[0] = strlib_update(data->call_log.remotePartyNumber[0], data->cld_number);
+           data->call_log.remotePartyName[0] = strlib_update(data->call_log.remotePartyName[0], data->cld_name);
+    }
+
+    // Start the duration count once the call reaches connected state.
+    if ( data->state == CONNECTED &&
+         data->call_log.startTime == 0 ) {
+       data->call_log.startTime = time(NULL);
+    }
+
+    if (data->state == ONHOOK ) {
+        // only set the duration if the call entered the connected state
+       if ( data->call_log.startTime != 0 ) {
+            data->call_log.duration = (cc_uint32_t) (time(NULL) - data->call_log.startTime);
+       } else {
+            data->call_log.startTime = time(NULL);
+        }
+    }
+
+    data->call_log.callState = data->state;
+}
+
+
+void handleMissedOrReceviedCall ( session_data_t *data )
+{
+    static const char *fname = "handleMissedOrReceviedCall";
+    int line = GET_LINE_ID(data->sess_id);
+    cc_string_t localName = strlib_empty(), localNumber = strlib_empty();
+    cc_string_t remoteName = strlib_empty(), remoteNumber = strlib_empty();
+
+    CCLOG_DEBUG(DEB_F_PREFIX"Entering...\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    if (data->type == CC_CALL_TYPE_INCOMING || data->type == CC_CALL_TYPE_FORWARDED ) {
+        localName = data->cld_name;
+        localNumber = data->cld_number;
+        remoteName = data->clg_name;
+        remoteNumber = data->clg_number;
+    } else {
+        localName = data->clg_name;
+        localNumber = data->clg_number;
+        remoteName = data->cld_name;
+        remoteNumber = data->cld_number;
+    }
+
+    if ( data->call_log.localPartyNumber == strlib_empty() ) {
+       data->call_log.localPartyNumber = strlib_update(data->call_log.localPartyNumber, localNumber);
+       data->call_log.localPartyName = strlib_update(data->call_log.localPartyName, localName);
+    }
+
+    // first update or update with the same number we update name and number
+    if ( data->call_log.remotePartyNumber[0] == strlib_empty() ||
+            (remoteNumber[0] != 0 && strncmp(data->call_log.remotePartyNumber[0],
+                        remoteNumber, strlen(remoteNumber)) == 0 )) {
+       data->call_log.remotePartyNumber[0] = strlib_update(data->call_log.remotePartyNumber[0], remoteNumber);
+       data->call_log.altPartyNumber[0] = strlib_update(data->call_log.altPartyNumber[0], data->alt_number);
+       if (data->call_log.remotePartyName[0] == strlib_empty() ) {
+           data->call_log.remotePartyName[0] = strlib_update(data->call_log.remotePartyName[0], remoteName);
+       }
+    } else {
+       // since number doesn't match this is a new leg
+       data->call_log.remotePartyName[1] = strlib_update(data->call_log.remotePartyName[1], remoteName);
+       data->call_log.remotePartyNumber[1] = strlib_update(data->call_log.remotePartyNumber[1], remoteNumber);
+       data->call_log.altPartyNumber[1] = strlib_update(data->call_log.altPartyNumber[1], data->alt_number);
+    }
+
+    if ( data->state == ONHOOK ) {
+       if ( data->call_log.callState == RINGIN ) {
+           data->call_log.startTime = time(NULL);
+           if (isMissedCallLoggingEnabled(line)) {
+               data->call_log.logDisp = CC_LOGD_MISSED;
+           } else {
+               data->call_log.logDisp = CC_LOGD_DELETE;
+           }
+           data->call_log.startTime = time(NULL);
+           data->call_log.duration = 0; //missed call duration is 0
+       } else if ( data->call_log.startTime != 0 ){
+          //connected call going onhook update duration
+           data->call_log.duration = (cc_uint32_t) (time(NULL) - data->call_log.startTime);
+       }
+    }
+
+    if ( data->state == CONNECTED && data->call_log.startTime == 0 ) {
+       data->call_log.logDisp = CC_LOGD_RCVD;
+       data->call_log.startTime = time(NULL);
+    }
+
+    data->call_log.callState = data->state;
+}
+
+
+/**
+ * Call logger update to be called on attr or callinfo or state events
+ */
+void  calllogger_update (session_data_t *data)
+{
+    static const char *fname = "calllogger_update";
+
+    CCLOG_DEBUG(DEB_F_PREFIX"Entering...\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+    if (data->call_log.logDisp == CC_LOGD_DELETE) {
+        CCLOG_DEBUG(DEB_F_PREFIX"log disposition set to delete. Ignoring call logging for sess_id=%x\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->sess_id);
+    }
+
+    if (data->call_log.logDisp == CC_LOGD_RCVD || data->call_log.logDisp == CC_LOGD_MISSED ||
+          data->type == CC_CALL_TYPE_INCOMING || data->type == CC_CALL_TYPE_FORWARDED ) {
+        handleMissedOrReceviedCall(data);
+    } else if ( data->call_log.logDisp == CC_LOGD_SENT ||
+                    data->type == CC_CALL_TYPE_OUTGOING ) {
+        handlePlacedCall (data);
+    }
+
+}
diff --git a/libs/sipcc/core/ccapp/call_logger.h b/libs/sipcc/core/ccapp/call_logger.h
new file mode 100644 (file)
index 0000000..6ccec2e
--- /dev/null
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __CALL_LOGGER_H__
+#define __CALL_LOGGER_H__
+
+#include "cc_types.h"
+#include "CCProvider.h"
+
+void calllogger_init_call_log(cc_call_log_t *log);
+void calllogger_copy_call_log(cc_call_log_t *dest, cc_call_log_t * src);
+void calllogger_free_call_log(cc_call_log_t *log);
+void calllogger_print_call_log(cc_call_log_t *log);
+void calllogger_setPlacedCallInfo(session_data_t *data);
+void calllogger_updateLogDisp(session_data_t *data);
+void calllogger_setMissedCallLoggingConfig(cc_string_t mask);
+void  calllogger_update(session_data_t *data);
+
+#endif
diff --git a/libs/sipcc/core/ccapp/capability_set.c b/libs/sipcc/core/ccapp/capability_set.c
new file mode 100644 (file)
index 0000000..71471fb
--- /dev/null
@@ -0,0 +1,281 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "capability_set.h"
+#include "CCProvider.h"
+
+// --------------------------------------------------------------------------------
+// Data Structures Used to Store the Capability Set Information
+//    Settings on kept on a per call state, per feature basis.
+//    Includes all general endpoint capabilities, plus fcp-controlled ones
+// --------------------------------------------------------------------------------
+static cc_boolean  capability_set[MAX_CALL_STATES][CCAPI_CALL_CAP_MAX];
+static cc_boolean  capability_idleset[CCAPI_CALL_CAP_MAX];
+
+
+// --------------------------------------------------------------------------------
+// Data Structures Used to Store the Parsed FCP information from UCM/FCP file
+//    (after parse will be used to selectively set capabilities in the above also)
+// --------------------------------------------------------------------------------
+
+// maximum number of features
+#define FCP_FEATURE_MAX      9
+
+char                                    g_fp_version_stamp[MAX_FP_VERSION_STAMP_LEN];
+static cc_feature_control_policy_info_t cc_feat_control_policy[FCP_FEATURE_MAX];
+static ccapi_call_capability_e          cc_fcp_id_to_capability_map[FCP_FEATURE_MAX+1];    // 0th entry of map is not used/null entry
+
+
+static const unsigned int               CALL_FORWARD_ALL_FCP_INDEX  = 1;
+static const unsigned int               CONFERENCE_LIST_FCP_INDEX   = 4;
+static const unsigned int               SPEED_DIAL_FCP_INDEX        = 5;
+static const unsigned int               CALL_BACK_FCP_INDEX         = 6;
+static const unsigned int               REDIAL_FCP_INDEX            = 7;
+
+static int                              fcp_index = -1;
+
+// --------------------------------------------------------------------------------
+
+
+/*
+ * capset_set_fcp_forwardall - sets the fcp-controlled call forward all feature
+ *
+ */
+static void capset_set_fcp_forwardall (cc_boolean state)
+{
+   CONFIG_DEBUG(DEB_F_PREFIX"FCP Setting CALLFWD Capability to [%d]\n", DEB_F_PREFIX_ARGS(JNI, "capset_set_fcp_forwardall"), (unsigned int)state);
+
+   capability_idleset[CCAPI_CALL_CAP_CALLFWD]       = state;
+   capability_set[OFFHOOK][CCAPI_CALL_CAP_CALLFWD]  = state;
+}
+
+/*
+ * capset_set_fcp_redial - sets the fcp-controlled redial feature
+ *
+ */
+static void capset_set_fcp_redial (cc_boolean state)
+{
+   CONFIG_DEBUG(DEB_F_PREFIX"FCP Setting REDIAL capability to [%d]\n", DEB_F_PREFIX_ARGS(JNI, "capset_set_fcp_redial"), (unsigned int)state);
+
+   capability_idleset[CCAPI_CALL_CAP_REDIAL]        = state;
+   capability_set[OFFHOOK][CCAPI_CALL_CAP_REDIAL]   = state;
+   capability_set[ONHOOK][CCAPI_CALL_CAP_REDIAL]    = state;
+}
+
+// --------------------------------------------------------------------------------
+
+
+/*
+ * fcp_set_index - sets a given feture index number (from fcp xml file), maps it to the internal
+ * call capability, and sets the appropriate state-based capability for the feature index
+ *
+ */
+static void fcp_set_index (unsigned int fcpCapabilityId, cc_boolean state)
+{
+   ccapi_call_capability_e capabilityId = CCAPI_CALL_CAP_MAX;
+
+   // range check the capability index
+   if ((fcpCapabilityId <= 0) || (fcpCapabilityId > FCP_FEATURE_MAX))
+   {
+        CONFIG_ERROR(CFG_F_PREFIX "Unable to set capability of unknown feature [%d] in FCP \n", "fcp_set_index", fcpCapabilityId);
+        return;
+   }
+
+   // convert the fcp index to an fcp capability id
+   capabilityId = cc_fcp_id_to_capability_map[fcpCapabilityId];
+
+
+   // based on the capability id, invoke the appropate method specific to that capability
+   switch (capabilityId)
+   {
+       case CCAPI_CALL_CAP_CALLFWD  :  capset_set_fcp_forwardall (state);      break;
+       case CCAPI_CALL_CAP_REDIAL   :  capset_set_fcp_redial (state);          break;
+       default :
+       {
+           CONFIG_ERROR(CFG_F_PREFIX "Unable to update settings for capability [%d]\n", "fcp_set_index", (int)capabilityId);
+           break;
+       }
+    }
+}
+
+/*
+ * capset_init - initialize the internal capability data structures to defaults
+ *
+ */
+static void capset_init ()
+{
+   // initialize the 4 tables related to capability set to false
+   memset(capability_idleset, 0, sizeof(capability_idleset));
+   memset(capability_set, 0, sizeof(capability_set));
+
+   // ----------------------------------------------------------------------
+   // FCP based capabilities
+   // ----------------------------------------------------------------------
+
+   CONFIG_DEBUG(DEB_F_PREFIX"FCP Initializing Capabilities to default\n", DEB_F_PREFIX_ARGS(JNI, "capset_init"));
+
+   // ----------------------------------------------------------------------
+   // Non-FCP-based Capabilities
+   // ----------------------------------------------------------------------
+
+   // Now, set all the non-FCP based capabilities to appropriate default settings
+   // (some of which may be enabled by default)
+
+   capability_idleset[CCAPI_CALL_CAP_NEWCALL]                    = TRUE;
+
+   // call-state based settings
+   // offhook
+   capability_set[OFFHOOK][CCAPI_CALL_CAP_ENDCALL]               = TRUE;
+
+   // onhook
+   capability_set[ONHOOK][CCAPI_CALL_CAP_NEWCALL] = TRUE;
+
+   // ringout
+   capability_set[RINGOUT][CCAPI_CALL_CAP_ENDCALL] = TRUE;
+
+   // ringing
+   capability_set[RINGIN][CCAPI_CALL_CAP_ANSWER] = TRUE;
+
+   // proceed
+   capability_set[PROCEED][CCAPI_CALL_CAP_ENDCALL] = TRUE;
+
+   // connected
+   capability_set[CONNECTED][CCAPI_CALL_CAP_ENDCALL] = TRUE;
+   capability_set[CONNECTED][CCAPI_CALL_CAP_HOLD] = TRUE;
+   capability_set[CONNECTED][CCAPI_CALL_CAP_TRANSFER] = TRUE;
+   capability_set[CONNECTED][CCAPI_CALL_CAP_CONFERENCE] = TRUE;
+   capability_set[CONNECTED][CCAPI_CALL_CAP_SELECT] = TRUE;
+
+   // hold
+   capability_set[HOLD][CCAPI_CALL_CAP_RESUME] = TRUE;
+   capability_set[REMHOLD][CCAPI_CALL_CAP_RESUME] = TRUE;
+
+   // busy
+   capability_set[BUSY][CCAPI_CALL_CAP_ENDCALL] = TRUE;
+
+   // reorder
+   capability_set[REORDER][CCAPI_CALL_CAP_ENDCALL] = TRUE;
+
+   // dialing
+   capability_set[DIALING][CCAPI_CALL_CAP_ENDCALL] = TRUE;
+   capability_set[DIALING][CCAPI_CALL_CAP_DIAL] = TRUE;
+   capability_set[DIALING][CCAPI_CALL_CAP_SENDDIGIT] = TRUE;
+   capability_set[DIALING][CCAPI_CALL_CAP_BACKSPACE] = TRUE;
+
+   // holdrevert
+   capability_set[HOLDREVERT][CCAPI_CALL_CAP_ANSWER] = TRUE;
+
+   // preservation
+   capability_set[PRESERVATION][CCAPI_CALL_CAP_ENDCALL] = TRUE;
+
+   // waiting for digits
+   capability_set[WAITINGFORDIGITS][CCAPI_CALL_CAP_SENDDIGIT] = TRUE;
+   capability_set[WAITINGFORDIGITS][CCAPI_CALL_CAP_BACKSPACE] = TRUE;
+}
+
+
+/*
+ * capset_get_idleset - get the set of features associated with idle state
+ *
+ */
+void capset_get_idleset ( cc_cucm_mode_t mode, cc_boolean features[])
+{
+  static const char fname[] = "capset_get_idleset";
+  int i;
+
+  CCAPP_DEBUG(DEB_F_PREFIX"updating idleset",
+            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+     for (i=0;i < CCAPI_CALL_CAP_MAX; i++) {
+  CCAPP_DEBUG(DEB_F_PREFIX"updating line features %d=%d",
+            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), i, capability_idleset[i]);
+       features[i] = capability_idleset[i];
+     }
+
+}
+
+/*
+ * capset_get_allowed_features - get the set of features
+ *
+ */
+void capset_get_allowed_features ( cc_cucm_mode_t mode, cc_call_state_t state, cc_boolean features[])
+{
+  static const char fname[] = "capset_get_allowed_features";
+  int i;
+
+  CCAPP_DEBUG(DEB_F_PREFIX"updating idleset",
+            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  for (i=0;i < CCAPI_CALL_CAP_MAX; i++) {
+      features[i] = capability_set[state][i];
+  }
+
+}
+
+// --------------------------------------------------------------------------------------------------------
+// ---------------------------- XML Parse Logic for Feature Control Policy --------------------------------
+// --------------------------------------------------------------------------------------------------------
+
+/*
+ * fcp_set_capabilities - updates the capabilities structure, based on the now parsed information
+ * from fcp xml file
+ *
+ */
+
+static void fcp_set_capabilities()
+{
+   int my_fcp_index = 0;
+
+    if ( (fcp_index+1) >= FCP_FEATURE_MAX) {
+        fcp_index = (FCP_FEATURE_MAX -1);
+        CONFIG_ERROR(CFG_F_PREFIX "Received more than the maximum supported features [%d] in FCP \n", "fcp_set_capabilities", FCP_FEATURE_MAX);
+
+    }
+   // loop over all the FCP features parsed, and for each one, based on ID, and enabled settings,
+   // update the corresponding call capability flags
+   for (my_fcp_index = 0; my_fcp_index <= fcp_index; my_fcp_index++)
+   {   // set the capability if fcp file has it marked as 'enabled'
+       fcp_set_index(cc_feat_control_policy[my_fcp_index].featureId, (cc_feat_control_policy[my_fcp_index].featureEnabled == TRUE));
+   }
+}
+
+/*
+ * fcp_init - initialize the data structure used to store the fcp parse info
+ *
+ */
+static void fcp_init()
+{
+   // master index; set to null
+   fcp_index = -1;
+
+   // initialize the map of fcp xml feature indexes to internal call capabilities
+   cc_fcp_id_to_capability_map[CALL_FORWARD_ALL_FCP_INDEX]   = CCAPI_CALL_CAP_CALLFWD;
+   cc_fcp_id_to_capability_map[REDIAL_FCP_INDEX]             = CCAPI_CALL_CAP_REDIAL;
+
+   // initialize the capability set data structures
+   capset_init();
+
+   // initialize the version
+   g_fp_version_stamp[0] = '\0';
+}
+
+/*
+ * capset_set_fcp_forwardall - sets the fcp-controlled call forward all feature
+ *
+ */
+int fcp_init_template (const char* fcp_plan_string)
+{
+    fcp_init();
+
+    if (fcp_plan_string == NULL)
+    {   // set up the default fcp
+       return (0);
+    }
+
+    // update the fcp capabilities structure, based on the parsed feature information
+    fcp_set_capabilities();
+
+    return (0);
+}
+// ---------------------------- End Of XML Parse Logic for Feature Control Policy --------------------------
diff --git a/libs/sipcc/core/ccapp/capability_set.h b/libs/sipcc/core/ccapp/capability_set.h
new file mode 100644 (file)
index 0000000..bad517a
--- /dev/null
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_CAPABILITY_SET_H_
+#define _CC_CAPABILITY_SET_H_
+
+#include "ccapi_types.h"
+
+extern void capset_get_idleset( cc_cucm_mode_t mode, cc_boolean features[]);
+extern void capset_get_allowed_features( cc_cucm_mode_t mode, cc_call_state_t state, cc_boolean features[]);
+
+// FCP (feature control policy) methods
+int  fcp_init_template (const char* fcp_plan_string);
+
+// FCP Definitions
+#define FCP_MAX_SIZE                0x5000
+#define FCP_FEATURE_NAME_MAX        24
+#define MAX_FP_VERSION_STAMP_LEN   (64+1)
+
+extern char g_fp_version_stamp[MAX_FP_VERSION_STAMP_LEN];
+
+
+// for each feature in the XML FCP file, we'll receive the
+// feature name, featureId, and whether or not it is enabled
+typedef struct cc_feature_control_policy_info_t_
+{
+    char                         featureName[FCP_FEATURE_NAME_MAX];
+    unsigned int                 featureId;
+    cc_boolean                   featureEnabled;
+} cc_feature_control_policy_info_t;
+
+#endif /* _CC_CAPABILITY_SET_H_ */
diff --git a/libs/sipcc/core/ccapp/cc_blf.c b/libs/sipcc/core/ccapp/cc_blf.c
new file mode 100644 (file)
index 0000000..49c5002
--- /dev/null
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cc_blf.h"
+#include "pres_sub_not_handler.h"
+
+/**
+ * Initialize the BLF stack
+ * @return
+ */
+int CC_BLF_init() {
+       pres_sub_handler_initialized();
+       return CC_SUCCESS;
+}
+/**
+ * Start BLF subscription
+ * @param request_id the request id
+ * @param duration the subscription duration
+ * @param watcher the name of subscription watcher
+ * @param presentity
+ * @param app_id the application id for the BLF
+ * @param feature_mask
+ * @return void
+ */
+void CC_BLF_subscribe(int request_id,
+               int duration,
+               const char *watcher,
+        const char *presentity,
+        int app_id,
+        cc_blf_feature_mask_t feature_mask) {
+       pres_get_state(request_id, duration, watcher, presentity, app_id, feature_mask);
+}
+/**
+ * Unsubscribe the BLF subscription
+ * @param request_id the request id
+ * @return void
+ */
+void CC_BLF_unsubscribe(int request_id) {
+       pres_terminate_req(request_id);
+}
+
+/**
+ * Unsubscribe all BLF subscription
+ * @return void
+ */
+void CC_BLF_unsubscribe_All() {
+       pres_terminate_req_all();
+}
+
diff --git a/libs/sipcc/core/ccapp/cc_call_feature.c b/libs/sipcc/core/ccapp/cc_call_feature.c
new file mode 100644 (file)
index 0000000..529affa
--- /dev/null
@@ -0,0 +1,681 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cc_call_feature.h"
+#include "CCProvider.h"
+#include "sessionConstants.h"
+#include "sessionTypes.h"
+#include "lsm.h"
+#include "phone_debug.h"
+#include "text_strings.h"
+#include "ccapi.h"
+#include "ccapp_task.h"
+#include "sessionHash.h"
+#include "cpr_rand.h"
+
+extern cpr_status_e ccappTaskPostMsg(unsigned int msgId, void * data, uint16_t len, int appId);
+
+/**
+ * Internal method: create call handle
+ * @param line
+ * @paran call_id
+ */
+cc_call_handle_t cc_createCallHandle(cc_lineid_t line, cc_callid_t call_id)
+{
+        return (CREATE_CALL_HANDLE(line, call_id));
+}
+
+/**
+ * Internal method: assign a valid call id.
+ * @param line_id line number
+ * @param call_id call id
+ * @return void
+ */
+void cc_getLineIdAndCallId (cc_lineid_t *line_id, cc_callid_t *call_id)
+{
+    // assign proper line_id and call_id if not already there
+    if ((*line_id) == 0 || (*line_id) == CC_ALL_LINES) {
+        /*
+         * If the filter is the All Calls Complex Filter and the primary line
+         * is at its configured call capacity, the next available line should
+         * be used. In this scenario, sessionUI/Mgr send the line_id as zero.
+         */
+        (*line_id) = lsm_get_available_line(FALSE);
+    }
+
+    if ((*call_id) == 0) {
+        (*call_id) = cc_get_new_call_id();
+    }
+}
+
+/**
+ * Invoke a call feature.
+ */
+cc_return_t cc_invokeFeature(cc_call_handle_t call_handle, group_cc_feature_t featureId, cc_sdp_direction_t video_pref, string_t data) {
+       session_feature_t callFeature;
+    callFeature.session_id = (SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT) + call_handle;
+    callFeature.featureID = featureId;
+    callFeature.featData.ccData.state = video_pref;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"cc_invokeFeature:sid=%d, line=%d, cid=%d, fid=%d, video_pref=%s data=%s\n",
+                        DEB_F_PREFIX_ARGS("cc_call_feature", "cc_invokeFeature"),
+                        callFeature.session_id,
+                        GET_LINE_ID(call_handle),
+                        GET_CALL_ID(call_handle),
+                        featureId, SDP_DIRECTION_PRINT(video_pref),
+                        ((featureId == CC_FEATURE_KEYPRESS) ? "...": data));
+
+    switch (featureId) {
+    case CC_FEATURE_KEYPRESS:
+    case CC_FEATURE_DIALSTR:
+    case CC_FEATURE_SPEEDDIAL:
+    case CC_FEATURE_BLIND_XFER_WITH_DIALSTRING:
+    case CC_FEATURE_END_CALL:
+    case CC_FEATURE_B2BCONF:
+    case CC_FEATURE_CONF:
+    case CC_FEATURE_XFER:
+    case CC_FEATURE_HOLD:
+        callFeature.featData.ccData.info = strlib_malloc(data, strlen(data));
+        callFeature.featData.ccData.info1 = NULL;
+        break;
+
+    default:
+        callFeature.featData.ccData.info = NULL;
+        callFeature.featData.ccData.info1 = NULL;
+        break;
+    }
+
+    if (ccappTaskPostMsg(CCAPP_INVOKE_FEATURE, &callFeature, sizeof(session_feature_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+            CCAPP_DEBUG(DEB_F_PREFIX"ccappTaskSendMsg failed\n",
+                       DEB_F_PREFIX_ARGS("cc_call_feature", "cc_invokeFeature"));
+            return CC_FAILURE;
+       }
+       return CC_SUCCESS;
+}
+
+/**
+ * Invoke a call feature.
+ */
+cc_return_t cc_invokeFeatureSDPMode(cc_call_handle_t call_handle, group_cc_feature_t featureId, cc_jsep_action_t action,
+                                    cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type,
+                                    uint16_t level, const cc_media_constraints_t *constraints, string_t data, string_t data1) {
+    session_feature_t callFeature;
+    session_data_t * sessionData;
+    unsigned int session_id = 0;
+    callFeature.session_id = (SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT) + call_handle;
+    callFeature.featureID = featureId;
+    callFeature.featData.ccData.action = action;
+    callFeature.featData.ccData.media_type = media_type;
+    callFeature.featData.ccData.stream_id = stream_id;
+    callFeature.featData.ccData.track_id = track_id;
+    callFeature.featData.ccData.level = level;
+    callFeature.featData.ccData.has_constraints = FALSE;
+
+    /* If constraints exist add to session hash */
+    if (constraints) {
+        if (constraints->constraint_count > 0 &&
+            (CC_FEATURE_CREATEOFFER == featureId || CC_FEATURE_CREATEANSWER == featureId )) {
+            /* A random number of 5 digits will not conflict with any
+             * other usage of the hash table */
+            session_id = abs(cpr_rand()) % 60000;
+            sessionData = (session_data_t *)findhash(session_id);
+            sessionData = cpr_malloc(sizeof(session_data_t));
+            memset(sessionData, 0, sizeof(session_data_t));
+            sessionData->cc_constraints = constraints;
+            (void) addhash(session_id, sessionData);
+            callFeature.featData.ccData.sessionid = session_id;
+            callFeature.featData.ccData.has_constraints = TRUE;
+        }
+    }
+
+    CCAPP_DEBUG(DEB_F_PREFIX"cc_invokeFeatureSDPMode:sid=%d, line=%d, cid=%d, fid=%d, video_pref=%s data=%s\n",
+                        DEB_F_PREFIX_ARGS("cc_call_feature", "cc_invokeFeatureSDPMode"),
+                        callFeature.session_id,
+                        GET_LINE_ID(call_handle),
+                        GET_CALL_ID(call_handle),
+                        featureId,
+                        ((featureId == CC_FEATURE_KEYPRESS) ? "...": data));
+
+    switch (featureId) {
+    case CC_FEATURE_KEYPRESS:
+    case CC_FEATURE_DIALSTR:
+    case CC_FEATURE_SPEEDDIAL:
+    case CC_FEATURE_BLIND_XFER_WITH_DIALSTRING:
+    case CC_FEATURE_END_CALL:
+    case CC_FEATURE_B2BCONF:
+    case CC_FEATURE_CONF:
+    case CC_FEATURE_XFER:
+    case CC_FEATURE_HOLD:
+    case CC_FEATURE_SETLOCALDESC:
+    case CC_FEATURE_SETREMOTEDESC:
+    case CC_FEATURE_SETPEERCONNECTION:
+       callFeature.featData.ccData.info = strlib_malloc(data, strlen(data));
+        callFeature.featData.ccData.info1 = NULL;
+       break;
+    case CC_FEATURE_ADDICECANDIDATE:
+       callFeature.featData.ccData.info = strlib_malloc(data, strlen(data));
+        callFeature.featData.ccData.info1 = strlib_malloc(data1, strlen(data1));
+       break;
+
+    default:
+        callFeature.featData.ccData.info = NULL;
+        callFeature.featData.ccData.info1 = NULL;
+       break;
+    }
+
+    if (ccappTaskPostMsg(CCAPP_INVOKE_FEATURE, &callFeature, sizeof(session_feature_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+            CCAPP_DEBUG(DEB_F_PREFIX"ccappTaskSendMsg failed\n",
+                       DEB_F_PREFIX_ARGS("cc_call_feature", "cc_invokeFeatureSDPMode"));
+            return CC_FAILURE;
+       }
+       return CC_SUCCESS;
+}
+
+/***********************************Basic Call Feature Control Methods************************************
+ * This section defines all the call related methods that an upper layer can use to control
+ * a call in progress.
+ */
+
+/**
+ * Used to create any outgoing call regular call. The incoming/reverting/consultation call will be
+ * created by the stack. It creates a call place holder and initialize the memory for a call. An user needs
+ * other methods to start the call, such as the method OriginateCall, etc
+ * @param line line number that is invoked and is assigned
+ * @return call handle wich includes the assigned line and call id
+ */
+cc_call_handle_t CC_createCall(cc_lineid_t line) {
+       static const char fname[] = "CC_CreateCall";
+       //Create call handle to initialize the memory.
+       cc_call_handle_t call_handle = CC_EMPTY_CALL_HANDLE;
+       cc_lineid_t lineid = line;
+       cc_callid_t callid = CC_NO_CALL_ID;
+
+
+       //Assign line and call id.
+       cc_getLineIdAndCallId(&lineid, &callid);
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, callid, lineid, fname));
+
+       if (lineid == CC_NO_LINE) {
+        lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT);
+        return call_handle;
+    }
+
+    call_handle = cc_createCallHandle(lineid, callid);
+
+       return call_handle;
+}
+
+/**
+ * Start the call that was created.
+ * @param the call handle
+ * @return SUCCESS or FAILURE
+ */
+ /*move it up...*/
+cc_return_t CC_CallFeature_originateCall(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref) {
+       static const char fname[] = "CC_CallFeature_originateCall:";
+       //CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+       //              GET_LINE_ID(call_handle), fname));
+       CCAPP_DEBUG(DEB_F_PREFIX"CC_CallFeature_originateCall:cHandle=%d\n",
+                               DEB_F_PREFIX_ARGS("cc_call_feature", fname),
+                               call_handle);
+    return cc_invokeFeature(call_handle, CC_FEATURE_OFFHOOK, video_pref, NULL);
+}
+
+/**
+ * Terminate or end a normal call.
+ * @param call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_terminateCall(cc_call_handle_t call_handle) {
+       static const char fname[] = "CC_CallFeature_TerminateCall";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+    return cc_invokeFeature(call_handle, CC_FEATURE_ONHOOK, CC_SDP_MAX_QOS_DIRECTIONS, NULL);
+}
+
+/**
+ * Answer an incoming or reverting call.
+ * @param call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_answerCall(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref) {
+       static const char fname[] = "CC_CallFeature_AnswerCall";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+    return cc_invokeFeature(call_handle, CC_FEATURE_ANSWER, video_pref, NULL);
+}
+
+/**
+ * Send a keypress to a call, it could be a single digit.
+ * @param call handle
+ * @param cc_digit digit pressed
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_sendDigit(cc_call_handle_t call_handle, cc_digit_t cc_digit) {
+       static const char fname[] = "CC_CallFeature_SendDigit";
+    char digit;
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+       //Demote to eliminate the endian issue
+       digit = cc_digit;
+    return cc_invokeFeature(call_handle, CC_FEATURE_KEYPRESS, CC_SDP_MAX_QOS_DIRECTIONS, (string_t)&digit);
+}
+
+/**
+ * Send a backspace action.
+ * @param call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_backSpace(cc_call_handle_t call_handle) {
+       static const char fname[] = "CC_CallFeature_BackSpace";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+    return cc_invokeFeature(call_handle, CC_FEATURE_BKSPACE, CC_SDP_MAX_QOS_DIRECTIONS, NULL);
+}
+
+/**
+ * Send a dial digit string on an active call, e.g."9191234567".
+ * @param call handle
+ * @param numbers dialed string
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_dial(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref, const string_t numbers) {
+       static const char fname[] = "CC_CallFeature_Dial";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+    if (cpr_strcasecmp(numbers, "DIAL") == 0) {
+           return cc_invokeFeature(call_handle, CC_FEATURE_DIAL, video_pref, numbers);
+    }
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_DIALSTR, video_pref, numbers);
+}
+
+cc_return_t CC_CallFeature_CreateOffer(cc_call_handle_t call_handle, const cc_media_constraints_t *constraints) {
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                GET_LINE_ID(call_handle), __FUNCTION__));
+
+    return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_CREATEOFFER, JSEP_NO_ACTION,
+                                   0, 0, NO_STREAM, 0, constraints, NULL, NULL);
+}
+
+cc_return_t CC_CallFeature_CreateAnswer(cc_call_handle_t call_handle, const cc_media_constraints_t *constraints) {
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                GET_LINE_ID(call_handle), __FUNCTION__));
+
+    return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_CREATEANSWER, JSEP_NO_ACTION,
+                                   0, 0, NO_STREAM, 0, constraints, NULL, NULL);
+}
+
+cc_return_t CC_CallFeature_SetLocalDescription(cc_call_handle_t call_handle, cc_jsep_action_t action, string_t sdp) {
+    const cc_media_constraints_t *constraints = NULL;
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+            GET_LINE_ID(call_handle), __FUNCTION__));
+
+    return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_SETLOCALDESC, action,
+                                   0, 0, NO_STREAM, 0, constraints, sdp, NULL);
+}
+
+cc_return_t CC_CallFeature_SetRemoteDescription(cc_call_handle_t call_handle, cc_jsep_action_t action, string_t sdp) {
+    const cc_media_constraints_t *constraints = NULL;
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+            GET_LINE_ID(call_handle), __FUNCTION__));
+
+    return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_SETREMOTEDESC, action,
+                                   0, 0, NO_STREAM, 0, constraints, sdp, NULL);
+}
+
+cc_return_t CC_CallFeature_SetPeerConnection(cc_call_handle_t call_handle, cc_peerconnection_t pc) {
+    const cc_media_constraints_t *constraints = NULL;
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                GET_LINE_ID(call_handle), __FUNCTION__));
+
+    return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_SETPEERCONNECTION, JSEP_NO_ACTION,
+                                   0, 0, NO_STREAM, 0, constraints, pc, NULL);
+}
+
+cc_return_t CC_CallFeature_AddStream(cc_call_handle_t call_handle, cc_media_stream_id_t stream_id,
+                                            cc_media_track_id_t track_id, cc_media_type_t media_type) {
+    const cc_media_constraints_t *constraints = NULL;
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+            GET_LINE_ID(call_handle), __FUNCTION__));
+
+    return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_ADDSTREAM, JSEP_NO_ACTION,
+                                   stream_id, track_id, media_type, 0, constraints, NULL, NULL);
+}
+
+cc_return_t CC_CallFeature_RemoveStream(cc_call_handle_t call_handle, cc_media_stream_id_t stream_id,
+                                               cc_media_track_id_t track_id, cc_media_type_t media_type) {
+
+    const cc_media_constraints_t *constraints = NULL;
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                GET_LINE_ID(call_handle), __FUNCTION__));
+
+    return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_REMOVESTREAM, JSEP_NO_ACTION,
+                                   stream_id, track_id, media_type, 0, constraints, NULL, NULL);
+}
+
+cc_return_t CC_CallFeature_AddICECandidate(cc_call_handle_t call_handle, const char* candidate, const char *mid, cc_level_t level) {
+    const cc_media_constraints_t *constraints = NULL;
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+            GET_LINE_ID(call_handle), __FUNCTION__));
+
+    return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_ADDICECANDIDATE, JSEP_NO_ACTION,
+                                   0, 0, NO_STREAM, (uint16_t)level, constraints, candidate, mid);
+}
+
+/**
+ * Initiate a speed dial.
+ * @param call handle
+ * @param callid call id
+ * @param speed dial numbers.
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_speedDial(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref, const string_t speed_dial_number) {
+       static const char fname[] = "CC_CallFeature_SpeedDial";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_SPEEDDIAL, video_pref, speed_dial_number);
+}
+
+/**
+ * Initiate a BLF call pickup.
+ * @param call handle
+ * @param speed dial number configured.
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_blfCallPickup(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref, const string_t speed_dial_number) {
+       static const char fname[] = "CC_CallFeature_BLFCallPickup";
+       cc_return_t ret = CC_SUCCESS;
+    string_t blf_sd = strlib_malloc(CISCO_BLFPICKUP_STRING, sizeof(CISCO_BLFPICKUP_STRING));
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+    blf_sd = strlib_append(blf_sd, "-");
+    blf_sd = strlib_append(blf_sd, speed_dial_number);
+
+       ret = cc_invokeFeature(call_handle, CC_FEATURE_SPEEDDIAL, video_pref, blf_sd);
+       //free memory
+       strlib_free(blf_sd);
+       return ret;
+}
+
+/**
+ * Redial the last dial numbers.
+ * @param call handle
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ * @Notice: if there is no active dial made, this method should not be called.
+ */
+cc_return_t CC_CallFeature_redial(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref) {
+       static const char fname[] = "CC_CallFeature_Redial";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_REDIAL, video_pref, NULL);
+}
+
+/**
+ * Update a media capability for a call.
+ * @param call_handle
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_updateCallMediaCapability(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref) {
+       static const char fname[] = "CC_CallFeature_updateCallMediaCapability";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_UPD_SESSION_MEDIA_CAP, video_pref, NULL);
+}
+
+/**
+ * Make a call forward all on particular line
+ * @param call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_callForwardAll(cc_call_handle_t call_handle) {
+       static const char fname[] = "CC_CallFeature_CallForwardAll";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_CFWD_ALL, CC_SDP_MAX_QOS_DIRECTIONS, NULL);
+}
+
+/**
+ * Resume a held call.
+ * @param call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_resume(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref) {
+       static const char fname[] = "CC_CallFeature_Resume";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_RESUME, video_pref, NULL);
+}
+
+/**
+ * End a consultation call.
+ * @param call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_endConsultativeCall(cc_call_handle_t call_handle) {
+       static const char fname[] = "CC_CallFeature_EndConsultativeCall";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_END_CALL, CC_SDP_MAX_QOS_DIRECTIONS, "ACTIVECALLS");
+}
+
+/**
+ * Initiate a conference. Steps to make a conference or transfer:
+ * 1. Create a call handle, e.g. chandle1.
+ * 2. Start the call on this call handle.
+ * 3. When the call is answered, invoke:
+ *        CC_CallFeature_Conference(chandle1, FALSE, CC_EMPTY_CALL_HANDLE) to start a conference operation.
+ * 4. Upon receiving the consultative call (cHandle2) created from pSipcc system,
+ *    invoke:
+ *        CC_CallFeature_Dial(cHandle2)
+ *       to dial the consultative call.
+ * 5. When the consultative call is in ringout or connected state, invoke:
+ *        CC_CallFeature_Conference(cHandle2, FALSE, CC_EMPTY_CALL_HANDLE) to
+ *    finish the conference.
+ * Note: 1. in the step 4, a user could kill the consultative call and pickup a hold call (not the parent call that
+ *    initiated the conference). In this scenario, a parent call handle should be supplied.
+ *    For instance,
+ *        CC_CallFeature_Conference(cHandle2, FALSE, cHandle1)
+ *       2. If it's a B2bConf, substitute the "FALSE" with "TRUE"
+ *
+ * @param call_handle the call handle for
+ *     1. the original connected call.
+ *     2. the consultative call or a held call besides the parent call initiated the transfer. This is used
+ *        on the second time to finish the transfer.
+ * @param is locall conference or not. If it's a local conference, it's a b2bconf.
+ * @param parent_call_handle if supplied, it will be the targeted parent call handle, which initiated the conference.
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_conference(cc_call_handle_t call_handle,
+               boolean is_local,
+               cc_call_handle_t parent_call_handle, cc_sdp_direction_t video_pref) {
+       static const char fname[] = "CC_CallFeature_Conference";
+       char call_handle_str[10];
+       cc_return_t ret = CC_SUCCESS;
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+       if (parent_call_handle == CC_EMPTY_CALL_HANDLE) {
+               if (is_local == FALSE) {
+                   return cc_invokeFeature(call_handle, CC_FEATURE_B2BCONF, video_pref, "");
+               } else {
+                   return cc_invokeFeature(call_handle, CC_FEATURE_CONF, video_pref, "");
+               }
+       } else {
+               cc_call_handle_t parent = (SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT) + parent_call_handle;
+        string_t parent_call_handle_str;
+               snprintf(call_handle_str, sizeof(call_handle_str), "%d", parent);
+               parent_call_handle_str = strlib_malloc(call_handle_str, strlen(call_handle_str));
+
+               if (is_local == FALSE) {
+                   ret = cc_invokeFeature(call_handle, CC_FEATURE_B2BCONF, video_pref, parent_call_handle_str);
+               } else {
+                   ret = cc_invokeFeature(call_handle, CC_FEATURE_CONF, video_pref, parent_call_handle_str);
+               }
+               strlib_free(parent_call_handle_str);
+               return ret;
+       }
+}
+
+/**
+ * Initiate a call transfer. Please refer to Conference feature.
+ * @param call_handle the call handle for
+ *     1. the original connected call.
+ *     2. the consultative call or a held call besides the parent call initiated the transfer. This is used
+ *        on the second time to finish the transfer.
+ * @param parent_call_handle if supplied, it will be the parent call handle, which initiated the transfer.
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_transfer(cc_call_handle_t call_handle, cc_call_handle_t parent_call_handle, cc_sdp_direction_t video_pref) {
+       static const char fname[] = "CC_CallFeature_transfer";
+       char call_handle_str[10];
+       cc_return_t ret = CC_SUCCESS;
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+       if (parent_call_handle == CC_EMPTY_CALL_HANDLE) {
+               return cc_invokeFeature(call_handle, CC_FEATURE_XFER, video_pref, "");
+       } else {
+               cc_call_handle_t parent = (SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT) + parent_call_handle;
+        string_t parent_call_handle_str;
+               snprintf(call_handle_str, sizeof(call_handle_str), "%d", parent);
+               parent_call_handle_str = strlib_malloc(call_handle_str, strlen(call_handle_str));
+
+               ret = cc_invokeFeature(call_handle, CC_FEATURE_XFER, video_pref, parent_call_handle_str);
+               strlib_free(parent_call_handle_str);
+               return ret;
+       }
+}
+
+/**
+ * Put a connected call on hold.
+ * @param call handle
+ * @param reason the reason to hold. The following values should be used.
+ *       CC_HOLD_REASON_NONE,
+ *    CC_HOLD_REASON_XFER, //Hold for transfer
+ *    CC_HOLD_REASON_CONF, //Hold for conference
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_holdCall(cc_call_handle_t call_handle, cc_hold_reason_t reason) {
+       static const char fname[] = "CC_CallFeature_HoldCall";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+       switch (reason) {
+       case CC_HOLD_REASON_XFER:
+               return cc_invokeFeature(call_handle, CC_FEATURE_HOLD, CC_SDP_MAX_QOS_DIRECTIONS, "TRANSFER");
+       case CC_HOLD_REASON_CONF:
+               return cc_invokeFeature(call_handle, CC_FEATURE_HOLD, CC_SDP_MAX_QOS_DIRECTIONS, "CONFERENCE");
+       case CC_HOLD_REASON_SWAP:
+               return cc_invokeFeature(call_handle, CC_FEATURE_HOLD, CC_SDP_MAX_QOS_DIRECTIONS, "SWAP");
+       default:
+               break;
+       }
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_HOLD, CC_SDP_MAX_QOS_DIRECTIONS, "");
+}
+
+/********************************End of basic call feature methods******************************************/
+
+/*************************************Additional call feature methods***************************************
+ *
+ */
+
+/**
+ * Join a call.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_b2bJoin(cc_call_handle_t call_handle) {
+       static const char fname[] = "CC_CallFeature_b2bJoin";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_B2B_JOIN, CC_SDP_MAX_QOS_DIRECTIONS, NULL);
+}
+
+/**
+ * Initiate a direct transfer
+ * @param call_handle the call handle for the call to initialize a transfer
+ * @param target_call_handle the call handle for the target transfer call.
+ * @retrun SUCCESS or FAILURE. If the target call handle is empty, a FAILURE will be returned.
+ */
+cc_return_t CC_CallFeature_directTransfer(cc_call_handle_t call_handle,
+        cc_call_handle_t target_call_handle) {
+    static const char fname[] = "CC_CallFeature_directTransfer";
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                GET_LINE_ID(call_handle), fname));
+    if (target_call_handle == CC_EMPTY_CALL_HANDLE) {
+        CCAPP_DEBUG(DEB_L_C_F_PREFIX"target call handle is empty.\n", DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                    GET_LINE_ID(call_handle), fname));
+        return CC_FAILURE;
+    }
+    return CC_CallFeature_transfer(call_handle, target_call_handle, CC_SDP_MAX_QOS_DIRECTIONS);
+}
+
+/**
+ * Initiate a join across line
+ * @param call_handle the call handle for the call that initializes a join across line (conference).
+ * @param target_call_handle the call handle for the call will be joined.
+ */
+cc_return_t CC_CallFeature_joinAcrossLine(cc_call_handle_t call_handle, cc_call_handle_t target_call_handle) {
+    static const char fname[] = "CC_CallFeature_joinAcrossLine";
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                GET_LINE_ID(call_handle), fname));
+    if (target_call_handle == CC_EMPTY_CALL_HANDLE) {
+        CCAPP_DEBUG(DEB_L_C_F_PREFIX"target call handle is empty.\n", DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                    GET_LINE_ID(call_handle), fname));
+        return CC_FAILURE;
+    }
+    return CC_CallFeature_conference(call_handle, TRUE, target_call_handle, CC_SDP_MAX_QOS_DIRECTIONS);
+}
+
+/**
+ * Select or locked a call.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_select(cc_call_handle_t call_handle) {
+       static const char fname[] = "CC_CallFeature_select";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_SELECT, CC_SDP_MAX_QOS_DIRECTIONS, NULL);
+}
+
+/**
+ * Cancel a call feature, e.g. when the consultative call is connected and the
+ * user wishes not to make the conference, thie method can be invoked.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_cancelXfrerCnf(cc_call_handle_t call_handle) {
+       static const char fname[] = "CC_CallFeature_cancelXfrerCnf";
+       CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
+                       GET_LINE_ID(call_handle), fname));
+
+       return cc_invokeFeature(call_handle, CC_FEATURE_CANCEL, CC_SDP_MAX_QOS_DIRECTIONS, NULL);
+}
+
+void CC_CallFeature_mute(boolean mute) {
+}
+
+void CC_CallFeature_speaker(boolean mute) {
+}
+
+cc_call_handle_t CC_CallFeature_getConnectedCall() {
+    return ccappGetConnectedCall();
+}
diff --git a/libs/sipcc/core/ccapp/cc_config.c b/libs/sipcc/core/ccapp/cc_config.c
new file mode 100644 (file)
index 0000000..d990051
--- /dev/null
@@ -0,0 +1,154 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cc_config.h"
+#include "CCProvider.h"
+#include "phone_debug.h"
+#include "cpr_types.h"
+#include "dialplan.h"
+#include "capability_set.h"
+#include "configmgr.h"
+#include "dialplanint.h"
+#include "ccapp_task.h"
+#include "cc_device_manager.h"
+#include "config_api.h"
+
+extern int config_parser_main( char *config, int complete_config);
+/**
+ * Set the maximum line available for registration
+ * @param lines the maximum line could be configured.
+ * @return
+ */
+int CC_Config_SetAvailableLines(cc_lineid_t lines) {
+    ccappTaskPostMsg(CCAPP_UPDATELINES, &lines, sizeof(unsigned short), CCAPP_CCPROVIER);
+    return CC_SUCCESS;
+}
+
+/**
+ * The following section defines the configuration methods.
+ */
+/**
+ * Defines the CFGID parameter setting methods.
+ * see cfgid.h for all possible configuration parameters
+ * @todo
+ */
+void CC_Config_setIntValue(int cfgid, int value) {
+    config_set_value(cfgid, &value, sizeof(int));
+    return;
+}
+
+void CC_Config_setBooleanValue(int cfgid, cc_boolean value) {
+    int temp = (int) value;
+
+    //  For Now, convert all numeric parameters to Integer.  This simplifies
+    //  the required SIP and GSM code changes.  We will go to the right size
+    //  when we implement the "full" JNI.
+
+    //    config_set_value(cfg_id, &bool_value, sizeof(boolean));
+    config_set_value(cfgid, &temp, sizeof(int));
+    return;
+}
+
+void CC_Config_setStringValue(int cfgid, const char* value) {
+    config_set_string(cfgid, (char *) value);
+    return;
+}
+
+void CC_Config_setByteValue(int cfgid, unsigned char value) {
+    int temp = (int) value;
+
+//  For Now, convert all numeric parameters to Integer.  This simplifies
+//  the required SIP and GSM code changes.  We will go to the right size
+//  when we implement the "full" JNI.
+
+//    config_set_value(cfg_id, &byte_value, sizeof(unsigned char));
+    config_set_value(cfgid, &temp, sizeof(int));
+    return;
+}
+
+void CC_Config_setArrayValue(int cfgid, char *byte_array, int length) {
+    unsigned char *byte_ptr;
+    int i;
+
+    byte_ptr = cpr_malloc(length);
+    if (byte_ptr == NULL) {
+        TNP_DEBUG(DEB_F_PREFIX"setPropertyCacheByteArray():malloc failed.\n", DEB_F_PREFIX_ARGS(JNI, "nSetPropertyCacheByteArray"));
+        return;
+    }
+
+    for (i = 0; i < length; i++) {
+        byte_ptr[i] = (unsigned char) byte_array[i];
+    }
+    config_set_value(cfgid, byte_ptr, length);
+    cpr_free(byte_ptr);
+
+    return;
+}
+
+/**
+ * Set the dialplan file
+ * @param dial_plan_string the dial plan content string
+ * @param length the length of dial plan string, the maximum size will be 0x2000.
+ * @return string dial plan version stamp
+ */
+char* CC_Config_setDialPlan(const char *dial_plan_string, int length) {
+    const char fname[] = "CC_Config_setDialPlan";
+    int ret;
+
+    /**
+     * If the string is null, empty or oversized, we will reset the dial
+     * plan by setting the length to 0.
+     */
+    if (dial_plan_string == NULL || length == 0 || length >= DIALPLAN_MAX_SIZE) {
+        TNP_DEBUG(DEB_F_PREFIX"Setting NULL dialplan string (length [%d] is 0, or length is larger than maximum [%d])\n",
+                DEB_F_PREFIX_ARGS(JNI, fname), length, DIALPLAN_MAX_SIZE);
+
+        dp_init_template (NULL, 0);
+        return (NULL);
+     }
+
+    ret = dp_init_template(dial_plan_string, length);
+    TNP_DEBUG(DEB_F_PREFIX"Parsed dial_plan_string.  Version=[%s], Length=[%d]\n", DEB_F_PREFIX_ARGS(JNI, fname), g_dp_version_stamp, length);
+    if (ret != 0)
+    {
+        return (NULL);
+    }
+
+    return (g_dp_version_stamp);
+}
+
+/**
+ * Set the feature control plan
+ * @param fcp plan string
+ * @param length the length of fcp string
+ * @return string feature version stamp
+ */
+
+char* CC_Config_setFcp(const char *fcp_plan_string, int len) {
+    const   char fname[] = "CC_Config_setFcp";
+    int ret               = 0;
+
+    /**
+     * If the string is null, return null (version)
+     */
+
+    TNP_DEBUG(DEB_F_PREFIX"FCP Parsing FCP doc\n", DEB_F_PREFIX_ARGS(JNI, fname));
+    if (fcp_plan_string == NULL)
+    {
+        TNP_DEBUG(DEB_F_PREFIX"Null FCP xml document\n",
+                DEB_F_PREFIX_ARGS(JNI, fname));
+
+        fcp_init_template (NULL);
+        return (NULL);
+    }
+
+    ret = fcp_init_template (fcp_plan_string);
+    TNP_DEBUG(DEB_F_PREFIX"Parsed FCP xml.  Version=[%s]\n", DEB_F_PREFIX_ARGS(JNI, fname), g_fp_version_stamp);
+    if (ret != 0)
+    {
+        return (NULL);
+    }
+
+    return (g_fp_version_stamp);
+}
diff --git a/libs/sipcc/core/ccapp/cc_device_feature.c b/libs/sipcc/core/ccapp/cc_device_feature.c
new file mode 100644 (file)
index 0000000..930119f
--- /dev/null
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cc_device_feature.h"
+#include "sessionConstants.h"
+#include "sessionTypes.h"
+#include "CCProvider.h"
+#include "phone_debug.h"
+#include "ccapp_task.h"
+
+/**
+ * Internal method
+ */
+void cc_invokeDeviceFeature(session_feature_t *feature) {
+
+    if (ccappTaskPostMsg(CCAPP_INVOKEPROVIDER_FEATURE, feature,
+                       sizeof(session_feature_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+        CCAPP_DEBUG(DEB_F_PREFIX"cc_invokeDeviceFeature failed\n",
+                DEB_F_PREFIX_ARGS("cc_device_feature", "cc_invokeDeviceFeature"));
+    }
+
+}
+
+void CC_DeviceFeature_supportsVideo(boolean enable) {
+    session_feature_t feat;
+
+    feat.session_id = (SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT);
+    feat.featureID = DEVICE_SUPPORTS_NATIVE_VIDEO;
+    feat.featData.ccData.info = NULL;
+    feat.featData.ccData.info1 = NULL;
+    feat.featData.ccData.state = enable;
+    cc_invokeDeviceFeature(&feat);
+}
+
+/**
+ * Enable video/camera.
+ * @param enable true or false
+ * @return void
+ */
+void CC_DeviceFeature_enableVideo(boolean enable) {
+    session_feature_t feat;
+
+    feat.session_id = (SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT);
+    feat.featureID = DEVICE_ENABLE_VIDEO;
+    feat.featData.ccData.info = NULL;
+    feat.featData.ccData.info1 = NULL;
+    feat.featData.ccData.state = enable;
+    cc_invokeDeviceFeature(&feat);
+}
+
+void CC_DeviceFeature_enableCamera(boolean enable) {
+    session_feature_t feat;
+
+    feat.session_id = (SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT);
+    feat.featureID = DEVICE_ENABLE_CAMERA;
+    feat.featData.ccData.info = NULL;
+    feat.featData.ccData.info1 = NULL;
+    feat.featData.ccData.state = enable;
+    cc_invokeDeviceFeature(&feat);
+}
+
diff --git a/libs/sipcc/core/ccapp/cc_device_manager.c b/libs/sipcc/core/ccapp/cc_device_manager.c
new file mode 100644 (file)
index 0000000..5a8c8d9
--- /dev/null
@@ -0,0 +1,593 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cc_constants.h"
+#include "session.h"
+#include "ccSession.h"
+#include "scSession.h"
+#include "cpr_types.h"
+#include "cpr_string.h"
+#include "phone_debug.h"
+#include "sessuri.h"
+#include "config_api.h"
+#include "CCProvider.h"
+#include "ccapp_task.h"
+#include "cc_device_manager.h"
+#include "ccapi_service.h"
+#include "cc_service.h"
+#include "subscription_handler.h"
+#include "ccapi_snapshot.h"
+#include "util_string.h"
+
+
+#define STARTUP_NORMAL           0
+#define STARTUP_UNSPECIFIED      1
+#define SHUTDOWN_NORMAL          2
+#define SHUTDOWN_UNSPECIFIED     3
+#define SHUTDOWN_VERMISMATHC     4
+#define RSP_TYPE_COMPLETE        5
+
+
+mgmt_state_t mgmtState = MGMT_STATE_IDLE;
+//int parse_config_properties (int device_handle, const char *device_name, const char *cfg, int from_memory);
+static boolean isStartRequestPending = FALSE;
+static boolean isServiceStopped = TRUE;
+
+extern cc_boolean is_action_to_be_deferred(cc_action_t action);
+
+
+char *mgmt_event_to_str (int evt) {
+
+    switch (evt) {
+        case EV_CC_CREATE:
+            return "EV_CC_CREATE";
+        case EV_CC_START:
+            return "EV_CC_START";
+        case EV_CC_CONFIG_RECEIVED:
+            return "EV_CC_CONFIG_RECEIVED";
+        case EV_CC_DO_SOFT_RESET:
+            return "EV_CC_DO_SOFT_RESET";
+        case EV_CC_INSERVICE:
+            return "EV_CC_INSERVICE";
+        case EV_CC_OOS_FAILOVER:
+            return "EV_CC_OOS_FAILOVER";
+        case EV_CC_OOS_FALLBACK:
+            return "EV_CC_OOS_FALLBACK";
+        case EV_CC_OOS_REG_ALL_FAILED:
+            return "EV_CC_OOS_REG_ALL_FAILED";
+        case EV_CC_OOS_SHUTDOWN_ACK:
+            return "EV_CC_OOS_SHUTDOWN_ACK";
+        case EV_CC_RE_REGISTER:
+            return "EV_CC_RE_REGISTER";
+        case EV_CC_STOP:
+            return "EV_CC_STOP";
+        case EV_CC_DESTROY:
+            return "EV_CC_DESTROY";
+        case EV_CC_IP_VALID:
+            return "EV_CC_IP_VALID";
+        case EV_CC_IP_INVALID:
+            return "EV_CC_IP_INVALID";
+    }
+
+    return "EV_INVALID";
+}
+
+char *mgmt_state_to_str (int state) {
+
+    switch (state) {
+        case MGMT_STATE_IDLE:
+            return "MGMT_STATE_IDLE";
+        case MGMT_STATE_CREATED:
+            return "MGMT_STATE_CREATED";
+        case MGMT_STATE_REGISTERING:
+            return "MGMT_STATE_REGISTERING";
+        case MGMT_STATE_REGISTERED:
+            return "MGMT_STATE_REGISTERED";
+        case MGMT_STATE_OOS:
+            return "MGMT_STATE_OOS";
+        case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+            return "MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK";
+        case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+            return "MGMT_STATE_WAITING_FOR_CONFIG_FILE";
+        case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+            return "MGMT_STATE_OOS_AWAIT_UN_REG_ACK";
+        case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+            return "MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK";
+        case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+            return "MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK";
+    }
+
+    return "MGMT_STATE_INVALID";
+}
+
+void  updateMediaConfigProperties( void )
+{
+        //TODO: check Java code
+
+
+}
+
+void  updateVideoConfigProperties( void )
+{
+        //TODO: check Java code
+
+}
+
+/*
+ * action for handled events for device manager
+ */
+int action(int cmd)
+{
+    int retVal = 0;
+    sessionProvider_cmd_t proCmd;
+
+    memset ( &proCmd, 0, sizeof(sessionProvider_cmd_t));
+    proCmd.sessionType = SESSIONTYPE_CALLCONTROL;
+    proCmd.cmd = cmd;
+
+    if (cmd == CMD_INSERVICE ) {
+        CCAPP_DEBUG("CC_device_manager_action: CMD_INSERVICE \n");
+        updateMediaConfigProperties();
+        updateVideoConfigProperties();
+        proCmd.cmdData.ccData.reason = STARTUP_NORMAL;
+        if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                       sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+            CCAPP_DEBUG("ccInvokeFeature: ccappTaskSendMsg failed\n");
+        }
+    } else if (cmd == CMD_INIT) {
+
+        CCAPP_DEBUG("CC_device_manager_action: CMD_INIT \n");
+        proCmd.cmdData.ccData.reason = STARTUP_NORMAL;
+
+        if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                       sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+            CCAPP_DEBUG("ccInvokeFeature: ccappTaskSendMsg failed\n");
+        }
+    } else if (cmd == CMD_RESTART) {
+
+        CCAPP_DEBUG("CC_device_manager_action: CMD_RESTART \n");
+        updateMediaConfigProperties();
+        proCmd.cmdData.ccData.reason = STARTUP_NORMAL;
+
+        if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                       sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+            CCAPP_DEBUG("ccInvokeFeature: ccappTaskSendMsg failed\n");
+        }
+
+    } else if (cmd == CMD_SHUTDOWN) {
+
+        CCAPP_DEBUG("CC_device_manager_action: CMD_SHUTDOWN \n");
+        proCmd.cmdData.ccData.reason = CC_CAUSE_REG_ALL_FAILED;
+        if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                       sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+            CCAPP_DEBUG("ccInvokeFeature: ccappTaskSendMsg failed\n");
+        }
+    } else {
+
+        proCmd.cmdData.ccData.reason = STARTUP_NORMAL;
+        CCAPP_DEBUG("CC_device_manager_action: Default \n");
+        if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                       sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+            CCAPP_DEBUG("ccInvokeFeature: ccappTaskSendMsg failed\n");
+        }
+
+    }
+
+    return retVal;
+
+}
+
+
+cc_boolean is_phone_registered() {
+    if (mgmtState == MGMT_STATE_REGISTERED) {
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+/*
+ * Wrapper function for settting state for handled events for device manager
+ */
+void setState(int st) {
+        DEF_DEBUG("setState: new registration state=  %s\n", mgmt_state_to_str(st));
+        mgmtState = st;
+}
+
+void processInsToOos (void )
+{
+        //CCAPP_DEBUG("CC_device_manager:  processInsToOoS \n");
+        DEF_DEBUG("CC_device_manager:  processInsToOoS \n");
+        sub_hndlr_stop();
+}
+
+void prepareForSoftReset()
+{
+        CCAPP_DEBUG("CC_device_manager:  prepareForSoftReset\n");
+
+}
+
+
+void processInserviceEvent( void)
+{
+    CCAPP_DEBUG("CC_device_manager:  process Inservice Event\n");
+     if (g_deviceInfo.cucm_mode == CC_MODE_CCM ) {
+        if (sub_hndlr_isAvailable() == FALSE) {
+            sub_hndlr_start();
+        }
+    }
+        setState(MGMT_STATE_REGISTERED);
+        //TODO: check Java code
+}
+
+
+/*
+ * Event handler for device manager
+ */
+void registration_processEvent(int event) {
+
+        boolean ignored=0;
+
+        DEF_DEBUG("registration_processEvent:  Event %s, current State %s \n",
+                     mgmt_event_to_str(event) , mgmt_state_to_str(mgmtState));
+
+        switch (event) {
+            case EV_CC_CREATE:
+                switch ( mgmtState) {
+                    case MGMT_STATE_IDLE:
+                        setState(MGMT_STATE_CREATED);
+                        init_empty_str(g_cfg_p);
+                        CC_Service_create();
+                        CC_Service_init();
+                    break;
+                    case MGMT_STATE_REGISTERED:
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+            break;
+
+            case EV_CC_START:
+                isServiceStopped = FALSE;
+                switch ( mgmtState) {
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                        configFetchReq(0);
+                    break;
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                        DEF_DEBUG("registration_processEvent: delaying start until SHUTDOWN_ACK is received.");
+                        isStartRequestPending = TRUE;
+                    break;
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_REGISTERED:
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+            break;
+
+            case EV_CC_IP_INVALID:
+                switch ( mgmtState) {
+                    case MGMT_STATE_REGISTERED:
+                        processInsToOos();
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_OOS:
+                        setState(MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK);
+                        action(CMD_UNREGISTER_ALL_LINES);
+                    break;
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                        setState(MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK);
+                    break;
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+
+                break;
+
+            case EV_CC_IP_VALID:
+                if (isServiceStopped == TRUE) {
+                    ignored = 1;
+                    DEF_DEBUG("registration_processEvent: "\
+                    "Ignoring IP_VALID as service was not Started ");
+                    break;
+                }
+                switch ( mgmtState) {
+                    case MGMT_STATE_REGISTERED:
+                    if (is_action_to_be_deferred(RE_REGISTER_ACTION)
+                                == FALSE) {
+                        processInsToOos();
+                        prepareForSoftReset();
+                        setState(MGMT_STATE_OOS_AWAIT_UN_REG_ACK);
+                        action(CMD_SHUTDOWN);
+                    }
+                    break;
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_OOS:
+                    if (is_action_to_be_deferred(RE_REGISTER_ACTION)
+                                == FALSE) {
+                        prepareForSoftReset();
+                        setState(MGMT_STATE_OOS_AWAIT_UN_REG_ACK);
+                        action(CMD_SHUTDOWN);
+                    }
+                    break;
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+            break;
+
+            case EV_CC_DO_SOFT_RESET:
+                switch ( mgmtState) {
+                    case MGMT_STATE_REGISTERED:
+                        processInsToOos();          /* FALL THROUGH */
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_OOS:
+                        prepareForSoftReset();
+                        setState(MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK);
+                        action(CMD_SHUTDOWN);
+                        break;
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+                break;
+
+            case EV_CC_CONFIG_RECEIVED:
+                switch ( mgmtState) {
+                    case MGMT_STATE_CREATED:    // This state is only at init
+                                             // needs handler to be added on receiving message
+                        setState(MGMT_STATE_REGISTERING);
+                        action(CMD_INSERVICE);
+                        break;
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                        setState(MGMT_STATE_REGISTERING);
+                        action(CMD_RESTART);
+                        break;
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_REGISTERED:
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+                break;
+
+            case EV_CC_INSERVICE:
+                switch (mgmtState) {
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_REGISTERING:
+                        setState(MGMT_STATE_REGISTERED);
+                    case MGMT_STATE_REGISTERED:
+                        processInserviceEvent();
+                        break;
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+                break;
+
+            case EV_CC_OOS_FAILOVER:
+            case EV_CC_OOS_FALLBACK:
+                switch ( mgmtState) {
+                    case MGMT_STATE_REGISTERED:
+                        processInsToOos();
+                    case MGMT_STATE_REGISTERING:
+                        setState(MGMT_STATE_OOS);
+                        break;
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+                break;
+            case EV_CC_OOS_REG_ALL_FAILED:
+                switch ( mgmtState) {
+                    case MGMT_STATE_REGISTERED:
+                        processInsToOos();      /* FALL THROUGH */
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_REGISTERING:
+                        setState(MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK);
+                        action(CMD_SHUTDOWN);
+                        break;
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+                break;
+
+                /*
+                 *This event shutdown sip stack to facilitate re-registration of
+                 * all lines.
+                 */
+            case EV_CC_RE_REGISTER:
+                switch ( mgmtState) {
+                    case MGMT_STATE_REGISTERED:
+                        processInsToOos();
+                        setState(MGMT_STATE_OOS_AWAIT_UN_REG_ACK);
+                        action(CMD_UNREGISTER_ALL_LINES);
+                        break;
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+                break;
+
+            case EV_CC_DESTROY:
+                isStartRequestPending = FALSE;
+                isServiceStopped = TRUE;
+                switch ( mgmtState) {
+                    case MGMT_STATE_REGISTERED:
+                    CC_Service_destroy();
+                        processInsToOos();
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                        setState(MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK);
+                        action(CMD_UNREGISTER_ALL_LINES);
+                        break;
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                        //setState(MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK);
+                        setState(MGMT_STATE_IDLE);
+                        CC_Service_destroy();
+                        break;
+                    case MGMT_STATE_CREATED:
+                        CC_Service_destroy();
+                        break;
+                    case MGMT_STATE_IDLE:
+                       CC_Service_destroy();
+                       break;
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                        setState(MGMT_STATE_IDLE);
+
+                    default:
+                        ignored = 1;
+                        break;
+                }
+
+            break;
+            case EV_CC_STOP:
+                isStartRequestPending = FALSE;
+                isServiceStopped = TRUE;
+                switch ( mgmtState) {
+                    case MGMT_STATE_REGISTERED:
+                        processInsToOos();
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                        setState(MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK);
+                        action(CMD_UNREGISTER_ALL_LINES);
+                    break;
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                        setState(MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK);
+                    break;
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_IDLE:
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                        setState(MGMT_STATE_IDLE);
+                                               //action(CMD_SHUTDOWN);
+                                               CC_Service_destroy();
+
+                    default:
+                        ignored = 1;
+                        break;
+                }
+            break;
+            case EV_CC_OOS_SHUTDOWN_ACK:
+                 switch ( mgmtState) {
+                    case MGMT_STATE_OOS_AWAIT_UN_REG_ACK:
+                        setState(MGMT_STATE_REGISTERING);
+                        action(CMD_RESTART);
+                        break;
+                    case MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK:
+                        setState(MGMT_STATE_WAITING_FOR_CONFIG_FILE);
+                        configFetchReq(0);
+                       setState(MGMT_STATE_IDLE);
+                        break;
+                    case MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK:
+                        setState(MGMT_STATE_WAITING_FOR_CONFIG_FILE);
+                        if (isStartRequestPending == TRUE) {
+                            isStartRequestPending = FALSE;
+                            configFetchReq(0);
+                        }
+                       setState(MGMT_STATE_IDLE);
+                    break;
+                    case MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK:
+                        CC_Service_destroy();
+                    break;
+                    case MGMT_STATE_REGISTERED:
+                    case MGMT_STATE_WAITING_FOR_CONFIG_FILE:
+                    case MGMT_STATE_OOS:
+                    case MGMT_STATE_REGISTERING:
+                    case MGMT_STATE_CREATED:
+                    case MGMT_STATE_IDLE:
+                    default:
+                        ignored = 1;
+                        break;
+                }
+                break;
+            default:
+                break;
+        }
+
+        if (ignored) {
+            DEF_DEBUG("registration_processEvent: IGNORED  Event  %s in State %s \n",
+                     mgmt_event_to_str(event) , mgmt_state_to_str(mgmtState));
+        }
+
+}
+
+
+
diff --git a/libs/sipcc/core/ccapp/cc_device_manager.h b/libs/sipcc/core/ccapp/cc_device_manager.h
new file mode 100644 (file)
index 0000000..ed0dbec
--- /dev/null
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Management Events
+ */
+ typedef enum {
+ EV_CC_CREATE=0,
+ EV_CC_START,
+ EV_CC_CONFIG_RECEIVED ,
+ EV_CC_DO_SOFT_RESET ,
+ EV_CC_INSERVICE,
+ EV_CC_OOS_FAILOVER,
+ EV_CC_OOS_FALLBACK,
+ EV_CC_OOS_REG_ALL_FAILED,
+ EV_CC_OOS_SHUTDOWN_ACK,
+ EV_CC_RE_REGISTER,
+ EV_CC_STOP,
+ EV_CC_DESTROY,
+ EV_CC_IP_VALID,
+ EV_CC_IP_INVALID
+} mgmt_event_t;
+
+
+/**
+ * Management states
+ */
+typedef enum {
+MGMT_STATE_CREATED=0,
+MGMT_STATE_IDLE,
+MGMT_STATE_REGISTERING,
+MGMT_STATE_REGISTERED,
+MGMT_STATE_OOS,
+MGMT_STATE_OOS_AWAIT_SHUTDOWN_ACK,
+MGMT_STATE_WAITING_FOR_CONFIG_FILE,
+MGMT_STATE_OOS_AWAIT_UN_REG_ACK,
+MGMT_STATE_STOP_AWAIT_SHUTDOWN_ACK,
+MGMT_STATE_DESTROY_AWAIT_SHUTDOWN_ACK
+} mgmt_state_t;
+
+extern void registration_processEvent(int event);
+cc_boolean is_phone_registered();
diff --git a/libs/sipcc/core/ccapp/cc_info.c b/libs/sipcc/core/ccapp/cc_info.c
new file mode 100644 (file)
index 0000000..d611710
--- /dev/null
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cc_info.h"
+#include "sessionTypes.h"
+#include "phone_debug.h"
+#include "CCProvider.h"
+#include "sessionConstants.h"
+#include "ccapp_task.h"
+
+/**
+ * Send call information.
+ * @param call_handle call handle
+ * @param info_package the Info-Package header of the Info Package
+ * @param info_type the Content-Type header of the Info Package
+ * @param info_body the message body of the Info Package
+ * @return void
+ */
+void CC_Info_sendInfo(cc_call_handle_t call_handle,
+        string_t info_package,
+        string_t info_type,
+        string_t info_body) {
+    static const char *fname = "CC_Info_sendInfo";
+    session_send_info_t send_info;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"entry... call_handle=0x%x\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_SES, fname), call_handle);
+
+    send_info.sessionID= (SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT) + call_handle;;
+    send_info.generic_raw.info_package = strlib_malloc(info_package, strlen(info_package));
+    send_info.generic_raw.content_type = strlib_malloc(info_type, strlen(info_type));
+    send_info.generic_raw.message_body = strlib_malloc(info_body, strlen(info_body));
+
+    /* Once the msg is posted to ccapp_msgq, ccapp 'owns' these strings */
+    // ccappTaskPostMsg does a shallow copy of *send_info
+    if (ccappTaskPostMsg(CCAPP_SEND_INFO, &send_info,
+                         sizeof(session_send_info_t), CCAPP_CCPROVIER) != CPR_SUCCESS) {
+        CCAPP_ERROR(DEB_F_PREFIX"ccappTaskPostMsg failed\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_SES, fname));
+    }
+
+}
diff --git a/libs/sipcc/core/ccapp/cc_service.c b/libs/sipcc/core/ccapp/cc_service.c
new file mode 100644 (file)
index 0000000..dd3dc3c
--- /dev/null
@@ -0,0 +1,229 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cc_service.h"
+#include "phone_debug.h"
+#include "CCProvider.h"
+#include "sessionConstants.h"
+#include "ccsip_messaging.h"
+#include "ccapp_task.h"
+
+/**
+ * External init.c methods
+ */
+extern int ccPreInit ();
+extern int ccInit ();
+extern int ccUnload ();
+extern void protCfgTblInit();
+
+extern cc_int32_t SipDebugMessage;
+extern cc_int32_t SipDebugState;
+extern cc_int32_t SipDebugTask;
+extern cc_int32_t SipDebugRegState;
+extern cc_int32_t GSMDebug;
+extern cc_int32_t FIMDebug;
+extern cc_int32_t LSMDebug;
+extern cc_int32_t FSMDebugSM;
+extern int32_t CSMDebugSM;
+extern cc_int32_t CCDebug;
+extern cc_int32_t CCDebugMsg;
+extern cc_int32_t AuthDebug;
+extern cc_int32_t ConfigDebug;
+extern cc_int32_t DpintDebug;
+extern cc_int32_t KpmlDebug;
+extern cc_int32_t VCMDebug;
+extern cc_int32_t PLATDebug;
+extern cc_int32_t CCEVENTDebug;
+extern cc_int32_t g_CCAppDebug;
+extern cc_int32_t g_CCLogDebug;
+extern cc_int32_t TNPDebug;
+
+/**
+  * Initialize all the debug variables
+  */
+void dbg_init(void)
+{
+    /* This is for the RT/TNP products */
+    SipDebugMessage = 1;
+    SipDebugState = 1;
+    SipDebugTask = 1;
+    SipDebugRegState = 1;
+    GSMDebug = 1;
+    FIMDebug = 1;
+    LSMDebug = 1;
+    FSMDebugSM = 1;
+    CSMDebugSM = 0;
+    VCMDebug = 1;
+    PLATDebug = 1;
+    CCEVENTDebug = 0;
+    CCDebug = 0;
+    CCDebugMsg = 0;
+    AuthDebug = 1;
+    TNPDebug = 1;
+    ConfigDebug = 1;
+    DpintDebug = 0;
+    KpmlDebug = 0;
+    g_CCAppDebug = 1;
+    g_CCLogDebug = 1;
+    TNPDebug = 1;
+    g_NotifyCallDebug = 0;
+    g_NotifyLineDebug = 0;
+}
+/**
+ * Defines the management methods.
+ */
+/**
+ * The following methods are defined to bring up the pSipcc stack
+ */
+/**
+ * Initialize the pSipcc stack.
+ * @return
+ */
+cc_return_t CC_Service_init() {
+    //Initialize stack
+    return ccInit();
+}
+
+/**
+ * Pre-initialize the pSipcc stack.
+ * @return
+ */
+cc_return_t CC_Service_create() {
+    //Preinitialize memory
+    ccPreInit();
+
+    //Initialize debug settings
+    dbg_init();
+
+    //Prepopulate the Configuration data table
+    protCfgTblInit();
+
+    return CC_SUCCESS;
+}
+
+/**
+ * Gracefully unload the pSipcc stack
+ * @return
+ */
+cc_return_t CC_Service_destroy() {
+    ccUnload();
+    return CC_SUCCESS;
+}
+
+/**
+ * Bring up the pSipcc stack in service
+ * @return
+ */
+cc_return_t CC_Service_start() {
+    sessionProvider_cmd_t proCmd;
+
+    CCAPP_DEBUG("CC_Service_start \n");
+
+    memset ( &proCmd, 0, sizeof(sessionProvider_cmd_t));
+    proCmd.sessionType = SESSIONTYPE_CALLCONTROL; //Not used
+    proCmd.cmd = CMD_INSERVICE;
+
+    if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                         sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+        CCAPP_DEBUG("CC_Service_start: ccappTaskSendMsg failed\n");
+        return CC_FAILURE;
+    }
+    return CC_SUCCESS;
+}
+
+/**
+ * Shutdown pSipcc stack for restarting
+ * @param mgmt_reason the reason to shutdown pSipcc stack
+ * @param reason_string literal string for shutdown
+ * @return
+ */
+cc_return_t CC_Service_shutdown(cc_shutdown_reason_t mgmt_reason, string_t reason_string) {
+    sessionProvider_cmd_t proCmd;
+
+    CCAPP_DEBUG("CC_Service_shutdown \n");
+
+    memset ( &proCmd, 0, sizeof(sessionProvider_cmd_t));
+    proCmd.sessionType = SESSIONTYPE_CALLCONTROL; //Not used
+    proCmd.cmd = CMD_SHUTDOWN;
+
+    if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                         sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+        CCAPP_DEBUG("CC_Service_shutdown: ccappTaskSendMsg failed\n");
+        return CC_FAILURE;
+    }
+    return CC_SUCCESS;
+}
+
+/**
+ * Unregister all lines of a phone
+ * @param mgmt_reason the reason to bring down the registration
+ * @param reason_string the literal string for unregistration
+ * @return
+ */
+cc_return_t CC_Service_unregisterAllLines(cc_shutdown_reason_t mgmt_reason, string_t reason_string) {
+    sessionProvider_cmd_t proCmd;
+
+    CCAPP_DEBUG("CC_Service_shutdown \n");
+
+    memset ( &proCmd, 0, sizeof(sessionProvider_cmd_t));
+    proCmd.sessionType = SESSIONTYPE_CALLCONTROL; //Not used
+    proCmd.cmd = CMD_SHUTDOWN;
+    proCmd.cmdData.ccData.reason = mgmt_reason;
+    proCmd.cmdData.ccData.reason_info = reason_string;
+
+    if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                         sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+        CCAPP_DEBUG("CC_Service_shutdown: ccappTaskSendMsg failed\n");
+        return CC_FAILURE;
+    }
+    return CC_SUCCESS;
+}
+
+/**
+ * Register all lines for a phone.
+ * @param mgmt_reason the reason of registration
+ * @param reason_string the literal string of the registration
+ * @return
+ */
+cc_return_t CC_Service_registerAllLines(cc_shutdown_reason_t mgmt_reason, string_t reason_string) {
+    sessionProvider_cmd_t proCmd;
+
+    CCAPP_DEBUG("CC_Service_registerAllLines \n");
+
+    memset ( &proCmd, 0, sizeof(sessionProvider_cmd_t));
+    proCmd.sessionType = SESSIONTYPE_CALLCONTROL; //Not used
+    proCmd.cmd = CMD_REGISTER_ALL_LINES;
+    proCmd.cmdData.ccData.reason = mgmt_reason;
+    proCmd.cmdData.ccData.reason_info = reason_string;
+
+    if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                         sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+        CCAPP_DEBUG("CC_Service_registerAllLines: ccappTaskSendMsg failed\n");
+        return CC_FAILURE;
+    }
+    return CC_SUCCESS;
+}
+
+/**
+ * Restart pSipcc stack
+ * @return
+ */
+cc_return_t CC_Service_restart() {
+    sessionProvider_cmd_t proCmd;
+
+    CCAPP_DEBUG("CC_Service_restart \n");
+
+    memset ( &proCmd, 0, sizeof(sessionProvider_cmd_t));
+    proCmd.sessionType = SESSIONTYPE_CALLCONTROL; //Not used
+    proCmd.cmd = CMD_RESTART;
+
+    if (ccappTaskPostMsg(CCAPP_SERVICE_CMD, (cprBuffer_t)&proCmd,
+                         sizeof(sessionProvider_cmd_t), CCAPP_CCPROVIER) == CPR_FAILURE) {
+        CCAPP_DEBUG("CC_Service_restart: ccappTaskSendMsg failed\n");
+        return CC_FAILURE;
+    }
+    return CC_SUCCESS;
+}
+
+
diff --git a/libs/sipcc/core/ccapp/ccapi_call.c b/libs/sipcc/core/ccapp/ccapi_call.c
new file mode 100644 (file)
index 0000000..8af7198
--- /dev/null
@@ -0,0 +1,375 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdio.h"
+#include "ccapi_call.h"
+#include "sessionHash.h"
+#include "CCProvider.h"
+#include "cc_call_feature.h"
+#include "cc_info.h"
+#include "lsm.h"
+#include "prot_configmgr.h"
+#include "ccapi_call_info.h"
+#include "util_string.h"
+
+/**
+ * Get call info snapshot
+ * @param [in] handle - call handle
+ * @return cc_call_info_snap_t
+ */
+cc_callinfo_ref_t CCAPI_Call_getCallInfo(cc_call_handle_t handle) {
+   unsigned int session_id = ccpro_get_sessionId_by_callid(GET_CALL_ID(handle));
+   cc_callinfo_ref_t snapshot=NULL;
+   session_data_t * data;
+
+   if ( session_id != 0 ) {
+      data = findhash(session_id);
+      if ( data != NULL ) {
+        snapshot = getDeepCopyOfSessionData(data);
+        if (snapshot == NULL) {
+            return NULL;
+        }
+        snapshot->ref_count = 1;
+      }
+   }
+   return snapshot;
+}
+/**
+ * Retain the snapshot
+ * @param cc_callinfo_ref_t - refrence to the block to be retained
+ * @return void
+ */
+void CCAPI_Call_retainCallInfo(cc_callinfo_ref_t ref) {
+    if (ref != NULL ) {
+        ref->ref_count++;
+    }
+}
+/**
+ * Free the snapshot
+ * @param cc_callinfo_ref_t - refrence to the block to be freed
+ * @return void
+ */
+void CCAPI_Call_releaseCallInfo(cc_callinfo_ref_t ref) {
+    if (ref != NULL ) {
+       DEF_DEBUG(DEB_F_PREFIX"ref=0x%x: count=%d",
+           DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI_Call_releaseCallInfo"), ref, ref->ref_count);
+       ref->ref_count--;
+       if ( ref->ref_count == 0 ) {
+            cleanSessionData(ref);
+            cpr_free(ref);
+       }
+    }
+}
+
+/**
+ * get the line associated with this call
+ * @param [in] handle - call handle
+ * @return cc_lineid_t
+ */
+cc_lineid_t CCAPI_Call_getLine(cc_call_handle_t call_handle){
+       static const char *fname="CCAPI_Call_getLine";
+
+       if ( call_handle != 0 ) {
+       cc_lineid_t lineid = GET_LINE_ID(call_handle);
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %u\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), lineid);
+       return lineid;
+       }
+       return 0;
+}
+
+/**
+ * Originate call
+ * Goes offhook and dials digits if specified
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @param [in] digits - digits to be dialed
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_originateCall(cc_call_handle_t handle, cc_sdp_direction_t video_pref, cc_string_t digits){
+       return CC_CallFeature_dial(handle, video_pref, digits);
+}
+
+cc_return_t CCAPI_CreateOffer(cc_call_handle_t handle, const cc_media_constraints_t *constraints) {
+       return CC_CallFeature_CreateOffer(handle, constraints);
+}
+
+cc_return_t CCAPI_CreateAnswer(cc_call_handle_t handle, const cc_media_constraints_t *constraints) {
+       return CC_CallFeature_CreateAnswer(handle, constraints);
+}
+
+cc_return_t CCAPI_SetLocalDescription(cc_call_handle_t handle, cc_jsep_action_t action, cc_string_t sdp) {
+       return CC_CallFeature_SetLocalDescription(handle, action, sdp);
+}
+
+cc_return_t CCAPI_SetRemoteDescription(cc_call_handle_t handle, cc_jsep_action_t action, cc_string_t sdp) {
+    return CC_CallFeature_SetRemoteDescription(handle, action, sdp);
+}
+
+cc_return_t CCAPI_SetPeerConnection(cc_call_handle_t handle, cc_peerconnection_t pc) {
+  return CC_CallFeature_SetPeerConnection(handle, pc);
+}
+
+cc_return_t CCAPI_AddStream(cc_call_handle_t handle, cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type) {
+  return CC_CallFeature_AddStream(handle, stream_id, track_id, media_type);
+}
+
+cc_return_t CCAPI_RemoveStream(cc_call_handle_t handle, cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type) {
+  return CC_CallFeature_RemoveStream(handle, stream_id, track_id, media_type);
+}
+
+cc_return_t CCAPI_AddICECandidate(cc_call_handle_t handle, cc_string_t candidate, cc_string_t mid, cc_level_t level) {
+       return CC_CallFeature_AddICECandidate(handle, candidate, mid, level);
+}
+
+/**
+ * Dial digits on the call
+ * @param [in] handle - call handle
+ * @paraqm [in] digits - digits to be dialed
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_sendDigit(cc_call_handle_t handle, cc_digit_t digit){
+   return CC_CallFeature_sendDigit(handle, digit);
+}
+
+/**
+ * Send Backspace
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_backspace(cc_call_handle_t handle){
+   return CC_CallFeature_backSpace(handle);
+}
+
+/**
+ * Answer Call
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_answerCall(cc_call_handle_t handle, cc_sdp_direction_t video_pref) {
+  return CC_CallFeature_answerCall(handle, video_pref);
+}
+
+/**
+ * Redial
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_redial(cc_call_handle_t handle, cc_sdp_direction_t video_pref){
+  return CC_CallFeature_redial(handle, video_pref);
+}
+
+/**
+ * Initiate Call Forward All
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_initiateCallForwardAll(cc_call_handle_t handle){
+  return CC_CallFeature_callForwardAll(handle);
+}
+/**
+ * Hold
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_hold(cc_call_handle_t handle, cc_hold_reason_t reason){
+  return CC_CallFeature_holdCall(handle, reason);
+}
+
+/**
+ * Resume
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_resume(cc_call_handle_t handle, cc_sdp_direction_t video_pref) {
+  return CC_CallFeature_resume(handle, video_pref);
+}
+
+/**
+ * end Consult leg
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_endConsultativeCall(cc_call_handle_t handle){
+  cc_callinfo_ref_t info_handle = CCAPI_Call_getCallInfo(handle);
+  cc_call_attr_t attr = CCAPI_CallInfo_getCallAttr(info_handle);
+  if (attr != CC_ATTR_CONF_CONSULT &&
+    attr != CC_ATTR_XFR_CONSULT &&
+    attr != CC_ATTR_LOCAL_CONF_CONSULT &&
+    attr != CC_ATTR_LOCAL_XFER_CONSULT) {
+    DEF_DEBUG(DEB_F_PREFIX"This method only calls on a consultative call",
+      DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI_Call_endConsultativeCall"), handle);
+    return CC_FAILURE;
+  }
+
+  return CC_CallFeature_endConsultativeCall(handle);
+}
+
+/**
+ * end Call
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_endCall(cc_call_handle_t handle){
+  return CC_CallFeature_terminateCall(handle);
+}
+
+/**
+ * Initiate a conference
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on consult call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_conferenceStart(cc_call_handle_t handle, cc_sdp_direction_t video_pref){
+  return CC_CallFeature_conference(handle, TRUE,//not used
+                  CC_EMPTY_CALL_HANDLE, video_pref);
+}
+
+/**
+ * complete conference
+ * @param [in] handle - call handle
+ * @param [in] phandle - call handle of the other leg
+ * @param [in] video_pref - video direction desired on consult call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_conferenceComplete(cc_call_handle_t handle, cc_call_handle_t phandle,
+                          cc_sdp_direction_t video_pref){
+  return CC_CallFeature_conference(handle, TRUE,//not used
+                  phandle, video_pref);
+
+}
+
+/**
+ * start transfer
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on consult call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_transferStart(cc_call_handle_t handle, cc_sdp_direction_t video_pref){
+  return CC_CallFeature_transfer(handle, CC_EMPTY_CALL_HANDLE, video_pref);
+}
+
+/**
+ * complete transfer
+ * @param [in] handle - call handle
+ * @param [in] phandle - call handle of the other leg
+ * @param [in] video_pref - video direction desired on consult call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_transferComplete(cc_call_handle_t handle, cc_call_handle_t phandle,
+                              cc_sdp_direction_t video_pref){
+  return CC_CallFeature_transfer(handle, phandle, video_pref);
+}
+
+/**
+ * cancel conference or transfer
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_cancelTransferOrConferenceFeature(cc_call_handle_t handle){
+  return CC_CallFeature_cancelXfrerCnf(handle);
+}
+
+/**
+ * direct Transfer
+ * @param [in] handle - call handle
+ * @param [in] handle - transfer target call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_directTransfer(cc_call_handle_t handle, cc_call_handle_t target){
+  return CC_CallFeature_directTransfer(handle, target);
+}
+
+/**
+ * Join Across line
+ * @param [in] handle - call handle
+ * @param [in] handle - join target
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_joinAcrossLine(cc_call_handle_t handle, cc_call_handle_t target){
+  return CC_CallFeature_joinAcrossLine(handle, target);
+}
+
+/**
+ * BLF Call Pickup
+ * @param [in] handle - call handle
+ * @param [in] speed - speedDial Number
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_blfCallPickup(cc_call_handle_t handle,
+                  cc_sdp_direction_t video_pref, cc_string_t speed){
+  return CC_CallFeature_blfCallPickup(handle, video_pref, speed);
+}
+
+/**
+ * Select a call
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_select(cc_call_handle_t handle){
+  return CC_CallFeature_select(handle);
+}
+
+/**
+ * Update Video Media Cap for the call
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_updateVideoMediaCap (cc_call_handle_t handle, cc_sdp_direction_t video_pref) {
+  return CC_CallFeature_updateCallMediaCapability(handle, video_pref);
+}
+
+/**
+ * send INFO method for the call
+ * @param [in] handle - call handle
+ * @param [in] infopackage - Info-Package header value
+ * @param [in] infotype - Content-Type header val
+ * @param [in] infobody - Body of the INFO message
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_sendInfo (cc_call_handle_t handle, cc_string_t infopackage, cc_string_t infotype, cc_string_t infobody)
+{
+       CC_Info_sendInfo(handle, infopackage, infotype, infobody);
+        return CC_SUCCESS;
+}
+
+/**
+ * API to mute/unmute audio
+ * @param [in] val - TRUE=> mute FALSE => unmute
+ * @return SUCCESS or FAILURE
+ * NOTE: The mute state is persisted within the stack and shall be remembered across hold/resume.
+ * This API doesn't perform the mute operation but simply caches the mute state of the session.
+ */
+cc_return_t CCAPI_Call_setAudioMute (cc_call_handle_t handle, cc_boolean val) {
+       unsigned int session_id = ccpro_get_sessionId_by_callid(GET_CALL_ID(handle));
+        session_data_t * sess_data_p = (session_data_t *)findhash(session_id);
+       DEF_DEBUG(DEB_F_PREFIX": val=%d, handle=%d datap=%x",
+           DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI_Call_setAudioMute"), val, handle, sess_data_p);
+       if ( sess_data_p != NULL ) {
+               sess_data_p->audio_mute = val;
+       }
+        return CC_SUCCESS;
+}
+
+/**
+ * API to mute/unmute Video
+ * @param [in] val - TRUE=> mute FALSE => unmute
+ * @return SUCCESS or FAILURE
+ * NOTE: The mute state is persisted within the stack and shall be remembered across hold/resume
+ * This API doesn't perform the mute operation but simply caches the mute state of the session.
+ */
+cc_return_t CCAPI_Call_setVideoMute (cc_call_handle_t handle, cc_boolean val){
+       unsigned int session_id = ccpro_get_sessionId_by_callid(GET_CALL_ID(handle));
+        session_data_t * sess_data_p = (session_data_t *)findhash(session_id);
+       DEF_DEBUG(DEB_F_PREFIX": val=%d, handle=%d datap=%x",
+           DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI_Call_setVideoMute"), val, handle, sess_data_p);
+       if ( sess_data_p != NULL ) {
+               sess_data_p->video_mute = val;
+               lsm_set_video_mute(GET_CALL_ID(handle), val);
+       }
+        return CC_SUCCESS;
+}
diff --git a/libs/sipcc/core/ccapp/ccapi_call_info.c b/libs/sipcc/core/ccapp/ccapi_call_info.c
new file mode 100644 (file)
index 0000000..20b3793
--- /dev/null
@@ -0,0 +1,784 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdio.h"
+#include "ccapi_call.h"
+#include "sessionHash.h"
+#include "CCProvider.h"
+#include "text_strings.h"
+#include "phone_debug.h"
+#include "peer_connection_types.h"
+
+/**
+ * get Line on which this call is
+ * @param [in] handle - call handle
+ * @return cc_line_id_t - line ID
+ */
+cc_lineid_t CCAPI_CallInfo_getLine(cc_callinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_CallInfo_getLine";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %u\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), GET_LINE_ID(CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id)));
+     return GET_LINE_ID(CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id));
+  }
+
+  return 0;
+}
+
+/**
+ * get Call state
+ * @param handle - call handle
+ * @return call state
+ */
+cc_call_state_t CCAPI_CallInfo_getCallState(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getCallState";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->state);
+     return data->state;
+  }
+
+  return ONHOOK;
+}
+
+/**
+ * get call attributes
+ * @param handle - call handle
+ * @return call attributes
+ */
+cc_call_attr_t CCAPI_CallInfo_getCallAttr(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getCallAttr";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->attr);
+     return data->attr;
+  }
+
+  return 0;
+}
+
+/**
+ * get Call Type
+ * @param handle - call handle
+ * @return call type
+ */
+cc_call_type_t CCAPI_CallInfo_getCallType(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getCallType";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->type);
+     return data->type;
+  }
+
+  return 0;
+}
+
+/**
+ * get Called party name
+ * @param handle - call handle
+ * @return called party name
+ */
+cc_string_t CCAPI_CallInfo_getCalledPartyName(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getCalledPartyName";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->cld_name);
+     return data->cld_name;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get Called party number
+ * @param handle - call handle
+ * @return called party number
+ */
+cc_string_t CCAPI_CallInfo_getCalledPartyNumber(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getCalledPartyNumber";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->cld_number);
+     return data->cld_number;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get Calling party name
+ * @param handle - call handle
+ * @return calling party name
+ */
+cc_string_t CCAPI_CallInfo_getCallingPartyName(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getCallingPartyName";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->clg_name);
+     return data->clg_name;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get Calling party number
+ * @param handle - call handle
+ * @return calling party number
+ */
+cc_string_t CCAPI_CallInfo_getCallingPartyNumber(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getCallingPartyNumber";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->clg_number);
+     return data->clg_number;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get alternate number
+ * @param handle - call handle
+ * @return calling party number
+ */
+cc_string_t CCAPI_CallInfo_getAlternateNumber(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getAlternateNumber";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->alt_number);
+     return data->alt_number;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get Original Called party name
+ * @param handle - call handle
+ * @return original called party name
+ */
+cc_string_t CCAPI_CallInfo_getOriginalCalledPartyName(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getOriginalCalledPartyName";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->orig_called_name);
+     return data->orig_called_name;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get Original Called party number
+ * @param handle - call handle
+ * @return original called party number
+ */
+cc_string_t CCAPI_CallInfo_getOriginalCalledPartyNumber(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getOriginalCalledPartyNumber";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->orig_called_number);
+     return data->orig_called_number;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get last redirecting party name
+ * @param handle - call handle
+ * @return last redirecting party name
+ */
+cc_string_t CCAPI_CallInfo_getLastRedirectingPartyName(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getLastRedirectingPartyName";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->last_redir_name);
+     return data->last_redir_name;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get past redirecting party number
+ * @param handle - call handle
+ * @return last redirecting party number
+ */
+cc_string_t CCAPI_CallInfo_getLastRedirectingPartyNumber(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getLastRedirectingPartyNumber";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->last_redir_number);
+     return data->last_redir_number;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get placed call party name
+ * @param handle - call handle
+ * @return placed party name
+ */
+cc_string_t CCAPI_CallInfo_getPlacedCallPartyName(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getPlacedCallPartyName";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->plcd_name);
+     return data->plcd_name;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get placed call party number
+ * @param handle - call handle
+ * @return placed party number
+ */
+cc_string_t CCAPI_CallInfo_getPlacedCallPartyNumber(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getPlacedCallPartyNumber";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->plcd_number);
+     return data->plcd_number;
+  }
+
+  return strlib_empty();
+}
+
+
+/**
+ * get call instance number
+ * @param handle - call handle
+ * @return
+ */
+cc_int32_t CCAPI_CallInfo_getCallInstance(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getCallInstance";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->inst);
+     return data->inst;
+  }
+
+  return 0;
+}
+
+/**
+ * get call status prompt
+ * @param handle - call handle
+ * @return call status
+ */
+cc_string_t CCAPI_CallInfo_getStatus(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getStatus";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->status);
+     return data->status;
+  }
+
+  return strlib_empty();
+
+}
+
+/**
+ * get call security
+ * @param handle - call handle
+ * @return call security status
+ */
+cc_call_security_t CCAPI_CallInfo_getSecurity(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getSecurity";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->security);
+     return data->security;
+  }
+
+  return CC_SECURITY_NONE;
+}
+
+/**
+ *  * get Call Selection Status
+ *   * @param [in] handle - call info handle
+ *    * @return cc_boolean - TRUE => selected
+ *     */
+cc_boolean CCAPI_CallInfo_getSelectionStatus(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getSelectionStatus";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->isSelected);
+       return data->isSelected;
+   }
+
+   return FALSE;
+}
+
+/**
+ * get call policy
+ * @param handle - call handle
+ * @return call policy
+ */
+cc_call_policy_t CCAPI_CallInfo_getPolicy(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getPolicy";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->policy);
+     return data->policy;
+  }
+
+  return CC_POLICY_NONE;
+}
+
+/**
+ * get GCID
+ * @param handle - call handle
+ * @return GCID
+ */
+cc_string_t CCAPI_CallInfo_getGCID(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getGCID";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->gci);
+     return data->gci;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get ringer state.
+ * @param handle - call handle
+ * @return ringer state
+ */
+cc_boolean CCAPI_CallInfo_getRingerState(cc_callinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_CallInfo_getRingerState";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->ringer_start);
+     return data->ringer_start;
+  }
+
+  return FALSE;
+}
+
+/**
+ * get ringer mode
+ * @param handle - call handle
+ * @return ringer mode
+ */
+int CCAPI_CallInfo_getRingerMode(cc_callinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_CallInfo_getRingerMode";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->ringer_mode);
+     return (int)(data->ringer_mode);
+  }
+
+  return -1;
+}
+
+/**
+ * get ringer loop count
+ * @param handle - call handle
+ * @return once Vs continuous
+ */
+cc_boolean CCAPI_CallInfo_getIsRingOnce(cc_callinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_CallInfo_getIsRingOnce";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->ringer_once);
+     return (int)(data->ringer_once);
+  }
+
+  return TRUE;
+}
+
+/**
+ * get onhook reason
+ * @param handle - call handle
+ * @return onhook reason
+ */
+cc_int32_t  CCAPI_CallInfo_getOnhookReason(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getOnhookReason";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->cause);
+     return data->cause;
+  }
+
+  return CC_CAUSE_NORMAL;
+}
+
+/**
+ * is Conference Call?
+ * @param handle - call handle
+ * @return boolean - is Conference
+ */
+cc_boolean  CCAPI_CallInfo_getIsConference(cc_callinfo_ref_t handle){
+  session_data_t *data = (session_data_t *)handle;
+  char isConf[32];
+
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, __FUNCTION__));
+
+  memset(isConf, 0, sizeof(isConf));
+
+  if(platGetPhraseText(CONFERENCE_LOCALE_CODE, isConf, sizeof(isConf)) == CC_FAILURE){
+         return FALSE;
+  }
+
+  if( data != NULL){
+      if( (strcasecmp(data->cld_name, isConf) == 0 && strcasecmp(data->cld_number, "") == 0) ||
+          (strcasecmp(data->clg_name, isConf) == 0 && strcasecmp(data->clg_number, "") == 0) )
+      {
+             return TRUE;
+      }
+  }
+
+  return FALSE;
+}
+
+/**
+ * getStream Statistics
+ * @param handle - call handle
+ * @return stream stats
+ */
+cc_return_t  CCAPI_CallInfo_getStreamStatistics(cc_callinfo_ref_t handle, cc_int32_t stats[], cc_int32_t *count)
+{
+  static const char *fname="CCAPI_CallInfo_getStreamStatistics";
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+  CCAPP_DEBUG(DEB_F_PREFIX"returned CC_SUCCESS (default)\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+ // todo
+ return CC_SUCCESS;
+}
+
+
+/**
+ * has capability - is the feature allowed
+ * @param handle - call handle
+ * @param feat_id - feature id
+ * @return boolean - is Allowed
+ */
+cc_boolean  CCAPI_CallInfo_hasCapability(cc_callinfo_ref_t handle, cc_int32_t feat_id){
+  static const char *fname="CCAPI_CallInfo_hasCapability";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"feature id:  %d , value returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),feat_id,  data->allowed_features[feat_id]);
+     return data->allowed_features[feat_id];
+  }
+
+  return FALSE;
+}
+
+/**
+ * get Allowed Feature set
+ * @param handle - call handle
+ * @return boolean array that can be indexed using CCAPI_CALL_CAP_XXXX to check if feature is enabled
+ */
+cc_boolean  CCAPI_CallInfo_getCapabilitySet(cc_callinfo_ref_t handle, cc_int32_t feat_set[]){
+  static const char *fname="CCAPI_CallInfo_getCapabilitySet";
+  session_data_t *data = (session_data_t *)handle;
+  int feat_id;
+
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     for (feat_id = 0; feat_id < CCAPI_CALL_CAP_MAX; feat_id++) {
+         feat_set[feat_id] = data->allowed_features[feat_id];
+         CCAPP_DEBUG(DEB_F_PREFIX"feature id:  %d , value %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),feat_id,  feat_set[feat_id]);
+     }
+
+     CCAPP_DEBUG(DEB_F_PREFIX"returned CC_SUCCESS\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+     return CC_SUCCESS;
+  }
+
+  return CC_FAILURE;
+}
+
+/**
+ * Call selection status
+ * @param [in] handle - call handle
+ * @return cc_boolean - selection status
+ */
+cc_boolean  CCAPI_CallInfo_isCallSelected(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_isCallSelected";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->isSelected);
+     return data->isSelected;
+  }
+
+  return FALSE;
+}
+
+/**
+ * Call negotiated video direction
+ * @param [in] handle - call handle
+ * @return cc_sdp_direction_t - video direction
+ */
+cc_sdp_direction_t  CCAPI_CallInfo_getVideoDirection(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getVideoDirection";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->vid_dir);
+     return (data->vid_dir);
+  }
+
+  return CC_SDP_DIRECTION_INACTIVE;
+}
+
+/**
+ * INFO Package for RECEIVED_INFO event
+ * @param [in] handle - call info handle
+ * @return cc_string_t - Info package header
+ */
+cc_string_t  CCAPI_CallInfo_getINFOPack (cc_callinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_CallInfo_getINFOPackage";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->info_package);
+     return data->info_package;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * INFO type for RECEIVED_INFO event
+ * @param [in] handle - call info handle
+ * @return cc_string_t - content-type  header
+ */
+cc_string_t  CCAPI_CallInfo_getINFOType (cc_callinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_CallInfo_getINFOType";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->info_type);
+     return data->info_type;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * INFO body for RECEIVED_INFO event
+ * @param [in] handle - call info handle
+ * @return cc_string_t - INFO body
+ */
+cc_string_t  CCAPI_CallInfo_getINFOBody (cc_callinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_CallInfo_getINFOBody";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->info_body);
+     return data->info_body;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * Get the call log reference
+ * @param [in] handle - call info handle
+ * @return cc_string_t - INFO body
+ * NOTE: Memory associated with the call log is tied to the cc_callinfo_ref_t handle
+ * this would be freed when the callinfo ref is freed.
+ */
+cc_calllog_ref_t  CCAPI_CallInfo_getCallLogRef(cc_callinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_CallInfo_getCallLogRef";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %x\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), &data->call_log);
+     return &data->call_log;
+  }
+
+  return NULL;
+}
+
+
+/**
+ * Returns the Audio mute state for this call
+ * @return boolean true=muted false=not muted
+ */
+cc_boolean CCAPI_CallInfo_isAudioMuted (cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_isAudioMuted";
+  session_data_t *data = (session_data_t *)handle;
+  session_data_t * sess_data_p;
+
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+  if ( data != NULL){
+      sess_data_p = (session_data_t *)findhash(data->sess_id);
+      if ( sess_data_p != NULL ) {
+          CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sess_data_p->audio_mute);
+          return sess_data_p->audio_mute;
+      }
+  }
+
+  return FALSE;
+}
+
+/**
+ * Returns the Video  mute state for this call
+ * @return boolean true=muted false=not muted
+ */
+cc_boolean CCAPI_CallInfo_isVideoMuted (cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_isVideoMuted";
+  session_data_t *data = (session_data_t *)handle;
+  session_data_t * sess_data_p;
+
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+  if ( data != NULL){
+      sess_data_p = (session_data_t *)findhash(data->sess_id);
+      if ( sess_data_p != NULL ) {
+          CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sess_data_p->video_mute);
+          return sess_data_p->video_mute;
+      }
+  }
+
+  return FALSE;
+}
+
+/**
+ * get SDP for CreateOffer\Create answer success callback
+ * @param handle - call handle
+ * @return sdp
+ */
+cc_string_t CCAPI_CallInfo_getSDP(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getSDP";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if (data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->sdp);
+     return data->sdp;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * get status code from internal JSEP functions
+ * @param handle - call handle
+ * @return status code
+ */
+cc_int32_t  CCAPI_CallInfo_getStatusCode(cc_callinfo_ref_t handle){
+  static const char *fname="CCAPI_CallInfo_getStatusCode";
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( data != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->cause);
+     return data->cause;
+  }
+
+  return CC_CAUSE_NORMAL;
+}
+
+
+/**
+ * get media stream table
+ * @param handle - call handle
+ * @return status MediaStreamTable
+ */
+MediaStreamTable*  CCAPI_CallInfo_getMediaStreams(cc_callinfo_ref_t handle) {
+  static const char *fname="CCAPI_CallInfo_getMediaStreams";
+  session_data_t *data = (session_data_t *)handle;
+  MediaTrack track;
+  MediaStreamTable* table = cpr_malloc(sizeof(MediaStreamTable));
+  if (!table)
+    return NULL;
+
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if (data != NULL) {
+    table->media_stream_id = data->media_stream_id;
+       table->num_tracks = 1;   /* this will change when we have multiple tracks per stream */
+       track.media_stream_track_id = data->media_stream_track_id;
+       track.video = FALSE;
+       table->track[0] = track;
+
+       /*
+        * Partly implemented multi-track handling
+          cc_table = data->media_tracks;
+          table->stream_id = (unsigned int)cc_table->stream_id;
+          table->num_tracks = (unsigned int)cc_table->num_tracks;
+          track.track_id = cc_table->track[0].ref_id;
+          table->track[0] = track;
+       */
+    return table;
+  }
+
+  return table;
+}
diff --git a/libs/sipcc/core/ccapp/ccapi_config.c b/libs/sipcc/core/ccapp/ccapi_config.c
new file mode 100644 (file)
index 0000000..e542901
--- /dev/null
@@ -0,0 +1,108 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "phone_debug.h"
+#include "CCProvider.h"
+#include "sessionConstants.h"
+#include "prot_configmgr.h"
+#include "cc_types.h"
+#include "config_parser.h"
+#include "config_api.h"
+#include "ccapi_snapshot.h"
+#include "ccapi_device.h"
+#include "ccapi_device_info.h"
+#include "cc_device_manager.h"
+#include "ccapi_service.h"
+#include "util_string.h"
+
+extern boolean apply_config;
+extern cc_apply_config_result_t apply_config_result;
+cc_boolean parse_setup_properties (int device_handle, const char *device_name, const char *sipUser, const char *sipPassword, const char *sipDomain);
+
+/**
+ *
+ * @return
+ */
+
+void CCAPI_Start_response(int device_handle, const char *device_name, const char *sipUser, const char *sipPassword, const char *sipDomain) {
+    static const char fname[] = "CCAPI_Start_response";
+
+    if (is_empty_str((char*)sipUser) || is_empty_str((char*)sipDomain)) {
+        CCAPP_ERROR(DEB_F_PREFIX" invalid registration details user=%x, domain=%x\n", DEB_F_PREFIX_ARGS(CC_API, fname), sipUser, sipDomain);
+        return;
+    }
+
+    g_dev_hdl = device_handle;
+    sstrncpy(g_dev_name, device_name, sizeof(g_dev_name));
+
+    if (is_phone_registered() == FALSE) {
+
+        if (parse_setup_properties(device_handle, device_name, sipUser, sipPassword, sipDomain)) {
+            registration_processEvent(EV_CC_CONFIG_RECEIVED);
+        }
+        return;
+    }
+
+ }
+
+/*  New Function
+    Register without using config file downloaded from cucm
+ */
+cc_boolean parse_setup_properties (int device_handle, const char *device_name, const char *sipUser, const char *sipPassword, const char *sipDomain) {
+    CC_Config_setStringValue(CFGID_DEVICE_NAME, device_name);
+
+    config_setup_main(sipUser, sipPassword, sipDomain);
+
+    ccsnap_device_init();
+    ccsnap_line_init();
+    ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_CONFIG_CHANGED, CC_DEVICE_ID);
+    return TRUE;
+}
+
+cc_boolean CCAPI_Config_set_server_address(const char *ip_address) {
+       config_setup_server_address(ip_address);
+       return TRUE;
+}
+
+cc_boolean CCAPI_Config_set_transport_udp(const cc_boolean is_udp) {
+       config_setup_transport_udp(is_udp);
+       return TRUE;
+}
+
+cc_boolean CCAPI_Config_set_local_voip_port(const int port) {
+       config_setup_local_voip_control_port(port);
+       return TRUE;
+}
+
+cc_boolean CCAPI_Config_set_remote_voip_port(const int port) {
+       config_setup_remote_voip_control_port(port);
+       return TRUE;
+}
+
+int CCAPI_Config_get_local_voip_port() {
+       return config_get_local_voip_control_port();
+}
+
+int CCAPI_Config_get_remote_voip_port() {
+       return config_get_remote_voip_control_port();
+}
+
+const char* CCAPI_Config_get_version() {
+       return config_get_version();
+}
+
+cc_boolean CCAPI_Config_set_p2p_mode(const cc_boolean is_p2p) {
+       config_setup_p2p_mode(is_p2p);
+       return TRUE;
+}
+
+cc_boolean CCAPI_Config_set_sdp_mode(const cc_boolean is_sdp) {
+       config_setup_sdp_mode(is_sdp);
+       return TRUE;
+}
+
+cc_boolean CCAPI_Config_set_avp_mode(const cc_boolean is_rtpsavpf) {
+       config_setup_avp_mode(is_rtpsavpf);
+       return TRUE;
+}
diff --git a/libs/sipcc/core/ccapp/ccapi_device.c b/libs/sipcc/core/ccapp/ccapi_device.c
new file mode 100644 (file)
index 0000000..79a9194
--- /dev/null
@@ -0,0 +1,290 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdlib.h"
+#include "string_lib.h"
+#include "ccapi_snapshot.h"
+#include "ccapi_device.h"
+#include "CCProvider.h"
+#include "cc_config.h"
+#include "cc_call_feature.h"
+#include "cc_device_feature.h"
+#include "ccsip_messaging.h"
+#include "ccapi_call_info.h"
+#include "cc_device_manager.h"
+#include "cc_service_listener.h"
+#include "platform_api.h"
+#include "util_string.h"
+#include "ccapi_service.h"
+#include "ccapi_device_info.h"
+
+char g_new_signaling_ip[MAX_IPADDR_STR_LEN];
+
+dock_undock_event_t  g_dock_undock_event = MEDIA_INTERFACE_UPDATE_NOT_REQUIRED;
+extern accessory_cfg_info_t g_accessoryCfgInfo;
+
+extern void escalateDeescalate();
+
+int signaling_interface_type;
+
+/**
+ * Get device reference handle
+ * @return cc_deviceinfo_ref_t - reference handle of the device
+ */
+cc_device_handle_t CCAPI_Device_getDeviceID()
+{
+   return CC_DEVICE_ID;
+}
+
+/**
+ * Get device reference handle
+ * @param handle - device handle
+ * @return cc_deviceinfo_ref_t - reference handle of the device
+ */
+cc_deviceinfo_ref_t CCAPI_Device_getDeviceInfo(cc_device_handle_t handle)
+{
+  cc_device_info_t *device_info = (cc_device_info_t*)cpr_malloc(sizeof(cc_device_info_t));
+
+  if (device_info) {
+     *device_info = g_deviceInfo;
+      device_info->name  = strlib_copy(g_deviceInfo.name);
+      if (device_info->name == NULL) {
+          device_info->name  = strlib_empty();
+      }
+      device_info->not_prompt  = strlib_copy(g_deviceInfo.not_prompt);
+      if (device_info->not_prompt == NULL) {
+          device_info->not_prompt = strlib_empty();
+      }
+      device_info->ref_count = 1;
+  }
+  return device_info;
+}
+
+/**
+ * Retain the deviceInfo snapshot
+ * @param handle - device handle
+ * @param cc_deviceinfo_ref_t - refrence to the block to be retained
+ * @return void
+ */
+void CCAPI_Device_retainDeviceInfo(cc_deviceinfo_ref_t ref){
+    cc_device_info_t *device_info = ref;
+    if (device_info) {
+        device_info->ref_count++;
+    }
+}
+
+/**
+ * Set device configuration file location
+ * @param [in] ref - refrence to the block to be freed
+ * @param [in] file_path - device config file full path
+ * @return void
+ */
+void CCAPI_Device_configUpdate(cc_device_handle_t handle, file_path_t file_path) {
+    CC_Config_setStringValue(CFGID_CONFIG_FILE, file_path);
+}
+
+/**
+ * Release the deviceInfo snapshot
+ * @param handle - device handle
+ * @param cc_deviceinfo_ref_t - refrence to the block to be released
+ * @return void
+ */
+void CCAPI_Device_releaseDeviceInfo(cc_deviceinfo_ref_t ref){
+    cc_device_info_t *device_info = ref;
+
+    if (device_info) {
+       device_info->ref_count--;
+       if ( device_info->ref_count == 0 ) {
+            strlib_free(device_info->name);
+            strlib_free(device_info->not_prompt);
+            cpr_free(device_info);
+        }
+    }
+}
+
+
+/**
+ * Create a call on the device
+ * @param handle - device handle
+ * @return cc_call_handle_t - handle of the call created
+ */
+cc_call_handle_t CCAPI_Device_CreateCall(cc_device_handle_t handle)
+{
+  return CC_createCall(0);
+}
+
+/**
+ * Enable or disable video capability of the device.
+ * @param handle - device handle
+ * @param enable - a flag to indicate that application wants to enable of
+ * disable video capability of the device.
+ * @return void
+ */
+void CCAPI_Device_enableVideo(cc_device_handle_t handle, cc_boolean enable)
+{
+   CC_DeviceFeature_enableVideo(enable);
+   g_accessoryCfgInfo.video = ACCSRY_CFGD_APK;
+}
+
+/**
+ * Enable or disable camera capability of the device.
+ * @param handle - device handle
+ * @param enable - a flag to indicate that application wants to enable of
+ * disable camera capability of the device.
+ * @return void
+ */
+void CCAPI_Device_enableCamera(cc_device_handle_t handle, cc_boolean enable)
+{
+   CC_DeviceFeature_enableCamera(enable);
+   g_accessoryCfgInfo.camera = ACCSRY_CFGD_APK;
+}
+
+/**
+ * CCAPI_Device_setDigestNamePasswd
+ *
+ * @param handle - device handle
+ * @param name - The Digest auth name
+ * @param passwd - The password for that name for the line
+ * @return void
+ */
+void CCAPI_Device_setDigestNamePasswd (cc_device_handle_t handle,
+                                       char *name, char *pw)
+{
+    int line;
+
+    for(line = 0; line < MAX_CONFIG_LINES; line++) {
+        CC_Config_setStringValue(CFGID_LINE_AUTHNAME + line, name);
+        CC_Config_setStringValue(CFGID_LINE_PASSWORD + line, pw);
+    }
+}
+
+/**
+ * CCAPI_Device_IP_Update
+ *
+ * There is a change in the IP address and the values of new set
+ * of signaling and media IP addresses are provided.
+ * These value are compared with the current IP address values
+ * and depending on what changed, restart and/or re-invite
+ * action is taken.
+ *
+ * The case being addressed.
+ * 1) If the signaling IP change  happens during a call,
+ *    the change is deferred till phone is idle.
+ * 2)If media IP change happens during a call, it is applied immediately.
+ * 3) If both change, and call is active, that is treated same
+ *    combination of case 1) and 2).
+ * 4) If no call is present, and signaling IP change,
+ *    sipcc will re-register with new IP.
+ *
+ * @param handle - device handle
+ * @param signaling_ip - IP address that Must be used for signalling
+ * @param signaling_interface - Interface Name associaed with signaling IP
+ * @param signaling_int_type - Interface type associaed with signaling IP
+ * @param media_ip - IP address that Must be used for media
+ * @param media_interface - Interface nmae associaed with media IP
+ * @param media_interface - Interface Type associaed with media IP
+ * @return void
+ */
+void CCAPI_Device_IP_Update (cc_device_handle_t handle,
+                              const char *signaling_ip,
+                              const char *signaling_interface,
+                              int signaling_int_type,
+                              const char *media_ip,
+                              const char *media_interface,
+                              int media_int_type)
+{
+    static const char fname[] = "CCAPI_Device_IP_Update";
+    char curr_signaling_ip[MAX_IPADDR_STR_LEN];
+    char curr_media_ip[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t      sig_ip;
+
+    signaling_interface_type = signaling_int_type;
+
+    // init the ip addr string to empty string
+    init_empty_str(curr_signaling_ip);
+    init_empty_str(curr_media_ip);
+    init_empty_str(g_new_signaling_ip);
+
+    config_get_value(CFGID_MY_IP_ADDR, &sig_ip, sizeof(cpr_ip_addr_t));
+    sig_ip.type = CPR_IP_ADDR_IPV4;
+    util_ntohl(&sig_ip, &sig_ip);
+    ipaddr2dotted(curr_signaling_ip, &sig_ip);
+
+    config_get_string(CFGID_MEDIA_IP_ADDR, curr_media_ip,
+                        MAX_IPADDR_STR_LEN);
+
+    DEF_DEBUG(DEB_F_PREFIX"New sig_ip=%s media_ip=%s  Current: sig_ip: %s,"\
+            "media_ip: %s \n",
+            DEB_F_PREFIX_ARGS(CC_API, fname),
+            signaling_ip,
+            media_ip,
+            curr_signaling_ip,
+            curr_media_ip);
+
+    /*
+     * If signaling and media IP are empty, stop the
+     * SIP service and return;
+     */
+    if ((is_empty_str((char *)signaling_ip) ||
+        (strncmp(signaling_ip, "0.0.0.0", MAX_IPADDR_STR_LEN) == 0))
+        && (is_empty_str((char *)media_ip) ||
+        (strncmp(media_ip, "0.0.0.0", MAX_IPADDR_STR_LEN) == 0))) {
+        CC_Config_setStringValue(CFGID_MY_IP_ADDR, "0.0.0.0");
+        CC_Config_setStringValue(CFGID_MEDIA_IP_ADDR, EMPTY_STR);
+        DEF_DEBUG(DEB_F_PREFIX"Media and Signaling IP Not provided."\
+                  "Shutdown sip stack", DEB_F_PREFIX_ARGS(CC_API, fname));
+        if ((strncmp(curr_signaling_ip, signaling_ip,
+              MAX_IPADDR_STR_LEN) != 0)) {
+            registration_processEvent(EV_CC_IP_INVALID);
+            return;
+        }
+    }
+
+    /*
+     * There is a change in the signaling IP, set the
+     * new IP as the platform signaling IP and re-register
+     */
+    if ((signaling_ip != NULL)  &&
+        (strncmp(curr_signaling_ip, signaling_ip, MAX_IPADDR_STR_LEN) != 0)) {
+        CC_Config_setStringValue(CFGID_MY_IP_ADDR, signaling_ip);
+        DEF_DEBUG(DEB_F_PREFIX"Signaling IP changed. Re-register, if needed.",
+                  DEB_F_PREFIX_ARGS(CC_API, fname));
+        registration_processEvent(EV_CC_IP_VALID);
+
+    }
+
+    /*
+     * There is a change in the media IP, set the
+     * new IP as the platform media IP and post the call to GSM
+     * to initiate re-inivite for all relevane calls
+     */
+    if ((media_ip != NULL)  &&
+        (strncmp(curr_media_ip, media_ip, MAX_IPADDR_STR_LEN) != 0)) {
+        CC_Config_setStringValue(CFGID_MEDIA_IP_ADDR, media_ip);
+        if (g_dock_undock_event != MEDIA_INTERFACE_UPDATE_IN_PROCESS) {
+            g_dock_undock_event = MEDIA_INTERFACE_UPDATE_STARTED;
+            DEF_DEBUG(DEB_F_PREFIX" MEDIA_INTERFACE_UPDATE received. escalateDeescalate.",
+                      DEB_F_PREFIX_ARGS(CC_API, fname));
+            escalateDeescalate();
+        }else {
+            DEF_DEBUG(DEB_F_PREFIX"MEDIA_INTERFACE_UPDATE received but escalateDeescalate already in progress:%d",
+                  DEB_F_PREFIX_ARGS(CC_API, fname), g_dock_undock_event);
+        }
+    }
+}
+
+/**
+ * CCAPI_Device_setVideoAutoTxPreference
+ *
+ * @param handle - device handle
+ * @param txPref - TRUE=> auto Tx Video prefered
+ * @return void
+ */
+void CCAPI_Device_setVideoAutoTxPreference (cc_device_handle_t handle, cc_boolean txPref)
+{
+        CCAPP_DEBUG("CCAPI_Device_setVideoAutoTxPreference: updated to %d\n", txPref);
+       cc_media_setVideoAutoTxPref(txPref);
+}
+
+
diff --git a/libs/sipcc/core/ccapp/ccapi_device_info.c b/libs/sipcc/core/ccapp/ccapi_device_info.c
new file mode 100644 (file)
index 0000000..c667191
--- /dev/null
@@ -0,0 +1,475 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "string_lib.h"
+#include "cc_constants.h"
+#include "ccapi_snapshot.h"
+#include "ccapi_device_info.h"
+#include "sessionHash.h"
+#include "CCProvider.h"
+#include "phone_debug.h"
+#include "ccapi_snapshot.h"
+#include "ccapi_device_info.h"
+#include "util_string.h"
+
+
+/**
+ * gets the device name
+ * @returns - a pointer to the device name
+ */
+cc_deviceinfo_ref_t CCAPI_DeviceInfo_getDeviceHandle ()
+{
+   static const char *fname="CCAPI_DeviceInfo_getDeviceHandle";
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+   CCAPP_DEBUG(DEB_F_PREFIX"returned 0 (default)\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+   return 0;
+}
+
+/**
+ * gets the device name
+ * @returns - a pointer to the device name
+ */
+cc_string_t CCAPI_DeviceInfo_getDeviceName (cc_deviceinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_DeviceInfo_getDeviceName";
+  cc_device_info_t *device = handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( device != NULL ) {
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device->name);
+     return device->name;
+  }
+
+  return strlib_empty();
+}
+
+/**
+ * gets the device idle status
+ * @param [in] handle - reference to device info
+ * @returns boolean - idle status
+ */
+cc_boolean CCAPI_DeviceInfo_isPhoneIdle(cc_deviceinfo_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_isPhoneIdle";
+    boolean ret = TRUE;
+    hashItr_t itr;
+    session_data_t * session_data;
+    cc_call_state_t call_state;
+
+    hashItrInit(&itr);
+
+    while ((session_data = hashItrNext(&itr)) != NULL) {
+        call_state = session_data->state;
+        if (call_state != ONHOOK &&
+            call_state != REMINUSE) {
+            ret = FALSE;
+            break;
+        }
+    }
+    CCAPP_DEBUG(DEB_F_PREFIX"idle state=%d session_id=0x%x call-state=%d handle=%x\n",
+        DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), ret,
+        (session_data != NULL)? session_data->sess_id: 0,
+        (session_data != NULL)? session_data->state: 0,
+        (handle)? handle:0);
+    return ret;
+
+}
+
+/**
+ * gets the service state
+ * @param [in] handle - reference to device info
+ * @returns cc_service_state_t - INS/OOS
+ */
+cc_service_state_t CCAPI_DeviceInfo_getServiceState (cc_deviceinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_DeviceInfo_getServiceState";
+  cc_device_info_t *device = handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( device != NULL ) {
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device->ins_state);
+     return device->ins_state;
+  }
+
+  return CC_STATE_IDLE;
+}
+
+/**
+ * gets the service cause
+ * @param [in] handle - reference to device info
+ * @returns cc_service_cause_t - reason for service state
+ */
+cc_service_cause_t CCAPI_DeviceInfo_getServiceCause (cc_deviceinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_DeviceInfo_getServiceCause";
+  cc_device_info_t *device = handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( device != NULL ) {
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device->ins_cause);
+     return device->ins_cause;
+  }
+
+  return CC_CAUSE_NONE;
+
+}
+
+/**
+ * gets the cucm mode
+ * @returns cc_cucm_mode_t - CUCM mode
+ */
+cc_cucm_mode_t CCAPI_DeviceInfo_getCUCMMode (cc_deviceinfo_ref_t handle)
+{
+  static const char *fname="CCAPI_DeviceInfo_getCUCMMode";
+  cc_device_info_t *device = handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+  if ( device != NULL ) {
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device->cucm_mode);
+     return device->cucm_mode;
+  }
+
+  return CC_MODE_INVALID;
+}
+
+/**
+ * gets list of handles to calls on the device
+ * @param handle - device handle
+ * @param handles - array of call handle to be returned
+ * @param count[in/out] number allocated in array/elements returned
+ * @returns
+ */
+void CCAPI_DeviceInfo_getCalls (cc_deviceinfo_ref_t handle, cc_call_handle_t handles[], cc_uint16_t *count)
+{
+    static const char *fname="CCAPI_DeviceInfo_getCalls";
+    hashItr_t itr;
+    session_data_t *data;
+    int i=0;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    hashItrInit(&itr);
+    while ( (data = (session_data_t*)hashItrNext(&itr)) != NULL &&
+              i<*count ) {
+        handles[i++] = CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id);
+    }
+    *count=i;
+    CCAPP_DEBUG(DEB_F_PREFIX"Finished (no return) \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+}
+
+/**
+ * gets list of handles to calls on the device by State
+ * @param handle - device handle
+ * @param state - state for whcih calls are requested
+ * @param handles - array of call handle to be returned
+ * @param count[in/out] number allocated in array/elements returned
+ * @returns
+ */
+void CCAPI_DeviceInfo_getCallsByState (cc_deviceinfo_ref_t handle, cc_call_state_t state,
+                  cc_call_handle_t handles[], cc_uint16_t *count)
+{
+    static const char *fname="CCAPI_DeviceInfo_getCallsByState";
+    hashItr_t itr;
+    session_data_t *data;
+    int i=0;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    hashItrInit(&itr);
+    while ( (data = (session_data_t*)hashItrNext(&itr)) != NULL &&
+              i<*count ) {
+        if ( data->state == state ) {
+            handles[i++] = CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id);
+        }
+    }
+    *count=i;
+    CCAPP_DEBUG(DEB_F_PREFIX"Finished (no return) \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+}
+
+/**
+ * gets list of handles to lines on the device
+ * @param handles[in,out] - array of line handle to be returned
+ * @param count[in/out] number allocated in array/elements returned
+ * @returns
+ */
+void CCAPI_DeviceInfo_getLines (cc_deviceinfo_ref_t handle, cc_lineid_t handles[], cc_uint16_t *count)
+{
+    static const char *fname="CCAPI_DeviceInfo_getLines";
+    cc_line_info_t *line;
+    int i=1, j=0;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    CCAPP_DEBUG(" LINES Start ");
+
+    while ( (line = ccsnap_getLineInfo(i++)) != NULL &&
+              j<*count ) {
+       CCAPP_DEBUG(" LINE  handle[%d]=%d", j, line->button );
+        /* We will use button as line handles */
+        handles[j++] = line->button;
+    }
+    *count=j;
+    CCAPP_DEBUG(DEB_F_PREFIX"Finished (no return) \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+}
+
+/**
+ * gets list of handles to features on the device
+ * @param handles[in,out] - array of feature handle to be returned
+ * @param count[in/out] number allocated in array/elements returned
+ * @returns
+ */
+void CCAPI_DeviceInfo_getFeatures (cc_deviceinfo_ref_t handle, cc_featureinfo_ref_t handles[], cc_uint16_t *count)
+{
+    static const char *fname="CCAPI_DeviceInfo_getFeatures";
+    cc_featureinfo_ref_t feature;
+    int i=0, j=0;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    for (i=1;i<=MAX_CONFIG_LINES && j<*count;i++) {
+       feature = (cc_featureinfo_ref_t) ccsnap_getFeatureInfo(i);
+       if(feature != NULL){
+                       handles[j++] = feature;
+       }
+    }
+    *count=j;
+    CCAPP_DEBUG(DEB_F_PREFIX"Finished (no return) \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+}
+
+/**
+ * gets handles of call agent servers
+ * @param handles - array of handles to call agent servers
+ * @param count[in/out] number allocated in array/elements returned
+ * @returns
+ */
+void CCAPI_DeviceInfo_getCallServers (cc_deviceinfo_ref_t handle, cc_callserver_ref_t handles[], cc_uint16_t *count)
+{
+    static const char *fname="CCAPI_DeviceInfo_getCallServers";
+    int i, j=0;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    for (i=0;i<CCAPI_MAX_SERVERS && i< *count; i++) {
+        if (g_deviceInfo.ucm[i].name != 0 && strlen(g_deviceInfo.ucm[i].name)) {
+           handles[j++] = &g_deviceInfo.ucm[i];
+       }
+    }
+    *count = j;
+    CCAPP_DEBUG(DEB_F_PREFIX"Finished (no return) \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+}
+
+/**
+ * gets call server name
+ * @param handle - handle of call server
+ * @returns name of the call server
+ * NOTE: The memory for the string will be freed once the device info reference is freed. No need to free this memory explicitly.
+ */
+cc_string_t CCAPI_DeviceInfo_getCallServerName (cc_callserver_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_getCallServerName";
+    cc_call_server_t *ref = (cc_call_server_t *) handle;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    if (ref != NULL && ref->name != 0) {
+        CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), ref->name);
+           return ref->name;
+    }
+    return strlib_empty();
+}
+
+/**
+ * gets call server mode
+ * @param handle - handle of call server
+ * @returns - mode of the call server
+ */
+cc_cucm_mode_t CCAPI_DeviceInfo_getCallServerMode (cc_callserver_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_getCallServerMode";
+    cc_call_server_t *ref = (cc_call_server_t *) handle;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    if (ref != NULL) {
+        CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), ref->type);
+        return ref->type;
+    }
+
+    return CC_MODE_INVALID;
+}
+
+/**
+ * gets calls erver name
+ * @param handle - handle of call server
+ * @returns status of the call server
+ */
+cc_ccm_status_t CCAPI_DeviceInfo_getCallServerStatus (cc_callserver_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_getCallServerStatus";
+    cc_call_server_t *ref = (cc_call_server_t *) handle;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    if (ref != NULL) {
+        CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), (ref->status));
+        return ref->status;
+    }
+
+    return CC_CCM_STATUS_NONE;
+}
+
+/**
+ * get the NOTIFICATION PROMPT
+ * @param [in] handle - reference to device info
+ * @returns
+ */
+cc_string_t CCAPI_DeviceInfo_getNotifyPrompt (cc_deviceinfo_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_getNotifyPrompt";
+    cc_device_info_t *ref = (cc_device_info_t *) handle;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    if (ref != NULL) {
+        CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), (ref->not_prompt));
+        return ref->not_prompt;
+    }
+
+    return strlib_empty();
+}
+
+/**
+ * get the NOTIFICATION PROMPT PRIORITY
+ * @param [in] handle - reference to device info
+ * @returns
+ */
+cc_uint32_t CCAPI_DeviceInfo_getNotifyPromptPriority (cc_deviceinfo_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_getNotifyPromptPriority";
+    cc_device_info_t *ref = (cc_device_info_t *) handle;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    if (ref != NULL) {
+        CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), (ref->not_prompt_prio));
+        return ref->not_prompt_prio;
+    }
+
+    return 0;
+}
+
+/**
+ * get the NOTIFICATION PROMPT PROGRESS
+ * @param [in] handle - reference to device info
+ * @returns
+ */
+cc_uint32_t CCAPI_DeviceInfo_getNotifyPromptProgress (cc_deviceinfo_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_getNotifyPromptProgress";
+    cc_device_info_t *ref = (cc_device_info_t *) handle;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    if (ref != NULL) {
+        CCAPP_DEBUG(DEB_F_PREFIX"returned %02X\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), (ref->not_prompt_prog));
+        return ref->not_prompt_prog;
+    }
+
+    return 0;
+}
+
+/**
+ * gets provisioing for missed call logging
+ * @param [in] handle - reference to device info
+ * @returns boolean - false => disabled true => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isMissedCallLoggingEnabled (cc_deviceinfo_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_isMissedCallLoggingEnabled";
+
+    CCAPP_DEBUG(DEB_F_PREFIX" return val %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), ccsnap_isMissedCallLoggingEnabled());
+    return ccsnap_isMissedCallLoggingEnabled();
+}
+
+/**
+ * gets provisioing for placed call logging
+ * @param [in] handle - reference to device info
+ * @returns boolean - false => disabled true => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isPlacedCallLoggingEnabled (cc_deviceinfo_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_isPlacedCallLoggingEnabled";
+
+    CCAPP_DEBUG(DEB_F_PREFIX" return val %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), ccsnap_isPlacedCallLoggingEnabled());
+    return ccsnap_isPlacedCallLoggingEnabled();
+}
+
+
+/**
+ * gets provisioing for received call logging
+ * @param [in] handle - reference to device info
+ * @returns boolean - false => disabled true => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isReceivedCallLoggingEnabled (cc_deviceinfo_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_isReceivedCallLoggingEnabled";
+
+    CCAPP_DEBUG(DEB_F_PREFIX" return val %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), ccsnap_isReceivedCallLoggingEnabled());
+    return ccsnap_isReceivedCallLoggingEnabled();
+}
+
+/**
+ * gets time registration completed successfully
+ * @param [in] handle - reference to device info
+ * @returns long - the time registration completed successfully
+ */
+long long CCAPI_DeviceInfo_getRegTime (cc_deviceinfo_ref_t handle)
+{
+    return (g_deviceInfo.reg_time);
+}
+
+/**
+ * Returns dot notation IP address phone used for registration purpose. If phone is not
+ * registered, then "0.0.0.0" is returned.
+ * @return  char IP address used to register phone.
+ */
+cc_string_t CCAPI_DeviceInfo_getSignalingIPAddress(cc_deviceinfo_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_getSignalingIPAddress";
+    cpr_ip_addr_t ip_addr = {0,{0}};
+
+    sip_config_get_net_device_ipaddr(&ip_addr);
+    ipaddr2dotted(g_deviceInfo.registration_ip_addr, &ip_addr);
+    CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), g_deviceInfo.registration_ip_addr);
+    return g_deviceInfo.registration_ip_addr;
+}
+
+/**
+ * Returns camera admin enable/disable status
+ * @param [in] handle - reference to device info
+ * @return  cc_boolean  - TRUE => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isCameraEnabled(cc_deviceinfo_ref_t handle) {
+   // returns the current status not the snapshot status
+   return cc_media_isTxCapEnabled();
+}
+
+/**
+ * Returns Video Capability admin enable/disable status
+ * @param [in] handle - reference to device info
+ * @return  cc_boolean  - TRUE => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isVideoCapEnabled(cc_deviceinfo_ref_t handle) {
+          // returns the current status not the snapshot status
+          return cc_media_isVideoCapEnabled();
+}
+
+/**
+ * gets the device mwi_lamp state
+ * @param [in] handle - reference to device info
+ * @returns boolean - mwi_lamp state
+ */
+cc_boolean CCAPI_DeviceInfo_getMWILampState(cc_deviceinfo_ref_t handle)
+{
+    static const char *fname="CCAPI_DeviceInfo_getMWILampState";
+    cc_device_info_t *device = handle;
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    if ( device != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device->mwi_lamp);
+       return device->mwi_lamp;
+    }
+
+    return FALSE;
+}
+
diff --git a/libs/sipcc/core/ccapp/ccapi_feature_info.c b/libs/sipcc/core/ccapp/ccapi_feature_info.c
new file mode 100644 (file)
index 0000000..cc40589
--- /dev/null
@@ -0,0 +1,154 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ccapi_snapshot.h"
+#include "sessionHash.h"
+#include "CCProvider.h"
+#include "phone_debug.h"
+
+/**
+ * Get the physical button number on which this feature is configured
+ * @param feature - feature reference handle
+ * @return cc_int32_t - button assigned to the feature
+ */
+cc_int32_t CCAPI_featureInfo_getButton(cc_featureinfo_ref_t feature)
+{
+   static const char *fname="CCAPI_featureInfo_getButton";
+   cc_feature_info_t  *info;
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   info = (cc_feature_info_t *) feature;
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->button);
+       return info->button;
+   }
+   return -1;
+}
+
+/**
+ * Get the featureID
+ * @param feature - feature reference handle
+ * @return cc_int32_t - type of to the feature
+ */
+cc_int32_t CCAPI_featureInfo_getFeatureID(cc_featureinfo_ref_t feature)
+{
+   static const char *fname="CCAPI_featureInfo_getFeatureID";
+   cc_feature_info_t  *info;
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   info = (cc_feature_info_t *) feature;
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->feature_id);
+       return info->feature_id;
+   }
+   return -1;
+}
+
+/**
+ * Get the feature Label Name
+ * @param feature - feature reference handle
+ * @return cc_string_t - name of the feature created
+ */
+cc_string_t CCAPI_featureInfo_getDisplayName(cc_featureinfo_ref_t feature) {
+   static const char *fname="CCAPI_featureInfo_getDisplayName";
+   cc_feature_info_t  *info;
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   info = (cc_feature_info_t *) feature;
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->name);
+       return ccsnap_get_line_label(info->button);
+   }
+   return NULL;
+}
+
+/**
+ * Get the speeddial Number
+ * @param feature - feature reference handle
+ * @return cc_string_t - speeddial number of the feature created
+ */
+cc_string_t CCAPI_featureInfo_getSpeedDialNumber(cc_featureinfo_ref_t feature) {
+   static const char *fname="CCAPI_featureInfo_getSpeedDialNumber";
+   cc_feature_info_t  *info;
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   info = (cc_feature_info_t *) feature;
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->speedDialNumber);
+       return info->speedDialNumber;
+   }
+   return NULL;
+}
+
+/**
+ * Get the contact
+ * @param feature - feature reference handle
+ * @return cc_string_t - contact of the feature created
+ */
+cc_string_t CCAPI_featureInfo_getContact(cc_featureinfo_ref_t feature) {
+   static const char *fname="CCAPI_featureInfo_getContact";
+   cc_feature_info_t  *info;
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   info = (cc_feature_info_t *) feature;
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->contact);
+       return info->contact;
+   }
+   return NULL;
+}
+
+/**
+ * Get the retrieval prefix
+ * @param feature - feature reference handle
+ * @return cc_string_t - retrieval prefix of the feature created
+ */
+cc_string_t CCAPI_featureInfo_getRetrievalPrefix(cc_featureinfo_ref_t feature) {
+   static const char *fname="CCAPI_featureInfo_getRetrievalPrefix";
+   cc_feature_info_t  *info;
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   info = (cc_feature_info_t *) feature;
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->retrievalPrefix);
+       return info->retrievalPrefix;
+   }
+   return NULL;
+}
+
+/**
+ * Get BLF state
+ * @param feature - feature reference handle
+ * @return cc_string_t - handle of the feature created
+ */
+cc_blf_state_t CCAPI_featureInfo_getBLFState(cc_featureinfo_ref_t feature) {
+   static const char *fname="CCAPI_featureInfo_getBLFState";
+   cc_feature_info_t  *info = (cc_feature_info_t  *)feature;
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->blf_state);
+       return info->blf_state;
+   }
+   return CC_SIP_BLF_UNKNOWN;
+}
+
+/**
+ * Get the feature option mask
+ * @param feature - feature reference handle
+ * @return cc_int32_t - feature option mask for the feature
+ */
+cc_int32_t CCAPI_featureInfo_getFeatureOptionMask(cc_featureinfo_ref_t feature)
+{
+   static const char *fname="CCAPI_featureInfo_getFeatureOptionMask";
+   cc_feature_info_t  *info;
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   info = (cc_feature_info_t *) feature;
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->featureOptionMask);
+       return info->featureOptionMask;
+   }
+   return -1;
+}
diff --git a/libs/sipcc/core/ccapp/ccapi_line.c b/libs/sipcc/core/ccapp/ccapi_line.c
new file mode 100644 (file)
index 0000000..0931e11
--- /dev/null
@@ -0,0 +1,81 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdlib.h"
+#include "string_lib.h"
+#include "cc_call_feature.h"
+#include "ccapi_snapshot.h"
+#include "ccapi_line.h"
+
+extern cc_line_info_t lineInfo[MAX_CONFIG_LINES+1];
+
+/**
+ * Get reference handle for the line
+ * @return cc_call_handle_t - handle of the call created
+ */
+cc_lineinfo_ref_t CCAPI_Line_getLineInfo(cc_uint32_t lineID)
+{
+  cc_line_info_t *line_info = NULL;
+  int i;
+
+  for (i=1;i<=MAX_CONFIG_LINES;i++) {
+      if ( (cc_uint32_t)lineInfo[i].button == lineID ) {
+          line_info = (cc_line_info_t*)cpr_malloc(sizeof(cc_line_info_t));
+
+          if (line_info) {
+              *line_info = lineInfo[i];
+              line_info->ref_count = 1;
+              line_info->name = strlib_copy(lineInfo[i].name);
+              line_info->dn = strlib_copy(lineInfo[i].dn);
+              line_info->cfwd_dest = strlib_copy(lineInfo[i].cfwd_dest);
+              line_info->externalNumber =
+                  strlib_copy(lineInfo[i].externalNumber);
+          }
+      }
+  }
+  return line_info;
+}
+
+/**
+ * Create a call on the line
+ * @param [in] line - lineID
+ * @return cc_call_handle_t - handle of the call created
+ */
+cc_call_handle_t CCAPI_Line_CreateCall(cc_lineid_t line)
+{
+ // do we need to check the line on which this gets created?
+  return CC_createCall(line);
+}
+
+/**
+ * Reatin the lineInfo snapshot
+ * @param cc_callinfo_ref_t - refrence to the block to be retained
+ * @return void
+ */
+void CCAPI_Line_retainLineInfo(cc_lineinfo_ref_t ref){
+    cc_line_info_t *line_info = ref;
+
+    line_info->ref_count++;
+}
+/**
+ * Free the lineInfo snapshot
+ * @param cc_callinfo_ref_t - refrence to the block to be freed
+ * @return void
+ */
+void CCAPI_Line_releaseLineInfo(cc_lineinfo_ref_t ref){
+    cc_line_info_t *line_info = ref;
+
+    if (line_info) {
+       line_info->ref_count--;
+       if ( line_info->ref_count == 0) {
+            strlib_free(line_info->name);
+            strlib_free(line_info->dn);
+            strlib_free(line_info->cfwd_dest);
+            strlib_free(line_info->externalNumber);
+            cpr_free(line_info);
+       }
+    }
+}
+
+
diff --git a/libs/sipcc/core/ccapp/ccapi_line_info.c b/libs/sipcc/core/ccapp/ccapi_line_info.c
new file mode 100644 (file)
index 0000000..dda2969
--- /dev/null
@@ -0,0 +1,425 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ccapi_snapshot.h"
+#include "ccapi_line.h"
+#include "sessionHash.h"
+#include "CCProvider.h"
+#include "phone_debug.h"
+
+/**
+ * Get the line ID
+ * @param line - line reference handle
+ * @return line ID
+ */
+cc_int32_t CCAPI_lineInfo_getID(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_getID";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   /* We will use button as line ID */
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->button);
+       return info->button;
+   }
+   return -1;
+}
+
+/**
+ * Get the line Name
+ * @param line - line reference handle
+ * @return cc_string_t - handle of the call created
+ */
+cc_string_t CCAPI_lineInfo_getName(cc_lineinfo_ref_t line) {
+   static const char *fname="CCAPI_lineInfo_getName";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->dn);
+       return info->dn;
+   }
+   return NULL;
+}
+
+/**
+ * Get the line Label
+ * @param [in] line - line reference handle
+ * @return cc_string_t - line Label
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_lineInfo_getLabel(cc_lineinfo_ref_t line) {
+   static const char *fname="CCAPI_lineInfo_getLabel";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+   cc_string_t label = strlib_empty();
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       label = ccsnap_get_line_label(info->button);
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), label);
+   }
+   return label;
+}
+
+/**
+ * Get the line DN Number
+ * @param line - line reference handle
+ * @return cc_string_t - handle of the call created
+ */
+cc_string_t CCAPI_lineInfo_getNumber(cc_lineinfo_ref_t line) {
+   static const char *fname="CCAPI_lineInfo_getNumber";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->name);
+       return info->name;
+   }
+   return NULL;
+}
+
+/**
+ * Get the line External Number
+ * @param line - line reference handle
+ * @return cc_string_t - handle of the call created
+ */
+cc_string_t CCAPI_lineInfo_getExternalNumber(cc_lineinfo_ref_t line) {
+   static const char *fname="CCAPI_lineInfo_getExternalNumber";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+   char externalNumberMask[MAX_EXTERNAL_NUMBER_MASK_SIZE];
+   memset(externalNumberMask, 0, sizeof(externalNumberMask));
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   config_get_string(CFGID_CCM_EXTERNAL_NUMBER_MASK, externalNumberMask, MAX_EXTERNAL_NUMBER_MASK_SIZE);
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->name);
+       if (strlen(externalNumberMask) > 0) {
+           CCAPP_DEBUG(DEB_F_PREFIX"number with mask applied == %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->externalNumber);
+           return info->externalNumber;
+       } else {
+           CCAPP_DEBUG(DEB_F_PREFIX"number without mask == %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->name);
+           return info->name;
+       }
+   }
+   return NULL;
+}
+
+/**
+ * Get the physical button number on which this line is configured
+ * @param line - line reference handle
+ * @return cc_uint32_t - button number
+ */
+cc_uint32_t CCAPI_lineInfo_getButton(cc_lineinfo_ref_t line){
+   static const char *fname="CCAPI_lineInfo_getButton";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->button);
+       return info->button;
+   }
+   return 0;
+
+}
+
+/**
+ * Get the Line Type
+ * @param [in] line - line reference handle
+ * @return cc_uint32_t - line featureID ( Line )
+ */
+cc_line_feature_t CCAPI_lineInfo_getLineType(cc_lineinfo_ref_t line){
+   static const char *fname="CCAPI_lineInfo_getLineType";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->line_type);
+       return info->line_type;
+   }
+   return 0;
+}
+
+
+
+/**
+ * Get the CFWDAll status for the line
+ * @param line - line reference handle
+ * @return cc_boolean - isForwarded
+ */
+cc_boolean CCAPI_lineInfo_isCFWDActive(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_isCFWDActive";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->isCFWD);
+       return info->isCFWD;
+   }
+   return FALSE;
+}
+
+/**
+ * Get the CFWDAll destination
+ * @param line - line reference handle
+ * @return cc_string_t - cfwd target
+ */
+cc_string_t CCAPI_lineInfo_getCFWDName(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_getCFWDName";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->cfwd_dest);
+       return info->cfwd_dest;
+   }
+   return NULL;
+}
+
+/**
+ * Get the MWI Status
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI status (boolean 0 => no MWI)
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIStatus(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_getMWIStatus";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d, status %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->mwi, info->mwi.status);
+       return info->mwi.status;
+   }
+   return 0;
+}
+
+/**
+ * Get the MWI Type
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI Type
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIType(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_getMWIType";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d, type %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->mwi, info->mwi.type);
+       return info->mwi.type;
+   }
+   return 0;
+}
+
+/**
+ * Get the MWI new msg count
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI new msg count
+ */
+cc_uint32_t CCAPI_lineInfo_getMWINewMsgCount(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_getMWINewMsgCount";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d, new count %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->mwi, info->mwi.new_count);
+       return info->mwi.new_count;
+   }
+   return 0;
+}
+/**
+ * Get the MWI old msg count
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI old msg count
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIOldMsgCount(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_getMWIOldMsgCount";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d, old_count %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->mwi, info->mwi.old_count);
+       return info->mwi.old_count;
+   }
+   return 0;
+}
+
+/**
+ * Get the MWI high priority new msg count
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI new msg count
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIPrioNewMsgCount(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_getMWIPrioNewMsgCount";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d , pri_new count %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->mwi, info->mwi.pri_new_count);
+       return info->mwi.pri_new_count;
+   }
+   return 0;
+}
+/**
+ * Get the MWI high priority old msg count
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI old msg count
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIPrioOldMsgCount(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_getMWIPrioOldMsgCount";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d, pri old_count %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->mwi, info->mwi.pri_old_count);
+       return info->mwi.pri_old_count;
+   }
+   return 0;
+}
+
+/**
+ * Get calls on line
+ * @param [in] line - lineID
+ * @param [out] callref[] - Array of callinfo references
+ * @param [in/out] count - count of call references populated
+ * @return void
+ */
+
+void CCAPI_LineInfo_getCalls(cc_lineid_t line, cc_call_handle_t handles[], int *count)
+{
+    static const char *fname="CCAPI_Line_getCalls";
+    hashItr_t itr;
+    session_data_t *data;
+    int i=0;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    hashItrInit(&itr);
+    while ( (data = (session_data_t*)hashItrNext(&itr)) != NULL &&
+              i<*count ) {
+         if ( GET_LINE_ID(data->sess_id) == line ){
+            handles[i++] = CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id);
+         }
+    }
+    *count=i;
+    CCAPP_DEBUG(DEB_F_PREFIX"Finished (no return) \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+}
+
+/**
+ * Get calls on line
+ * @param [in] line - lineID
+ * @param [out] callref[] - Array of callinfo references
+ * @param [in/out] count - count of call references populated
+ * @return void
+ */
+void CCAPI_LineInfo_getCallsByState(cc_lineid_t line, cc_call_state_t state,
+                 cc_call_handle_t handles[], int *count)
+{
+    static const char *fname="CCAPI_Line_getCallsByState";
+    hashItr_t itr;
+    session_data_t *data;
+    int i=0;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    hashItrInit(&itr);
+    while ( (data = (session_data_t*)hashItrNext(&itr)) != NULL &&
+              i<*count ) {
+         if ( GET_LINE_ID(data->sess_id) == line && data->state ==state ){
+            handles[i++] = CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id);
+         }
+    }
+    *count=i;
+    CCAPP_DEBUG(DEB_F_PREFIX"Finished (no return) \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+}
+
+/**
+ * Get the physical button number on which this line is configured
+ * @param [in] line - line reference handle
+ * @return cc_uint32_t - button number
+ */
+cc_boolean CCAPI_lineInfo_getRegState(cc_lineinfo_ref_t line)
+{
+   static const char *fname="CCAPI_lineInfo_getRegState";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL ) {
+       CCAPP_DEBUG(DEB_F_PREFIX"returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->reg_state);
+       return info->reg_state;
+   }
+   return 0;
+}
+
+/**
+ * has capability - is the feature allowed
+ * @param [in] line - line reference handle
+ * @param [in] feat_id - feature id
+ * @return boolean - is Allowed
+ */
+cc_boolean  CCAPI_LineInfo_hasCapability (cc_lineinfo_ref_t line, cc_int32_t feat_id){
+   static const char *fname="CCAPI_LineInfo_hasCapability";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+   if ( info != NULL){
+     CCAPP_DEBUG(DEB_F_PREFIX"feature id:  %d , value returned %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),feat_id,  info->allowed_features[feat_id]);
+      return info->allowed_features[feat_id];
+    }
+
+    return FALSE;
+}
+
+
+/**
+ * get Allowed Feature set
+ * @param [in] line - line reference handle
+ * @param [in,out] feat_set - array of len CC_CALL_CAP_MAX
+ * @return cc_return_t - CC_SUCCESS or CC_FAILURE
+ */
+cc_return_t  CCAPI_LineInfo_getCapabilitySet (cc_lineinfo_ref_t line, cc_int32_t feat_set[]){
+    static const char *fname="CCAPI_LineInfo_getCapabilitySet";
+   cc_line_info_t  *info = (cc_line_info_t *) line;
+    int feat_id;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    if ( info != NULL){
+       for (feat_id = 0; feat_id < CCAPI_CALL_CAP_MAX; feat_id++) {
+         feat_set[feat_id] = info->allowed_features[feat_id];
+         CCAPP_DEBUG(DEB_F_PREFIX"feature id:  %d , value %d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),feat_id,  feat_set[feat_id]);
+       }
+
+       CCAPP_DEBUG(DEB_F_PREFIX"returned CC_SUCCESS\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+       return CC_SUCCESS;
+    }
+
+    return CC_FAILURE;
+}
+
+
diff --git a/libs/sipcc/core/ccapp/ccapi_service.c b/libs/sipcc/core/ccapp/ccapi_service.c
new file mode 100644 (file)
index 0000000..bd6cafe
--- /dev/null
@@ -0,0 +1,166 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ccapi_service.h"
+#include "cc_device_manager.h"
+#include "cc_service.h"
+#include "phone_debug.h"
+#include "CCProvider.h"
+#include "sessionConstants.h"
+#include "ccsip_messaging.h"
+#include "ccapp_task.h"
+#include "config_api.h"
+#include "ccapi_device.h"
+#include "ccapi_device_info.h"
+#include "cc_device_listener.h"
+#include "cc_service_listener.h"
+#include "plat_api.h"
+#include "util_string.h"
+
+int sendResetUpdates  = 0;         // default is not to send updates
+
+// Global Variables
+int g_dev_hdl;
+char g_dev_name[G_DEV_NAME_SIZE];
+char g_cfg_p[G_CFG_P_SIZE];
+int g_compl_cfg;
+
+// Externs
+extern void setState();
+extern void resetReady();
+extern void resetNotReady();
+extern void ccpro_handleserviceControlNotify();
+
+
+extern cc_srv_ctrl_cmd_t reset_type;
+boolean isServiceStartRequestPending = FALSE;
+cc_boolean is_action_to_be_deferred(cc_action_t action);
+extern cc_action_t pending_action_type;
+//cc_boolean parse_config_properties (int device_handle, const char *device_name, const char *cfg, int from_memory);
+
+
+
+/**
+ * Defines the management methods.
+ */
+
+/**
+ * Pre-initialize the Sipcc stack.
+ * @return
+ */
+cc_return_t CCAPI_Service_create() {
+    CCAPP_ERROR("CCAPI_Service_create - calling CC_Service_create \n");
+
+    registration_processEvent(EV_CC_CREATE);
+    return (CC_SUCCESS);
+    //return (service_processEvent(EV_SRVC_CREATE));
+}
+
+/**
+ * Gracefully unload the Sipcc stack
+ * @return
+ */
+cc_return_t CCAPI_Service_destroy() {
+    CCAPP_ERROR("CCAPI_Service_destroy - calling CC_Service_destroy \n");
+
+ //   if (is_action_to_be_deferred(STOP_ACTION) == TRUE) {
+ //       return CC_SUCCESS;
+ //   }
+    // initialize the config to empty
+    init_empty_str(g_cfg_p);
+    isServiceStartRequestPending = FALSE;
+    registration_processEvent(EV_CC_DESTROY);
+    return (CC_SUCCESS);
+}
+
+/**
+ * Bring up the Sipcc stack in service
+ * @return
+ */
+cc_return_t CCAPI_Service_start() {
+
+    if (isServiceStartRequestPending == TRUE) {
+        DEF_DEBUG("CCAPI_Service_start request is already pending. Ignoring this.\n");
+        return CC_SUCCESS;
+    }
+
+    DEF_DEBUG("CCAPI_Service_start - \n");
+    isServiceStartRequestPending = TRUE;
+
+    registration_processEvent(EV_CC_START);
+
+    return (CC_SUCCESS);
+}
+
+/**
+ * Stop Sipcc stack service
+ * @return
+ */
+cc_return_t CCAPI_Service_stop() {
+
+       int  sdpmode = 0;
+
+    CCAPP_ERROR("CCAPI_Service_stop - calling registration stop \n");
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (!sdpmode) {
+        if (is_action_to_be_deferred(STOP_ACTION) == TRUE) {
+            return CC_SUCCESS;
+        }
+    }
+    sendResetUpdates  = 0;         // reset to default is not to send updates
+    isServiceStartRequestPending = FALSE;
+    registration_processEvent(EV_CC_STOP);
+    return CC_SUCCESS;
+}
+
+
+/**
+ * reregister the Sipcc stack service, without downloading the config file
+ *
+ */
+cc_return_t CCAPI_Service_reregister(int device_handle, const char *device_name,
+                             const char *cfg,
+                             int complete_config)
+{
+    CCAPP_ERROR("CCAPI_Service_reregister - initiate reregister \n");
+
+    if (is_action_to_be_deferred(RE_REGISTER_ACTION) == TRUE) {
+        return CC_SUCCESS;
+    }
+    if (pending_action_type != NO_ACTION) {
+        CCAPP_ERROR("Reset/Restart is pending, reregister Ignored! \n");
+        return CC_FAILURE;
+    }
+
+    if (is_empty_str((char*)cfg)) {
+        CCAPP_ERROR("Reregister request with empty config.  Exiting.\n");
+        return CC_FAILURE;
+    }
+
+    g_dev_hdl = device_handle;
+    sstrncpy(g_dev_name, device_name, sizeof(g_dev_name));
+    sstrncpy(g_cfg_p, cfg, sizeof(g_cfg_p));
+    CCAPP_DEBUG("CCAPI_Service_reregister - devce name [%s], cfg [%s] \n", g_dev_name, g_cfg_p);
+    g_compl_cfg  = complete_config;
+
+        registration_processEvent(EV_CC_RE_REGISTER);
+
+    return (CC_SUCCESS);
+}
+
+/**
+ * Reset Manager has request a reset, send the current state and
+ * start sending updates.
+ */
+void CCAPI_Service_reset_request()  {
+    cc_deviceinfo_ref_t handle = 0;
+    sendResetUpdates  = 1;
+    if (CCAPI_DeviceInfo_isPhoneIdle(handle) == TRUE) {
+        resetReady();
+    } else {
+        resetNotReady();
+    }
+
+}
diff --git a/libs/sipcc/core/ccapp/ccapi_snapshot.c b/libs/sipcc/core/ccapp/ccapi_snapshot.c
new file mode 100644 (file)
index 0000000..b2e15b4
--- /dev/null
@@ -0,0 +1,715 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "string.h"
+#include "string_lib.h"
+#include "text_strings.h"
+#include "ccapi_snapshot.h"
+#include "ccapi_device.h"
+#include "ccapi_device_listener.h"
+#include "ccapi_line.h"
+#include "ccapi_line_listener.h"
+#include "ccapi_line_info.h"
+#include "ccapi_call.h"
+#include "ccapi_call_listener.h"
+#include "CCProvider.h"
+#include "capability_set.h"
+#include "phone_debug.h"
+
+cc_device_info_t g_deviceInfo;
+accessory_cfg_info_t g_accessoryCfgInfo;
+cc_line_info_t lineInfo[MAX_CONFIG_LINES+1];
+cc_feature_info_t featureInfo[MAX_CONFIG_LINES+1];
+
+static void printCallInfo(cc_callinfo_ref_t info, const char* fname);
+static void printFeatureInfo (ccapi_device_event_e type, cc_featureinfo_ref_t feature_info, const char* fname);
+
+cc_string_t lineLabels[MAX_CONFIG_LINES+1] = {0};
+
+
+void ccsnap_set_line_label(int btn, cc_string_t label) {
+
+   CCAPP_ERROR(DEB_F_PREFIX"btn=%d label=%s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_set_line_label"), btn, label);
+   if ( btn > 0 && btn <= MAX_CONFIG_LINES+1 ) {
+       if ( label == NULL ) {
+         label = strlib_empty();
+       }
+       if ( lineLabels[btn] == NULL ) {
+         lineLabels[btn] = strlib_empty();
+       }
+       lineLabels[btn] = strlib_update(lineLabels[btn], label);
+   }
+}
+
+cc_string_t ccsnap_get_line_label(int btn) {
+   if ( btn > 0 && btn <= MAX_CONFIG_LINES+1 ) {
+     return lineLabels[btn];
+   }
+   return strlib_empty();
+}
+
+/*
+ * The below two functions are borrowed from CUCM/CUP as they both perform
+ * identical functions.  That is, taking a DN 1555 and
+ * a mask 919476XXXX to build a true external number 9194761555.
+ */
+static void stringInsert(char *string, int num, char ch)
+{
+
+   int len = strlen(string);
+   int k, j;
+   char tempString[100];
+   sstrncpy(tempString, string, 100);
+
+   for (k = 0; k < num; k++)
+       string[k] = ch;
+
+   for (j = 0; j < len; j++)
+       string[k++] = tempString[j];
+
+   string[k] = 0;
+
+}
+
+/*
+ * Taken from CUCM/CUP code as they have done this already.
+ */
+cc_string_t CCAPI_ApplyTranslationMask (const char *ext, const char *mask)
+{
+
+    char translationMask[100] = {'\0'};
+    char dn[100] = {'\0'};
+    char translatedString[100] = {'\0'};
+    cc_string_t result;
+    unsigned int maskLen,
+                 dnLen,
+                 i, j = 0;
+
+    if ((ext == NULL) || (mask == NULL)) {
+        return NULL;
+    }
+
+    maskLen = strlen(mask);
+    dnLen = strlen(ext);
+
+    if ((dnLen == 0) || (maskLen == 0)) {
+        CCAPP_DEBUG(DEB_F_PREFIX"CCAPI_ApplyTranslationMask DN or mask has len=0\n",
+DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI_ApplyTranslationMask"));
+        return NULL;
+    }
+
+    /* make sure there's enough space in the buffer to
+     * hold the translated string.
+     */
+    if (dnLen + maskLen > 99) {
+        CCAPP_DEBUG(DEB_F_PREFIX"CCAPI_ApplyTranslationMask length overflow\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI_ApplyTranslationMask"));
+       return NULL;
+    }
+
+    sstrncpy(translationMask, mask, 100);
+    sstrncpy(dn, ext, 100);
+
+    /* make sure DN is numeric only */
+    for (i=0; i< dnLen; i++) {
+        if (isalpha(dn[i])) {
+           return 0;
+        }
+    }
+
+    if (maskLen > dnLen) {
+       stringInsert(dn, maskLen - dnLen, '?');
+    }
+
+    /* if the digit string is longer than the translation mask
+     * prepad the translation mask with '%'.
+     */
+    if (dnLen > maskLen) {
+       stringInsert(translationMask, dnLen - maskLen, '%');
+    }
+
+    dnLen = strlen(dn);
+
+    for (i=0; i < dnLen; i++) {
+        if (translationMask[i] == '%')
+            continue;
+        else if (translationMask[i] == 'X')
+            translatedString[j++] = dn[i];
+        else
+            translatedString[j++] = translationMask[i];
+    }
+
+    translatedString[j] = 0;
+    result = strlib_malloc(translatedString, strlen(translatedString));
+    return result;
+}
+
+/**
+ * Before initing the line_info release any memory which has been used
+ * so we do not leak any here.
+ */
+void ccsnap_line_pre_init () {
+    int i;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering line_pre_init to clear it out to avoid mem leaks\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_line_pre_init"));
+
+    for (i=1;i<MAX_CONFIG_LINES;i++) {
+        if ((lineInfo[i].name) && (strlen(lineInfo[i].name) > 0)) {
+            strlib_free(lineInfo[i].name);
+        }
+        if ((lineInfo[i].dn) && (strlen(lineInfo[i].dn) > 0)) {
+            strlib_free(lineInfo[i].dn);
+        }
+        if ((lineInfo[i].cfwd_dest) && (strlen(lineInfo[i].cfwd_dest) > 0)) {
+            strlib_free(lineInfo[i].cfwd_dest);
+        }
+        if ((lineInfo[i].externalNumber) &&
+            (strlen(lineInfo[i].externalNumber) > 0)) {
+            strlib_free(lineInfo[i].externalNumber);
+        }
+        if ((featureInfo[i].speedDialNumber) &&
+            (strlen(featureInfo[i].speedDialNumber) > 0)) {
+            strlib_free(featureInfo[i].speedDialNumber);
+        }
+        if ((featureInfo[i].contact) && (strlen(featureInfo[i].contact) > 0)) {
+            strlib_free(featureInfo[i].contact);
+        }
+        if ((featureInfo[i].name) && (strlen(featureInfo[i].name) > 0)) {
+            strlib_free(featureInfo[i].name);
+        }
+        if ((featureInfo[i].retrievalPrefix) &&
+            (strlen(featureInfo[i].retrievalPrefix) > 0)) {
+            strlib_free(featureInfo[i].retrievalPrefix);
+        }
+    }
+}
+
+/**
+ * Initialize lineinfo and featureinfo arrays
+ */
+void ccsnap_line_init() {
+   int i;
+   cc_uint32_t tmpInt;
+   char tempStr[MAX_URL_LENGTH];
+   char maskStr[MAX_EXTERNAL_NUMBER_MASK_SIZE];
+
+   /* clean up structure if need be */
+   ccsnap_line_pre_init();
+
+   memset(lineInfo, 0, MAX_CONFIG_LINES*sizeof(cc_line_info_t));
+   memset(featureInfo, 0, MAX_CONFIG_LINES*sizeof(cc_feature_info_t));
+   for (i=1;i<=MAX_CONFIG_LINES;i++) {
+      config_get_line_value(CFGID_LINE_FEATURE, &tmpInt, sizeof(tmpInt), i);
+      if ( tmpInt == cfgLineFeatureDN ) {
+          lineInfo[i].button = i;
+          lineInfo[i].line_type = tmpInt;
+          config_get_line_value(CFGID_LINE_INDEX,  &tmpInt, sizeof(tmpInt), i);
+          lineInfo[i].line_id = tmpInt;
+          config_get_line_value(CFGID_LINE_DISPLAYNAME_STRING, tempStr,
+                          MAX_URL_LENGTH, i);
+          lineInfo[i].dn = strlib_malloc(tempStr, strlen(tempStr));
+          config_get_line_value(CFGID_LINE_NAME_STRING, tempStr,
+                          MAX_URL_LENGTH, i);
+          lineInfo[i].name = strlib_malloc(tempStr, strlen(tempStr));
+          config_get_line_value(CFGID_LINE_CFWDALL, tempStr,
+                          MAX_URL_LENGTH, i);
+          lineInfo[i].cfwd_dest = strlib_malloc(tempStr, strlen(tempStr));
+          config_get_line_value(CFGID_LINE_SPEEDDIAL_NUMBER_STRING, tempStr,
+                          MAX_URL_LENGTH, i);
+          memset(maskStr, 0, sizeof(maskStr));
+          config_get_string(CFGID_CCM_EXTERNAL_NUMBER_MASK, maskStr, MAX_EXTERNAL_NUMBER_MASK_SIZE);
+          if (strlen(maskStr) > 0) {
+              lineInfo[i].externalNumber = CCAPI_ApplyTranslationMask(lineInfo[i].name, maskStr);
+              CCAPP_DEBUG("Setting lineInfo[i].externalNumber to %s\n", lineInfo[i].externalNumber);
+          } else {
+              lineInfo[i].externalNumber = strlib_empty();
+          }
+      } else {
+          lineInfo[i].line_id = MAX_CONFIG_LINES+1; // invalid line id
+          lineInfo[i].button = i;
+          lineInfo[i].dn = strlib_empty();
+          lineInfo[i].name = strlib_empty();
+          lineInfo[i].cfwd_dest = strlib_empty();
+          lineInfo[i].externalNumber = strlib_empty();
+      }
+      capset_get_idleset(CC_MODE_CCM, lineInfo[i].allowed_features);
+
+      // get feature again because it might have been changed if it is a DN
+      // and the tmpInt might have a different value
+      config_get_line_value(CFGID_LINE_FEATURE, &tmpInt, sizeof(tmpInt), i);
+
+      // features which have no properties
+      if ( tmpInt == cfgLineFeatureAllCalls ||
+                 tmpInt == cfgLineFeatureMaliciousCallID ||
+                 tmpInt == cfgLineFeatureRedial || tmpInt == cfgLineFeatureAnswerOldest || tmpInt == cfgLineFeatureServices ) {
+         featureInfo[i].feature_id = tmpInt;
+         featureInfo[i].button = i;
+          featureInfo[i].speedDialNumber = strlib_empty();
+          featureInfo[i].contact = strlib_empty();
+          featureInfo[i].name = strlib_empty();
+          featureInfo[i].retrievalPrefix = strlib_empty();
+          featureInfo[i].featureOptionMask = 0;
+      } else if ( tmpInt == cfgLineFeatureSpeedDialBLF || tmpInt == cfgLineFeatureSpeedDial){
+         featureInfo[i].feature_id = tmpInt;
+         featureInfo[i].button = i;
+          config_get_line_value(CFGID_LINE_SPEEDDIAL_NUMBER_STRING, tempStr,
+                          MAX_URL_LENGTH, i);
+          featureInfo[i].speedDialNumber = strlib_malloc(tempStr, strlen(tempStr));
+          featureInfo[i].contact = strlib_empty();
+          config_get_line_value(CFGID_LINE_NAME_STRING, tempStr,
+                          MAX_URL_LENGTH, i);
+          featureInfo[i].name = strlib_malloc(tempStr, strlen(tempStr));
+          featureInfo[i].retrievalPrefix = strlib_empty();
+          config_get_line_value(CFGID_LINE_FEATURE_OPTION_MASK,  &tmpInt, sizeof(tmpInt), i);
+          featureInfo[i].featureOptionMask = tmpInt;
+          featureInfo[i].blf_state = CC_SIP_BLF_UNKNOWN;
+      } else {
+          featureInfo[i].feature_id = 0;
+          featureInfo[i].button = MAX_CONFIG_LINES+1; // invalid button value
+          featureInfo[i].speedDialNumber = strlib_empty();
+          featureInfo[i].contact = strlib_empty();
+          featureInfo[i].name = strlib_empty();
+          featureInfo[i].retrievalPrefix = strlib_empty();
+          featureInfo[i].featureOptionMask = 0;
+      }
+   }
+}
+
+cc_line_info_t* ccsnap_getLineInfo(int lineID)
+{
+     int i;
+     cc_lineid_t line = (cc_lineid_t)lineID;
+
+     for (i=1;i<=MAX_CONFIG_LINES;i++) {
+        if ( lineInfo[i].line_id == line ) {
+            return &lineInfo[i];
+        }
+     }
+
+     return NULL;
+}
+
+cc_line_info_t* ccsnap_getLineInfoFromBtn(int btnID)
+{
+     int i;
+
+     for (i=1;i<=MAX_CONFIG_LINES;i++) {
+        if ( lineInfo[i].button == btnID ) {
+            return &lineInfo[i];
+        }
+     }
+
+     return NULL;
+}
+
+cc_boolean allowedFeature(int fid){
+    return TRUE;
+}
+
+cc_feature_info_t* ccsnap_getFeatureInfo(int featureIndex)
+{
+       if ( ( featureIndex<=MAX_CONFIG_LINES ) &&
+                       ( featureIndex>= 1 ) &&
+               ( featureInfo[featureIndex].button == featureIndex ) ) {
+            if ( allowedFeature(featureInfo[featureIndex].feature_id) ){
+               return &featureInfo[featureIndex];
+            }
+       }
+
+     return NULL;
+}
+
+/**
+ * Release any used mem to avoid a leak.
+ */
+void ccsnap_device_pre_init () {
+    int i = 0;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"Entering device_pre_init to clear it out to avoid mem leaks\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_device_pre_init"));
+    if ((g_deviceInfo.name) && (strlen(g_deviceInfo.name) > 0)) {
+        strlib_free(g_deviceInfo.name);
+    }
+    if ((g_deviceInfo.not_prompt) && (strlen(g_deviceInfo.not_prompt) > 0)) {
+        strlib_free(g_deviceInfo.not_prompt);
+    }
+
+    i = 0;
+    while (i < CCAPI_MAX_SERVERS) {
+        if ((g_deviceInfo.ucm[i].name) &&
+            (strlen(g_deviceInfo.ucm[i].name) > 0)) {
+            strlib_free(g_deviceInfo.ucm[i].name);
+        }
+        i++;
+    }
+}
+
+void ccsnap_device_init() {
+   char temp[MAX_SIP_URL_LENGTH];
+
+   /* clean up structure if need be */
+   ccsnap_device_pre_init();
+
+   memset (&g_deviceInfo, 0, sizeof(g_deviceInfo));
+   g_deviceInfo.name =strlib_empty();
+   g_deviceInfo.not_prompt =strlib_empty();
+
+   g_deviceInfo.not_prompt_prio = 0;
+   g_deviceInfo.not_prompt_prog = 0;
+   g_deviceInfo.mwi_lamp = FALSE;
+   g_deviceInfo.cucm_mode = CC_MODE_CCM;
+   g_deviceInfo.ins_state = CC_STATE_IDLE;
+   g_deviceInfo.ins_cause = CC_CAUSE_NONE;
+   g_deviceInfo.reg_time  = 0;
+
+   config_get_string(CFGID_CCM1_ADDRESS, temp, MAX_SIP_URL_LENGTH);
+   g_deviceInfo.ucm[0].name = strlib_malloc(temp, strlen(temp));
+   g_deviceInfo.ucm[0].type = CC_MODE_CCM;
+   g_deviceInfo.ucm[0].status = CC_CCM_STATUS_NONE;
+
+   config_get_string(CFGID_CCM2_ADDRESS, temp, MAX_SIP_URL_LENGTH);
+   g_deviceInfo.ucm[1].name = strlib_malloc(temp, strlen(temp));
+   g_deviceInfo.ucm[1].type = CC_MODE_CCM;
+   g_deviceInfo.ucm[1].status = CC_CCM_STATUS_NONE;
+
+   config_get_string(CFGID_CCM3_ADDRESS, temp, MAX_SIP_URL_LENGTH);
+   g_deviceInfo.ucm[2].name = strlib_malloc(temp, strlen(temp));
+   g_deviceInfo.ucm[2].type = CC_MODE_CCM;
+   g_deviceInfo.ucm[2].status = CC_CCM_STATUS_NONE;
+
+   config_get_string(CFGID_CCM_TFTP_IP_ADDR, temp, MAX_SIP_URL_LENGTH);
+   g_deviceInfo.ucm[3].name = strlib_malloc(temp, strlen(temp));
+   g_deviceInfo.ucm[3].type = CC_MODE_CCM;
+   g_deviceInfo.ucm[3].status = CC_CCM_STATUS_NONE;
+
+   g_accessoryCfgInfo.camera = ACCSRY_CFGD_CFG;
+   g_accessoryCfgInfo.video = ACCSRY_CFGD_CFG;
+}
+
+void ccsnap_gen_deviceEvent(ccapi_device_event_e event, cc_device_handle_t handle){
+    const char* fname = "ccsnap_gen_deviceEvent";
+
+    cc_device_info_t *device_info = CCAPI_Device_getDeviceInfo(handle);
+    if ( device_info != NULL ) {
+        CCAPP_DEBUG(DEB_F_PREFIX"data->ref_count=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device_info->ref_count);
+
+        switch (event) {
+            case CCAPI_DEVICE_EV_NOTIFYPROMPT:
+              CCAPP_DEBUG(DEB_F_PREFIX"data->not_prompt=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device_info->not_prompt);
+              CCAPP_DEBUG(DEB_F_PREFIX"data->not_prompt_prio=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device_info->not_prompt_prio);
+              CCAPP_DEBUG(DEB_F_PREFIX"data->not_prompt_prog=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device_info->not_prompt_prog);
+              break;
+            case CCAPI_DEVICE_EV_STATE:
+              CCAPP_DEBUG(DEB_F_PREFIX"setting property %s to %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), "FullyRegistered", ((device_info->ins_state == CC_STATE_INS) ? "1" : "0"));
+              //intentional follow through to let the debugs get printed.
+            default:
+              CCAPP_DEBUG(DEB_F_PREFIX"data->name=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device_info->name);
+              CCAPP_DEBUG(DEB_F_PREFIX"data->mwi_lamp=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device_info->mwi_lamp);
+              CCAPP_DEBUG(DEB_F_PREFIX"data->ins_state=%02X \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device_info->ins_state);
+              CCAPP_DEBUG(DEB_F_PREFIX"data->cucm_mode=%02X \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device_info->cucm_mode);
+              CCAPP_DEBUG(DEB_F_PREFIX"data->ins_cause=%02X \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), device_info->ins_cause);
+              break;
+
+        }
+
+        CCAPI_DeviceListener_onDeviceEvent(event, handle, device_info);
+    }
+    CCAPI_Device_releaseDeviceInfo(device_info);
+}
+
+void ccsnap_gen_lineEvent(ccapi_line_event_e event, cc_lineid_t handle){
+    const char* fname = "ccsnap_gen_lineEvent";
+    cc_line_info_t *line_info = CCAPI_Line_getLineInfo(handle);
+
+    if ( line_info != NULL ) {
+        if (g_CCAppDebug) {
+            CCAPP_DEBUG(DEB_F_PREFIX"data->ref_count=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->ref_count);
+            CCAPP_DEBUG(DEB_F_PREFIX"data->line_id=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->line_id);
+            CCAPP_DEBUG(DEB_F_PREFIX"data->button=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->button);
+            CCAPP_DEBUG(DEB_F_PREFIX"data->reg_state=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->reg_state);
+            CCAPP_DEBUG(DEB_F_PREFIX"data->isCFWD=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->isCFWD);
+            CCAPP_DEBUG(DEB_F_PREFIX"data->isLocalCFWD=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->isLocalCFWD);
+            CCAPP_DEBUG(DEB_F_PREFIX"data->mwi=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->mwi);
+            CCAPP_DEBUG(DEB_F_PREFIX"data->name=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->name);
+            CCAPP_DEBUG(DEB_F_PREFIX"data->dn=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->dn);
+            CCAPP_DEBUG(DEB_F_PREFIX"data->cfwd_dest=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), line_info->cfwd_dest);
+        }
+        CCAPI_LineListener_onLineEvent(event, handle, line_info);
+    }
+    CCAPI_Line_releaseLineInfo(line_info);
+}
+
+void ccsnap_gen_callEvent(ccapi_call_event_e event, cc_call_handle_t handle){
+
+    session_data_t *call_info = CCAPI_Call_getCallInfo(handle);
+
+    if ( call_info == NULL ) {
+        call_info = getDeepCopyOfSessionData(NULL);
+    }
+
+    //print all info
+    if (g_CCAppDebug) {
+        printCallInfo(call_info, "ccsnap_gen_callEvent");
+    }
+
+    CCAPI_CallListener_onCallEvent(event, handle, call_info);
+    CCAPI_Call_releaseCallInfo(call_info);
+}
+
+void ccsnap_update_ccm_status(cc_string_t addr, cc_ccm_status_t status)
+{
+    int i;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"entry ccm %s status=%d\n",
+               DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_update_ccm_status"), addr, status);
+
+    for (i=0;i< CCAPI_MAX_SERVERS;i++) {
+        if ( g_deviceInfo.ucm[i].status == status ) {
+            //move the status to the new addr
+            g_deviceInfo.ucm[i].status = CC_CCM_STATUS_NONE;
+        }
+        if ( !strcmp(addr, g_deviceInfo.ucm[i].name) ) {
+           g_deviceInfo.ucm[i].status = status;
+           CCAPP_DEBUG(DEB_F_PREFIX"server %s is now status=%d\n",
+            DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_update_ccm_status"),
+            g_deviceInfo.ucm[i].name, status);
+        }
+    }
+}
+
+void ccsnap_handle_mnc_reached (cc_line_info_t *line_info, cc_boolean mnc_reached, cc_cucm_mode_t mode)
+{
+    cc_call_handle_t handles[MAX_CALLS];
+    int count = MAX_CALLS, i;
+    session_data_t *cinfo;
+
+    if (mnc_reached) {
+       line_info->allowed_features[CCAPI_CALL_CAP_NEWCALL] = FALSE;
+        line_info->allowed_features[CCAPI_CALL_CAP_REDIAL] = FALSE;
+        line_info->allowed_features[CCAPI_CALL_CAP_CALLFWD] = FALSE;
+    } else {
+         capset_get_idleset(mode, line_info->allowed_features);
+    }
+
+    // update connected calls caps on this line
+    CCAPI_LineInfo_getCallsByState(line_info->line_id, CONNECTED, handles, &count);
+    for ( i=0; i<count; i++) {
+       cinfo = CCAPI_Call_getCallInfo(handles[i]);
+       if (cinfo) {
+           if ( cinfo->attr == (cc_call_attr_t) CONF_CONSULT ||
+                cinfo->attr == (cc_call_attr_t) XFR_CONSULT ) {
+              CCAPI_Call_releaseCallInfo(cinfo);
+              continue;
+           }
+            cinfo->allowed_features[CCAPI_CALL_CAP_TRANSFER] = mnc_reached?FALSE:TRUE;
+            cinfo->allowed_features[CCAPI_CALL_CAP_CONFERENCE] = mnc_reached?FALSE:TRUE;
+            //print call info
+            if (g_CCAppDebug) {
+                printCallInfo(cinfo, "ccsnap_handle_mnc_reached");
+            }
+            CCAPI_CallListener_onCallEvent(CCAPI_CALL_EV_CAPABILITY, handles[i], cinfo);
+       }
+    }
+    // update RIU call caps on this line
+    CCAPI_LineInfo_getCallsByState(line_info->line_id, REMINUSE, handles, &count);
+    for ( i=0; i<count; i++) {
+        cinfo = CCAPI_Call_getCallInfo(handles[i]);
+        if (cinfo) {
+            cinfo->allowed_features[CCAPI_CALL_CAP_BARGE] = mnc_reached?FALSE:TRUE;
+            //print call info
+            if (g_CCAppDebug) {
+                printCallInfo(cinfo, "ccsnap_handle_mnc_reached");
+            }
+            CCAPI_CallListener_onCallEvent(CCAPI_CALL_EV_CAPABILITY, handles[i], cinfo);
+        }
+    }
+}
+
+void ccsnap_gen_blfFeatureEvent(cc_blf_state_t state, int appId)
+{
+    cc_feature_info_t *feature_info = NULL;
+
+    feature_info = ccsnap_getFeatureInfo(appId);
+
+    // if the feature exists
+    if (feature_info != NULL) {
+        feature_info->blf_state = state;
+        printFeatureInfo(CCAPI_DEVICE_EV_BLF, feature_info, "ccsnap_gen_blfFeatureEvent");
+        CCAPI_DeviceListener_onFeatureEvent(CCAPI_DEVICE_EV_BLF, CC_DEVICE_ID, feature_info);
+    }
+}
+
+/**
+ * Inserts localized strings into existing strings with escape characters.
+ * @param destination the return phrase holder
+ * @param source the phrase with escape characters.
+ * @param len the input length to cap the maximum value
+ * @return pointer to the new string
+ */
+cc_string_t ccsnap_EscapeStrToLocaleStr(cc_string_t destination, cc_string_t source, int len)
+{
+       static const char *fname="ccsnap_EscapeStrToLocaleStr";
+       char phrase_collector[MAX_LOCALE_STRING_LEN] = { '\0' };
+       char* phrase_collector_ptr = phrase_collector;
+       char* esc_string_itr = (char*)source;
+       int remaining_length = 0;
+       cc_string_t ret_str = strlib_empty();
+
+       if(destination == NULL){
+               CCAPP_DEBUG(DEB_F_PREFIX"Error: destination is NULL\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+               return NULL;
+       }
+
+       if(source == NULL){
+               CCAPP_DEBUG(DEB_F_PREFIX"Error: source is NULL\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+               strlib_free(destination);
+               return strlib_empty();
+       }
+
+       if(source[0] == '\0'){
+               strlib_free(destination);
+               return strlib_empty();
+       }
+
+       if (len == LEN_UNKNOWN) {
+               len = strlen(source) + MAX_LOCALE_PHRASE_LEN;
+       }
+
+       if (len <= 0){
+               CCAPP_DEBUG(DEB_F_PREFIX"Error: cannot write string of length <= 0\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+               strlib_free(destination);
+               return strlib_empty();
+       }
+
+       if (len > MAX_LOCALE_STRING_LEN){
+               len = MAX_LOCALE_STRING_LEN;
+       }
+
+       remaining_length = len;
+       while(  *esc_string_itr != NUL    &&
+                       remaining_length > 0     &&
+                       strlen(phrase_collector_ptr) < (size_t)(len-1))
+       {
+               int rtn = CC_SUCCESS;
+               int phrase_index = 0;
+                char* phrase_bucket_ptr = (char*)cpr_malloc(remaining_length * sizeof(char));
+
+                if (phrase_bucket_ptr == NULL) {
+                    CCAPP_ERROR(DEB_F_PREFIX"Error: phrase_bucket_ptr is NULL\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+                    strlib_free(destination);
+                    return NULL;
+                }
+                phrase_bucket_ptr[0] = '\0';
+               switch(*esc_string_itr){
+               case OLD_CUCM_DICTIONARY_ESCAPE_TAG:
+                       phrase_index += CALL_CONTROL_PHRASE_OFFSET;
+                       // Do not set break to combine common code
+               case NEW_CUCM_DICTIONARY_ESCAPE_TAG:
+                       esc_string_itr++;
+                       phrase_index += (int)(*esc_string_itr);
+                       rtn = platGetPhraseText(phrase_index, phrase_bucket_ptr, remaining_length-1);
+                       if(rtn == CC_FAILURE) break;
+                       sstrncat(phrase_collector_ptr, (cc_string_t)phrase_bucket_ptr, remaining_length);
+                       remaining_length--;
+                       break;
+               default:
+                       // We need length 2 to concat 1 char and a terminating char
+                       sstrncat(phrase_collector_ptr, esc_string_itr, 1 + sizeof(char));
+                       remaining_length--;
+                       break;
+               }
+               esc_string_itr++;
+                cpr_free(phrase_bucket_ptr);
+       }
+
+    ret_str = strlib_malloc(phrase_collector_ptr, len);
+
+    if (!ret_str) {
+        /*
+         * If a malloc error occurred, give them back what they had.
+         * It's not right, but it's better than nothing.
+         */
+        ret_str = destination;
+    } else {
+        strlib_free(destination);
+    }
+
+    CCAPP_DEBUG(DEB_F_PREFIX"Localization String returning %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), ret_str);
+    return (ret_str);
+}
+
+static boolean missed, placed, received;
+void ccsnap_set_phone_services_provisioning(boolean misd, boolean plcd, boolean rcvd) {
+   CCAPP_ERROR(DEB_F_PREFIX"missed=%d placed=%d received=%d\n",
+               DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_set_phone_services_provisioning"), misd, plcd, rcvd);
+   missed = misd;
+   placed = plcd;
+   received = rcvd;
+}
+
+boolean ccsnap_isMissedCallLoggingEnabled()
+{
+   return missed;
+}
+
+boolean ccsnap_isReceivedCallLoggingEnabled()
+{
+   return received;
+}
+
+boolean ccsnap_isPlacedCallLoggingEnabled()
+{
+   return placed;
+}
+
+/**
+ * Helper method
+ */
+
+static void printCallInfo(cc_callinfo_ref_t info, const char* fname) {
+    CCAPP_DEBUG(DEB_F_PREFIX"data->ref_count=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->ref_count);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->sess_id=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->sess_id);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->line=%02X \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->line);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->id=%u \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->id);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->inst=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->inst);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->state=%02X \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->state);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->attr=%02X \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->attr);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->type=%02X \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->type);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->security=%02X \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->security);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->policy=%02X \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->policy);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->isSelected=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->isSelected);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->log_disp=%u \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->log_disp);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->clg_name=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->clg_name);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->clg_number=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->clg_number);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->alt_number=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->alt_number);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->cld_name=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->cld_name);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->cld_number=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->cld_number);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->orig_called_name=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->orig_called_name);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->orig_called_number=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->orig_called_number);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->last_redir_name=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->last_redir_name);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->last_redir_number=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->last_redir_number);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->plcd_name=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->plcd_name);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->plcd_number=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->plcd_number);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->status=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->status);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->gci=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->gci);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->cause=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->cause);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->vid_dir=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->vid_dir);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->vid_offer=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->vid_offer);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->is_conf=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->is_conf);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->ringer_start=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->ringer_start);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->ringer_mode=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->ringer_mode);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->ringer_once=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), info->ringer_once);
+}
+
+static void printFeatureInfo (ccapi_device_event_e type, cc_featureinfo_ref_t feature_info, const char* fname) {
+    CCAPP_DEBUG(DEB_F_PREFIX"data->button=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), feature_info->button);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->contact=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), feature_info->contact);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->featureOptionMask=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), feature_info->featureOptionMask);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->feature_id=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), feature_info->feature_id);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->name=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), feature_info->name);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->retrievalPrefix=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), feature_info->retrievalPrefix);
+    CCAPP_DEBUG(DEB_F_PREFIX"data->speedDialNumber=%s \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), feature_info->speedDialNumber);
+    if (type == CCAPI_DEVICE_EV_BLF) {
+        CCAPP_DEBUG(DEB_F_PREFIX"data->blf_state=%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), feature_info->blf_state);
+    }
+
+}
diff --git a/libs/sipcc/core/ccapp/ccapi_snapshot.h b/libs/sipcc/core/ccapp/ccapi_snapshot.h
new file mode 100644 (file)
index 0000000..087513f
--- /dev/null
@@ -0,0 +1,114 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_SNAPSHOT_H_
+#define _CCAPI_SNAPSHOT_H_
+
+#include "ccsip_platform.h"
+#include "prot_configmgr.h"
+#include "ccapi_line.h"
+
+/*
+ * MWI info
+ */
+typedef struct cc_mwi_info_t_ {
+  cc_uint32_t     status;
+  cc_uint32_t     type;
+  cc_uint32_t     new_count;
+  cc_uint32_t     old_count;
+  cc_uint32_t     pri_new_count;
+  cc_uint32_t     pri_old_count;
+} cc_mwi_info_t;
+
+/*
+ * line reference data structure
+ */
+typedef struct cc_line_info_t_ {
+  cc_uint32_t     ref_count;
+  cc_uint32_t     line_id;
+  cc_uint32_t     line_type;
+  cc_int32_t     button;
+  cc_boolean      reg_state;
+  cc_boolean      isCFWD;
+  cc_boolean      isLocalCFWD;
+  cc_mwi_info_t   mwi;
+  cc_string_t     name;
+  cc_string_t     dn;
+  cc_string_t     cfwd_dest;
+  cc_boolean    allowed_features[CCAPI_CALL_CAP_MAX];
+  cc_string_t     externalNumber;
+  cc_boolean      fwd_caller_name_display;
+  cc_boolean      fwd_caller_number_display;
+  cc_boolean      fwd_redirected_number_display;
+  cc_boolean      fwd_dialed_number_display;
+} cc_line_info_t;
+
+typedef struct cc_feature_info_t_ {
+  cc_int32_t     feature_id;
+  cc_int32_t     button;
+  cc_string_t     speedDialNumber;
+  cc_string_t     contact;
+  cc_string_t     name;
+  cc_string_t     retrievalPrefix;
+  cc_uint32_t     featureOptionMask;
+  cc_blf_state_t  blf_state;
+} cc_feature_info_t;
+
+typedef struct cc_call_server_t_ {
+  cc_string_t     name;
+  cc_ccm_status_t status;
+  cc_int32_t      type;
+} cc_call_server_t;
+
+typedef struct cc_device_info_t_ {
+  cc_uint32_t     ref_count;
+  cc_string_t     name;
+  cc_string_t     not_prompt;
+  char            registration_ip_addr[MAX_IPADDR_STR_LEN];
+  cc_int32_t      not_prompt_prio;
+  cc_boolean      not_prompt_prog;
+  cc_boolean      mwi_lamp;
+  cc_cucm_mode_t  cucm_mode;
+  cc_service_state_t  ins_state;
+  cc_service_cause_t  ins_cause;
+  long long                reg_time;
+  cc_call_server_t    ucm[CCAPI_MAX_SERVERS];
+} cc_device_info_t;
+
+typedef enum {
+    ACCSRY_CFGD_CFG,   //accessory last configured by configuration file.
+    ACCSRY_CFGD_APK,   //accessory last configured by another application.
+} accsry_cfgd_by_t;
+
+typedef struct accessory_cfg_info_t_ {
+    accsry_cfgd_by_t camera;
+    accsry_cfgd_by_t video;
+} accessory_cfg_info_t;
+
+extern accessory_cfg_info_t g_accessoryCfgInfo;
+extern cc_device_info_t g_deviceInfo;
+extern cc_line_info_t lineInfo[MAX_CONFIG_LINES+1];
+
+cc_line_info_t* ccsnap_getLineInfo(int lineID);
+cc_line_info_t* ccsnap_getLineInfoFromBtn(int btnID);
+void ccsnap_line_init();
+void ccsnap_device_init();
+void ccsnap_gen_deviceEvent(ccapi_device_event_e event, cc_device_handle_t handle);
+void ccsnap_gen_lineEvent(ccapi_line_event_e event, cc_lineid_t handle);
+void ccsnap_gen_callEvent(ccapi_call_event_e event, cc_call_handle_t handle);
+void ccsnap_update_ccm_status(cc_string_t addr, cc_ccm_status_t status);
+void ccsnap_handle_mnc_reached (cc_line_info_t *line_info,
+                cc_boolean mnc_reached, cc_cucm_mode_t mode);
+cc_feature_info_t* ccsnap_getFeatureInfo(int featureIndex);
+void ccsnap_gen_blfFeatureEvent(cc_blf_state_t state, int appId);
+cc_string_t ccsnap_EscapeStrToLocaleStr(cc_string_t destination, cc_string_t source, int len);
+void ccsnap_set_phone_services_provisioning(boolean misd, boolean plcd, boolean rcvd);
+boolean ccsnap_isMissedCallLoggingEnabled();
+boolean ccsnap_isReceivedCallLoggingEnabled();
+boolean ccsnap_isPlacedCallLoggingEnabled();
+void ccsnap_set_line_label(int btn, cc_string_t label);
+cc_string_t ccsnap_get_line_label(int btn);
+
+#endif
+
diff --git a/libs/sipcc/core/ccapp/ccapp_task.c b/libs/sipcc/core/ccapp/ccapp_task.c
new file mode 100644 (file)
index 0000000..0920c1a
--- /dev/null
@@ -0,0 +1,177 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ccapp_task.h"
+#include "phone.h"
+#include "CCProvider.h"
+#include "platform_api.h"
+
+extern cprMsgQueue_t ccapp_msgq;
+extern void CCAppInit();
+static sll_lite_list_t sll_list;
+
+/**
+ * Add/Get ccapp task listener
+ */
+void addCcappListener(appListener* listener, int type) {
+
+   listener_t *alistener = NULL;
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entered: listenr=0x%x, type=%d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "addCcappListener"),
+           listener, type);
+
+   if (listener == NULL)
+   {
+       CCAPP_ERROR(DEB_F_PREFIX"listener is NULL, returning\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "addCcappListener"));
+       return;
+   }
+
+   alistener = cpr_malloc(sizeof(listener_t));
+   if (alistener == NULL) {
+       CCAPP_ERROR(DEB_F_PREFIX"alistener is NULL, returning\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "addCcappListener"));
+       return;
+   }
+
+   alistener->type = type;
+   alistener->listener_p = listener;
+
+   sll_lite_link_tail(&sll_list, (sll_lite_node_t *)alistener);
+   CCAPP_DEBUG(DEB_F_PREFIX"Added: listenr=0x%x, type=%d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "addCcappListener"),
+           alistener->listener_p, alistener->type);
+}
+
+appListener *getCcappListener(int type) {
+    static const char fname[] ="getCcappListener";
+    listener_t *temp_info;
+    sll_lite_node_t *iterator;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"entered: for app[%d]\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+            type);
+
+    iterator = sll_list.head_p;
+    while (iterator) {
+        temp_info = (listener_t *)iterator;
+        CCAPP_DEBUG(DEB_F_PREFIX"appid=%d, listener=0x%x\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), temp_info->type, temp_info->listener_p);
+        if (temp_info->type == type) {
+            {
+                return temp_info->listener_p;
+            }
+        }
+        iterator = iterator->next_p;
+    }
+    return NULL;
+}
+
+/**
+ *
+ * CC Provider wrapper for posting msg to CCAPP
+ *
+ * @param   msgId - message ID
+ * @param   data - ptr to data
+ * @param   len - len of data
+ *
+ * @return  CPR_SUCCESS/CPR_FAILURE
+ *
+ * @pre     None
+ */
+
+cpr_status_e ccappTaskPostMsg(unsigned int msgId, void * data, uint16_t len, int appId)
+{
+    cprBuffer_t *msg;
+    static const char fname[] = "ccappPostMsg";
+    cpr_status_e retval = CPR_SUCCESS;
+
+    msg = (cprBuffer_t *) cpr_malloc(len);
+    if (msg == NULL) {
+        CCAPP_ERROR(DEB_F_PREFIX"failed to allocate message.\n",
+               DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+        return CPR_FAILURE;
+    }
+
+    memcpy(msg, data, len);
+
+    if ((retval=ccappTaskSendMsg(msgId, msg, len, appId)) == CPR_FAILURE) {
+        cpr_free(msg);
+    }
+
+    return retval;
+}
+
+/**
+ *
+ * CC Provider wrapper for cprSendMessage
+ *
+ * @param   cmd - Command
+ * @param   msg - msg ptr
+ * @param   len - len of msg
+ * @param   usr -
+ *
+ * @return  CPR_SUCCESS/CPR_FAILURE
+ *
+ * @pre     msg is a malloc mem ptr
+ */
+cpr_status_e
+ccappTaskSendMsg (uint32_t cmd, void *msg, uint16_t len, uint32_t UsrInfo)
+{
+    phn_syshdr_t *syshdr;
+
+    syshdr = (phn_syshdr_t *) cprGetSysHeader(msg);
+    if (!syshdr) {
+        return CPR_FAILURE;
+    }
+    syshdr->Cmd = cmd;
+    syshdr->Len = len;
+    syshdr->Usr.UsrInfo = UsrInfo;
+
+    if (cprSendMessage(ccapp_msgq , (cprBuffer_t*)msg, (void **)&syshdr) == CPR_FAILURE) {
+        cprReleaseSysHeader(syshdr);
+        return CPR_FAILURE;
+    }
+    return CPR_SUCCESS;
+}
+
+/**
+ *
+ * CCApp Provider main routine.
+ *
+ * @param   arg - CCApp msg queue
+ *
+ * @return  void
+ *
+ * @pre     None
+ */
+void CCApp_task(void * arg)
+{
+    static const char fname[] = "CCApp_task";
+    phn_syshdr_t   *syshdr = NULL;
+    appListener *listener = NULL;
+    void * msg;
+
+    //initialize the listener list
+    sll_lite_init(&sll_list);
+
+    CCAppInit();
+
+    while (1) {
+        msg = cprGetMessage(ccapp_msgq, TRUE, (void **) &syshdr);
+        if ( msg) {
+            CCAPP_DEBUG(DEB_F_PREFIX"Received Cmd[%d] for app[%d]\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                    syshdr->Cmd, syshdr->Usr.UsrInfo);
+
+            listener = getCcappListener(syshdr->Usr.UsrInfo);
+            if (listener != NULL) {
+                (* ((appListener)(listener)))(msg, syshdr->Cmd);
+            } else {
+                CCAPP_DEBUG(DEB_F_PREFIX"Event[%d] doesn't have a dedicated listener.\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                        syshdr->Usr.UsrInfo);
+            }
+            cprReleaseSysHeader(syshdr);
+            cpr_free(msg);
+        }
+    }
+}
+
+
+
diff --git a/libs/sipcc/core/ccapp/ccapp_task.h b/libs/sipcc/core/ccapp/ccapp_task.h
new file mode 100644 (file)
index 0000000..e2792a3
--- /dev/null
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sll_lite.h"
+
+//Define app id for ccapp task
+#define CCAPP_CCPROVIER     1
+#define CCAPP_MSPROVIDER    2
+
+typedef void(* appListener) (void *message, int type);
+typedef struct {
+    sll_lite_node_t node;
+    int type;
+    appListener *listener_p;
+} listener_t;
+
+extern void addCcappListener(appListener* listener, int type);
+appListener *getCcappListener(int type);
+cpr_status_e ccappTaskSendMsg (uint32_t cmd, void *msg, uint16_t len, uint32_t usrInfo);
diff --git a/libs/sipcc/core/ccapp/ccprovider.c b/libs/sipcc/core/ccapp/ccprovider.c
new file mode 100755 (executable)
index 0000000..f6c2d94
--- /dev/null
@@ -0,0 +1,2238 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <limits.h>
+
+#include "CCProvider.h"
+#include "ccSession.h"
+#include "ccsip_task.h"
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_ipc.h"
+#include "phone.h"
+#include "phntask.h"
+#include "ccapi.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "dialplanint.h"
+#include "configmgr.h"
+#include "prot_configmgr.h"
+#include "lsm.h"
+#include "sip_common_regmgr.h"
+#include "sessionHash.h"
+#include "pres_sub_not_handler.h"
+#include "cc_call_listener.h"
+#include "cc_service_listener.h"
+#include "cc_config.h"
+#include "cc_device_listener.h"
+#include "cc_service_listener.h"
+#include "plat_api.h"
+#include "cc_info_listener.h"
+#include "cc_blf_listener.h"
+#include "platform_api.h"
+#include "ccapp_task.h"
+#include "ccapi.h"
+#include "capability_set.h"
+#include "plat_debug.h"
+
+#include "config_api.h"
+#include "ccapi_call_info.h"
+#include "ccapi_call_listener.h"
+#include "ccapi_snapshot.h"
+#include "ccapi_device.h"
+#include "ccapi_line_listener.h"
+#include "ccapi_device_listener.h"
+#include "cc_device_manager.h"
+#include "call_logger.h"
+#include "subscription_handler.h"
+#include "ccapi_device_info.h"
+#include "conf_roster.h"
+#include "reset_api.h"
+#include "prlog.h"
+
+/*---------------------------------------------------------
+ *
+ * Definitions
+ *
+ */
+#define NOTIFY_CALL_STATUS (data->state == OFFHOOK? "OFFHOOK" : \
+        data->state == ONHOOK? "ONHOOK" : \
+        data->state == RINGOUT? "RINGOUT" : \
+        data->state == RINGIN? "RINGIN" : \
+        data->state == PROCEED ? "PROCEED" : \
+        data->state == CONNECTED ? "CONNECTED" : \
+        data->state == HOLD ? "HOLD" : \
+        data->state == REMHOLD ? "REMHOLD" : \
+        data->state == RESUME ? "RESUME" : \
+        data->state == BUSY ? "BUSY" : \
+        data->state == REORDER ? "REORDER" : \
+        data->state == CONFERENCE ? "CONFERENCE" : \
+        data->state == DIALING ? "DIALING" : \
+        data->state == REMINUSE ? "REMINUSE" : \
+        data->state == HOLDREVERT ? "HOLDREVERT" :\
+        data->state == WHISPER ? "WHISPER" : \
+        data->state == PRESERVATION ? "PRESERVATION" : \
+        data->state == WAITINGFORDIGITS ? "WAITINGFORDIGITS" : \
+        "Unknown state")
+
+#define CCAPP_TASK_CMD_PRINT(arg) ((arg) == CCAPP_SERVICE_CMD ?  "CCAPP_SERVICE_CMD" : \
+                (arg) == CCAPP_CREATE_SESSION ? "CCAPP_CREATE_SESSION" : \
+                (arg) == CCAPP_CLOSE_SESSION ? "CCAPP_CLOSE_SESSION" : \
+                (arg) == CCAPP_INVOKE_FEATURE ? "CCAPP_INVOKE_FEATURE" : \
+                (arg) == CCAPP_SESSION_UPDATE ? "CCAPP_SESSION_UPDATE" : \
+                (arg) == CCAPP_FEATURE_UPDATE ? "CCAPP_FEATURE_UPDATE" : \
+                (arg) == CCAPP_UPDATELINES ? "CCAPP_UPDATELINES" : \
+                (arg) == CCAPP_FAILOVER_IND ? "CCAPP_FAILOVER_IND" : \
+                (arg) == CCAPP_FALLBACK_IND ? "CCAPP_FALLBACK_IND" : \
+                (arg) == CCAPP_MODE_NOTIFY ? "CCAPP_MODE_NOTIFY" : \
+                (arg) == CCAPP_SHUTDOWN_ACK ? "CCAPP_SHUTDOWN_ACK" : \
+                (arg) == CCAPP_REG_ALL_FAIL ? "CCAPP_REG_ALL_FAIL" : \
+                (arg) == CCAPP_INVOKEPROVIDER_FEATURE ?  "CCAPP_INVOKEPROVIDER_FEATURE" : \
+                (arg) == CCAPP_SEND_INFO ?  "CCAPP_SEND_INFO" : \
+                (arg) == CCAPP_RCVD_INFO ?  "CCAPP_RCVD_INFO" : \
+                (arg) == CCAPP_LOGOUT_RESET ?  "CCAPP_LOGOUT_RESET" : \
+                (arg) == CCAPP_SESSION_MGMT ?  "CCAPP_SESSION_MGMT" : \
+                (arg) == CCAPP_THREAD_UNLOAD ?  "CCAPP_THREAD_UNLOAD" : \
+                (arg) == CCAPP_SESSION_MGMT ?  "CCAPP_SESSION_MGMT" : \
+                "Unknown Cmd")
+
+#define APP_ERR_MSG err_msg
+
+#define MAX_REASON_LENGTH      MAX_SIP_REASON_LENGTH
+#define MAX_SESSION_PARAM_LEN  128
+
+/* UNKNOWN_PHRASE_STR should equal the value returned
+ *  if the phrase code is not found in the dictionary.
+ *  For CIUS, this is found in LocalizedStrings.java */
+#define UNKNOWN_PHRASE_STR "???"
+#define UNKNOWN_PHRASE_STR_SIZE 3
+
+/*---------------------------------------------------------
+ *
+ * Local Variables
+ *
+ */
+
+/*---------------------------------------------------------
+ *
+ * Global Variables
+ *
+ */
+static CCAppGlobal_t gCCApp;
+cc_int32_t g_CCAppDebug=TRUE;
+cc_int32_t g_CCLogDebug=TRUE;
+cc_int32_t g_NotifyCallDebug=TRUE;
+cc_int32_t g_NotifyLineDebug=TRUE;
+
+cc_srv_ctrl_req_t reset_type;
+
+extern cprMsgQueue_t ccapp_msgq;
+extern cprThread_t ccapp_thread;
+extern boolean platform_initialized;
+extern void resetReady();
+extern void resetNotReady();
+extern int sendResetUpdates;
+
+extern boolean apply_config;
+
+extern  char g_new_signaling_ip[];
+
+extern int configFileDownloadNeeded;
+cc_action_t pending_action_type = NO_ACTION;
+
+
+/*--------------------------------------------------------------------------
+ * External function prototypes
+ *--------------------------------------------------------------------------
+ */
+extern void send_protocol_config_msg(void);
+extern void gsm_shutdown(void);
+extern void sip_shutdown(void);
+extern void dp_shutdown(void);
+extern void MiscAppTaskShutdown(void);
+extern void lsm_init(void);
+extern void fsm_init(void);
+extern void fim_init(void);
+extern void SIPTaskPostRestart(boolean restart);
+extern void ccsip_register_cancel(boolean cancel_reg, boolean backup_proxy);
+extern void notify_register_update(int last_available_line);
+extern void getLineIdAndCallId (line_t *line_id, callid_t *call_id);
+extern void set_default_video_pref(int pref);
+extern void set_next_sess_video_pref(int pref);
+extern void cc_media_update_native_video_support(boolean val);
+extern void cc_media_update_video_cap(boolean val);
+extern void cc_media_update_video_txcap(boolean val);
+
+session_data_t * getDeepCopyOfSessionData(session_data_t *data);
+static void ccappUpdateSessionData(session_update_t *sessUpd);
+static void ccappFeatureUpdated (feature_update_t *featUpd);
+void destroy_ccapp_thread();
+void ccpro_handleserviceControlNotify();
+
+/*---------------------------------------------------------
+ *
+ * Function declarations
+ *
+ */
+
+cc_service_state_t mapProviderState(cc_reg_state_t state)
+{
+   switch(state) {
+     case CC_INSERVICE:
+        return CC_STATE_INS;
+     case CC_CREATED_IDLE:
+        return CC_STATE_IDLE;
+     case CC_OOS_REGISTERING:
+     case CC_OOS_FAILOVER:
+     case CC_OOS_IDLE:
+        default:
+        return CC_STATE_OOS;
+
+   }
+}
+
+static void ccapp_hlapi_update_device_reg_state() {
+        g_deviceInfo.ins_state = mapProviderState(gCCApp.state);
+        g_deviceInfo.ins_cause = gCCApp.cause;
+        g_deviceInfo.cucm_mode = gCCApp.mode;
+       ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_STATE, CC_DEVICE_ID);
+}
+
+void ccpro_handleINS() {
+      registration_processEvent(EV_CC_INSERVICE);
+}
+
+void ccpro_handleOOS() {
+      switch(gCCApp.cause){
+         case CC_CAUSE_REG_ALL_FAILED:
+            registration_processEvent(EV_CC_OOS_REG_ALL_FAILED);
+            break;
+         case CC_CAUSE_FAILOVER:
+            registration_processEvent(EV_CC_OOS_FAILOVER);
+            break;
+         case CC_CAUSE_FALLBACK:
+            registration_processEvent(EV_CC_OOS_FALLBACK);
+            break;
+         case CC_CAUSE_SHUTDOWN:
+            registration_processEvent(EV_CC_OOS_SHUTDOWN_ACK);
+            break;
+      }
+}
+
+cc_boolean is_action_to_be_deferred(cc_action_t action) {
+    if (CCAPI_DeviceInfo_isPhoneIdle(CC_DEVICE_ID) == FALSE) {
+        pending_action_type = action;
+        DEF_DEBUG("Action deferred=%d", action);
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
+void perform_deferred_action() {
+    cc_action_t  temp_action = pending_action_type;
+
+    if (is_action_to_be_deferred(pending_action_type) == TRUE) {
+        return;
+    }
+
+    pending_action_type = NO_ACTION;
+    DEF_DEBUG("Perform deferred action=%d", temp_action);
+
+    if (temp_action == RESET_ACTION || temp_action == RESTART_ACTION) {
+        ccpro_handleserviceControlNotify();
+    } else if (temp_action == RE_REGISTER_ACTION) {
+        CCAPI_Service_reregister(g_dev_hdl, g_dev_name, g_cfg_p, g_compl_cfg);
+    } else if (temp_action == STOP_ACTION) {
+        CCAPI_Service_stop();
+    } else if (temp_action == DESTROY_ACTION) {
+        CCAPI_Service_destroy();
+    }
+}
+
+void ccpro_handleserviceControlNotify() {
+    cc_action_t  temp_action  = NO_ACTION;
+    if (reset_type == CC_DEVICE_RESET) {
+        temp_action = RESET_ACTION;
+    } else if (reset_type == CC_DEVICE_RESTART) {
+        temp_action = RESTART_ACTION;
+    }
+
+    if ((reset_type != CC_DEVICE_ICMP_UNREACHABLE) &&
+        is_action_to_be_deferred(temp_action) == TRUE) {
+        return;
+    }
+
+
+    if (reset_type == CC_DEVICE_RESET) {
+        resetRequest();
+    } else if (reset_type == CC_DEVICE_RESTART) {
+        registration_processEvent(EV_CC_DO_SOFT_RESET);
+    }
+}
+
+/**
+ * Returns the registration state
+ */
+cc_reg_state_t ccapp_get_state() {
+   return gCCApp.state;
+}
+
+/**
+ *
+ * CCApp Provider init routine.
+ *
+ * @param   none
+ *
+ * @return  void
+ *
+ * @pre     None
+ */
+void CCAppInit()
+{
+  ccProvider_state_t srvcState;
+
+    gCCApp.state = CC_CREATED_IDLE;
+    gCCApp.cause = CC_CAUSE_NONE;
+    gCCApp.mode = CC_MODE_INVALID;
+    gCCApp.cucm_mode = NONE_AVAIL;
+    if (platThreadInit("CCApp_Task") != 0) {
+        return;
+    }
+
+    /*
+     * Adjust relative priority of CCApp thread.
+     */
+    (void) cprAdjustRelativeThreadPriority(CCPROVIDER_THREAD_RELATIVE_PRIORITY);
+
+  debug_bind_keyword("cclog", &g_CCLogDebug);
+  srvcState.cause = gCCApp.cause;  // XXX set but not used
+  srvcState.mode = gCCApp.mode;  // XXX set but not used
+  (void) srvcState;
+
+  //Add listeners
+  //CCProvider lister
+  DEF_DEBUG(DEB_F_PREFIX"Add ccp listener: type%d", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAppInit"),
+          CCAPP_CCPROVIER);
+
+  addCcappListener((appListener *)ccp_handler, CCAPP_CCPROVIER);
+
+}
+
+/*
+ *  Function:    processProviderEvent
+ *
+ *  Description: inform session events to CCApp
+ *
+ *  Parameters:
+ *    call_id - call identifier
+ *    line_id - line identifier
+ *
+ *  Returns:     none
+ */
+void
+processProviderEvent (line_t line_id, unsigned int event, int data)
+{
+  static const char fname[] = "processProviderEvent";
+  callid_t call_id = 0;
+
+    DEF_DEBUG(DEB_F_PREFIX"line=%d event=%d data=%d", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                line_id, event, data);
+    switch(event) {
+         case DEVICE_FEATURE_CFWD:
+             getLineIdAndCallId(&line_id, &call_id);
+             if (lsm_is_line_available(line_id, FALSE) == FALSE) {
+                 //Send onhook to indicate session closure.
+                ccsnap_gen_callEvent(CCAPI_CALL_EV_STATE, CREATE_CALL_HANDLE(line_id, call_id));
+                 lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT);
+                 break;
+             }
+
+             cc_feature(CC_SRC_UI, call_id, line_id, CC_FEATURE_CFWD_ALL, NULL);
+
+             break;
+         case DEVICE_SUPPORTS_NATIVE_VIDEO:
+             cc_media_update_native_video_support(data);
+             break;
+         case DEVICE_ENABLE_VIDEO:
+             cc_media_update_video_cap(data);
+             break;
+         case DEVICE_ENABLE_CAMERA:
+             cc_media_update_native_video_txcap(data);
+             break;
+    }
+}
+
+
+/*
+ *  Function:    CheckAndGetAvailableLine
+ *
+ *  Parameters:
+ *    line - line identifier
+ *    call - call identifier
+ *
+ *  Returns:  TRUE if a line is available.
+ */
+static boolean CheckAndGetAvailableLine(line_t *line, callid_t *call)
+{
+    /*
+     * case 1: line>0 & call>0: invoking this in an offhook session.
+     * case 2: line>0 & call=0: invoking this onhook with simple filter active
+     * case 3: line=0 & call=0: invoking this in onhook with all calls filter active
+     */
+    if ((*line > 0) && (*call == 0)) {
+        if (lsm_is_line_available(*line, FALSE) == FALSE) {
+            // since the session is not created yet, no need to close the session.
+            lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT);
+            return FALSE;
+        }
+    }
+    else if ((*line == 0) && (*call == 0)) {
+        *line = lsm_get_available_line(FALSE);
+        if (*line == 0 ) {
+            // since the session is not created yet, no need to close the session.
+            lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT);
+            return FALSE;
+        }
+    }
+    *call = (*call == 0) ? cc_get_new_call_id() : *call;
+    return TRUE;
+}
+
+/*
+ *  Function:    getVideoPref
+ *
+ *  Description: get video direction if specified
+ *
+ *  Parameters:
+ *    data - urn param
+ *
+ *  Returns:     sdp_direction_e - requested video direction
+ */
+sdp_direction_e getVideoPref( string_t data)
+{
+    if ( data == NULL ) {
+      return SDP_MAX_QOS_DIRECTIONS;
+    }
+
+    if ( strstr(data, "video=sendrecv")) {
+         return SDP_DIRECTION_SENDRECV;
+    } else if (strstr(data, "video=sendonly")) {
+         return SDP_DIRECTION_SENDONLY;
+    } else if (strstr(data, "video=recvonly")){
+         return SDP_DIRECTION_RECVONLY;
+    } else if (strstr(data, "video=none")){
+         return SDP_DIRECTION_INACTIVE;
+    } else {
+         return SDP_MAX_QOS_DIRECTIONS;
+    }
+}
+
+/*
+ *  Function: updateSessionVideoPref
+ *
+ *  Description: update video direction for the session only
+ *
+ *  Parameters:
+ *    line_id - line at which the session needs the video pref updated
+ *    call_id - callid for which the session needs the video pref updated
+ *    dir - video pref
+ *
+ *  Returns:
+ */
+static void updateSessionVideoPref( line_t line_id, callid_t call_id, sdp_direction_e video_pref)
+{
+    static const char fname[] = "updateSessionVideoPref";
+    cc_feature_data_t featdata;
+
+    DEF_DEBUG(DEB_L_C_F_PREFIX"updating to %d video capapbility %s",
+            DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, call_id, line_id, fname), video_pref,
+            (video_pref == SDP_DIRECTION_INACTIVE ? "disabled" : "enabled"));
+    featdata.caps.support_direction = video_pref;
+    cc_feature(CC_SRC_UI, call_id, line_id, CC_FEATURE_UPD_SESSION_MEDIA_CAP, &featdata);
+}
+
+/*
+ *  Function:    updateVideoPref
+ *
+ *  Description: update video direction if specified
+ *
+ *  Parameters:
+ *    line_id - line at which the session needs the video pref updated
+ *    call_id - callid for which the session needs the video pref updated
+ *    data - urn param
+ *
+ *  Returns:
+ */
+
+static void updateVideoPref( unsigned int event, line_t line_id, callid_t call_id, sdp_direction_e video_pref/*string_t data*/)
+{
+    static const char fname[] = "updateVideoPref";
+
+    if ( video_pref == SDP_MAX_QOS_DIRECTIONS ) {
+        // we are done no updates needed
+        return;
+    }
+
+    switch (event) {
+        case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+            if ( call_id == CC_NO_CALL_ID ) {
+                // This is the only means to update the default behavior
+                set_default_video_pref(video_pref);
+            } else {
+                updateSessionVideoPref(line_id, call_id, video_pref);
+            }
+            break;
+        case CC_FEATURE_DIALSTR:
+        case CC_FEATURE_DIAL:
+        case CC_FEATURE_SPEEDDIAL:
+        case CC_FEATURE_REDIAL:
+            if ( call_id == CC_NO_CALL_ID ) {
+                set_next_sess_video_pref(video_pref);
+            } else {
+                updateSessionVideoPref(line_id, call_id, video_pref);
+            }
+            break;
+        case CC_FEATURE_NEW_CALL:
+        case CC_FEATURE_OFFHOOK:
+        case CC_FEATURE_CONF:
+        case CC_FEATURE_B2BCONF:
+        case CC_FEATURE_XFER:
+            set_next_sess_video_pref(video_pref);
+            break;
+        case CC_FEATURE_RESUME:
+        case CC_FEATURE_ANSWER:
+            updateSessionVideoPref(line_id, call_id, video_pref);
+            break;
+        default:
+            CCAPP_DEBUG(DEB_L_C_F_PREFIX"event=%d. update to %d not supported",
+                DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, call_id, line_id, fname), event, video_pref);
+
+    }
+}
+
+/*
+ *  Function:    getDigits
+ *
+ *  Description: returns the first param before the ;
+ *
+ *  Parameters:
+ *    data - urn param
+ *    digits - memory to return the first param
+ *  Returns:
+ */
+void getDigits(string_t data, char *digits) {
+   char *endptr;
+   int len=0;
+
+   digits[0]=0;
+
+   if ( data !=NULL ) {
+       endptr = strchr(data,';');
+       if ( endptr ) {
+           len = endptr - data;
+       } else {
+           len = strlen(data);
+       }
+
+       if ( len) {
+           memcpy(digits, data, len);
+           digits[len] = 0;
+       }
+    }
+}
+
+/*
+ *  Function:    processSessionEvent
+ *
+ *  Description: inform session events to CCApp
+ *
+ *  Parameters:
+ *    call_id - call identifier
+ *    line_id - line identifier
+ *
+ *  Returns:     none
+ */
+void
+processSessionEvent (line_t line_id, callid_t call_id, unsigned int event, sdp_direction_e video_pref, ccSession_feature_t ccData)
+{
+    static const char fname[] = "processSessionEvent";
+    cc_feature_data_t featdata;
+    int instance = line_id;
+    boolean incoming = FALSE;
+    session_data_t * sess_data_p;
+    char digits[CC_MAX_DIALSTRING_LEN];
+    char* data = (char*)ccData.info;
+    char* data1 =(char*)ccData.info1;
+    long strtol_result;
+    char *strtol_end;
+
+    CCAPP_DEBUG(DEB_L_C_F_PREFIX"event=%d data=%s",
+                DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, call_id, line_id, fname), event,
+                ((event == CC_FEATURE_KEYPRESS) ? "..." : data));
+
+    memset(&featdata, 0, sizeof(cc_feature_data_t));
+    updateVideoPref(event, line_id, call_id, video_pref);
+    switch(event) {
+         case CC_FEATURE_ONHOOK:
+             getLineIdAndCallId(&line_id, &call_id);
+             CCAPP_DEBUG(DEB_F_PREFIX"CC_FEATURE_ONHOOK = %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data);
+             cc_onhook_ext(CC_SRC_UI, call_id, line_id, FALSE,
+                         (data && (strncasecmp(data, "ACTIVECALLS", sizeof("ACTIVECALLS")) == 0))?
+                             CC_REASON_ACTIVECALL_LIST: CC_REASON_NULL );
+            break;
+         case CC_FEATURE_KEYPRESS:
+             dp_int_update_keypress(line_id, call_id, (unsigned char)*data);
+             break;
+         case CC_FEATURE_BKSPACE:
+             dp_int_update_keypress(line_id, call_id, BKSP_KEY);
+             break;
+         case CC_FEATURE_CREATEOFFER:
+             featdata.session.sessionid = ccData.sessionid;
+             featdata.session.has_constraints = ccData.has_constraints;
+             cc_createoffer (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_CREATEOFFER, &featdata);
+             break;
+         case CC_FEATURE_CREATEANSWER:
+             featdata.session.sessionid = ccData.sessionid;
+             featdata.session.has_constraints = ccData.has_constraints;
+             cc_createanswer (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_CREATEANSWER, data, &featdata);
+             break;
+         case CC_FEATURE_SETLOCALDESC:
+             cc_setlocaldesc (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_SETLOCALDESC, ccData.action, data, &featdata);
+             break;
+         case CC_FEATURE_SETREMOTEDESC:
+             cc_setremotedesc (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_SETREMOTEDESC, ccData.action, data, &featdata);
+             break;
+         case CC_FEATURE_SETPEERCONNECTION:
+           PR_ASSERT(strlen(data) < PC_HANDLE_SIZE);
+           if (strlen(data) >= PC_HANDLE_SIZE)
+             return;
+
+           sstrncpy(featdata.pc.pc_handle, data, sizeof(featdata.pc.pc_handle));
+
+           cc_int_feature2(CC_MSG_SETPEERCONNECTION, CC_SRC_UI, CC_SRC_GSM,
+             call_id, (line_t)instance,
+             CC_FEATURE_SETPEERCONNECTION, &featdata);
+           break;
+         case CC_FEATURE_ADDSTREAM:
+           featdata.track.stream_id = ccData.stream_id;
+           featdata.track.track_id = ccData.track_id;
+           featdata.track.media_type = ccData.media_type;
+           cc_int_feature2(CC_MSG_ADDSTREAM, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_ADDSTREAM, &featdata);
+           break;
+         case CC_FEATURE_REMOVESTREAM:
+           featdata.track.stream_id = ccData.stream_id;
+           featdata.track.track_id = ccData.track_id;
+           featdata.track.media_type = ccData.media_type;
+           cc_int_feature2(CC_MSG_REMOVESTREAM, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_REMOVESTREAM, &featdata);
+           break;
+         case CC_FEATURE_ADDICECANDIDATE:
+           featdata.candidate.level = ccData.level;
+           sstrncpy(featdata.candidate.candidate, data, sizeof(featdata.candidate.candidate)-1);
+           sstrncpy(featdata.candidate.mid, data1, sizeof(featdata.candidate.mid)-1);
+           cc_int_feature2(CC_MSG_ADDCANDIDATE, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_ADDICECANDIDATE, &featdata);
+           break;
+         case CC_FEATURE_DIALSTR:
+             if (CheckAndGetAvailableLine(&line_id, &call_id) == TRUE) {
+                 getDigits(data, digits);
+                 if (strlen(digits) == 0) {
+                    //if dial string is empty then go offhook
+                    cc_offhook(CC_SRC_UI, call_id, line_id);
+                 } else {
+                 dp_int_init_dialing_data(line_id, call_id);
+                 dp_int_dial_immediate(line_id, call_id, TRUE,
+                                       (char *)digits, NULL, CC_MONITOR_NONE);
+                 }
+             }
+             break;
+
+         case CC_FEATURE_DIAL:
+             dp_int_dial_immediate(line_id, call_id, FALSE, NULL, NULL, CC_MONITOR_NONE);
+             break;
+         case CC_FEATURE_SPEEDDIAL:
+             /*
+              * BLF Pickup Line Selection:
+              * -------------------------
+              *
+              * case 1: line_id>0 & call_id>0 : BLF key press after going offhook on a given line.
+              * Check if the given line has available call capacity. If not, end the session
+              * and display STR_INDEX_NO_LINE_FOR_PICKUP toast.
+              *
+              * case 2: line_id>0 & call_id=0 : BLF key press w/o offhook when a simple filter is active.
+              * Check if the given line has available call capacity. If not, find the next available line.
+              * If no available line is found, display STR_INDEX_NO_LINE_FOR_PICKUP toast.
+              *
+              * case 3: line_id=0 & call_id=0 : BLF key press w/o offhook when a AllCalls filter is active.
+              * Find an available line. If no available line is found,
+              * display STR_INDEX_NO_LINE_FOR_PICKUP toast.
+              */
+             if (strncmp(data, CISCO_BLFPICKUP_STRING, (sizeof(CISCO_BLFPICKUP_STRING) - 1)) == 0) {
+                 if ((instance > 0) && (call_id > 0)) {
+                     if (lsm_is_line_available(instance, incoming) == FALSE) {
+                         session_update_t sessUpd;
+                         sessUpd.eventID = CALL_STATE;
+                         sessUpd.sessionID = createSessionId(line_id, call_id);
+                         sessUpd.sessType = SESSIONTYPE_CALLCONTROL;
+                         sessUpd.update.ccSessionUpd.data.state_data.state = evOnHook;
+                         sessUpd.update.ccSessionUpd.data.state_data.line_id = line_id;
+                         sessUpd.update.ccSessionUpd.data.state_data.cause = CC_CAUSE_NORMAL;
+                         ccappUpdateSessionData(&sessUpd);
+                         // Pass this update to Session Manager
+                         lsm_ui_display_notify_str_index(STR_INDEX_NO_LINE_FOR_PICKUP);
+                         break;
+                     }
+                 }
+                 else if ((instance > 0) && (call_id == 0)) {
+                     if (lsm_is_line_available(instance, incoming) == FALSE) {
+                         line_id = lsm_get_available_line(incoming);
+                         if (line_id == 0 ) {
+                             // since the session is not created yet, no need to close the session.
+                             lsm_ui_display_notify_str_index(STR_INDEX_NO_LINE_FOR_PICKUP);
+                             break;
+                         }
+                     }
+                 }
+                 else if ((instance == 0) && (call_id == 0)) {
+                     line_id = lsm_get_available_line(incoming);
+                     if (line_id == 0 ) {
+                         // since the session is not created yet, no need to close the session.
+                         lsm_ui_display_notify_str_index(STR_INDEX_NO_LINE_FOR_PICKUP);
+                         break;
+                      }
+                 }
+                 call_id = (call_id == 0) ? cc_get_new_call_id() : call_id;
+             }
+             /*
+              * SpeedDial Line Selection:
+              * ------------------------
+              *
+              * case 1: line_id>0 & call_id>0 : speeddial key press after going offhook on a given line.
+              * Simply use this line to make an outgoing call.
+              *
+              * case 2: line_id>0 & call_id=0 : SD key press w/o offhook when a simple filter is active.
+              * Check if the given line has available call capacity. If not,
+              * display STR_INDEX_ERROR_PASS_LIMIT toast.
+              *
+              * case 3: line_id=0 & call_id=0 : SD key press w/o offhook when a AllCalls filter is active.
+              * Find an available line. If no available line is found,
+              * display STR_INDEX_ERROR_PASS_LIMIT toast.
+              */
+             else if (CheckAndGetAvailableLine(&line_id, &call_id) == FALSE) {
+                 break;
+             }
+
+             getDigits(data,digits);
+
+             dp_int_init_dialing_data(line_id, call_id);
+             dp_int_dial_immediate(line_id, call_id, TRUE,
+                                   digits, NULL, CC_MONITOR_NONE);
+             break;
+         case CC_FEATURE_BLIND_XFER_WITH_DIALSTRING:
+             featdata.xfer.cause = CC_CAUSE_XFER_LOCAL_WITH_DIALSTRING;
+             sstrncpy(featdata.xfer.dialstring, (char *)data, CC_MAX_DIALSTRING_LEN);
+             cc_feature(CC_SRC_UI, call_id, (line_t)instance, CC_FEATURE_BLIND_XFER,
+                        &featdata);
+        break;
+         case CC_FEATURE_REDIAL:
+             if (line_id == 0) {
+                 /*
+                  * If line_id is zero, get the last dialed line.
+                  */
+                 line_id = dp_get_redial_line();
+                 if (line_id == 0) {
+                     break;
+                 }
+             }
+
+             /* if not an existing call, check capacity on the line */
+             if (call_id == CC_NO_CALL_ID) {
+                 if (lsm_is_line_available(line_id, FALSE) == FALSE) {
+                     /*
+                      * since the session is not created yet, no
+                      * need to close the session.
+                      */
+                     lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT);
+                     break;
+                     } else {
+                         /* we have capacity and call does not exist */
+                     call_id = cc_get_new_call_id();
+                 }
+             }
+             dp_int_do_redial(line_id, call_id);
+             break;
+
+         case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+             /* handled in updateVideoPref() just outside of the switch */
+             break;
+
+         case CC_FEATURE_NEW_CALL:
+         case CC_FEATURE_OFFHOOK:
+             if (lsm_is_line_available(line_id, FALSE) == FALSE) {
+                 //close the session
+                 CCAPP_DEBUG(DEB_F_PREFIX"CC_FEATURE_OFFHOOK: no available line.\n",
+                                             DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+                ccsnap_gen_callEvent(CCAPI_CALL_EV_STATE, CREATE_CALL_HANDLE(line_id, call_id));
+                 lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT);
+                 break;
+             }
+
+             if (call_id == 0) {
+               call_id = cc_get_new_call_id();
+             } else {
+               if (event == CC_FEATURE_NEW_CALL) {
+                   CCAPP_DEBUG(DEB_F_PREFIX"CC_FEATURE_NEW_CALL called with callid=%d\n",
+                           DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), call_id);
+               } else {
+                   CCAPP_DEBUG(DEB_F_PREFIX"CC_FEATURE_OFFHOOK called with callid=%d\n",
+                           DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), call_id);
+               }
+             }
+             cc_offhook(CC_SRC_UI, call_id, line_id);
+             break;
+
+         case CC_FEATURE_CFWD_ALL:
+             getLineIdAndCallId(&line_id, &call_id);
+
+             cc_feature(CC_SRC_UI, call_id, line_id, CC_FEATURE_CFWD_ALL, NULL);
+
+             break;
+
+         case CC_FEATURE_RESUME:
+         case CC_FEATURE_ANSWER:
+
+             sess_data_p = (session_data_t *)findhash(createSessionId(line_id, call_id));
+             if ( sess_data_p == NULL ) {
+               break;
+             }
+             if (sess_data_p->state == HOLD || sess_data_p->state == HOLDREVERT) {
+                 cc_feature(CC_SRC_UI, call_id, line_id, CC_FEATURE_RESUME, NULL);
+             } else {
+                 cc_feature(CC_SRC_UI, call_id, line_id, event, NULL);
+             }
+             break;
+
+         case CC_FEATURE_END_CALL:
+                    CCAPP_DEBUG(DEB_F_PREFIX"CC_FEATURE_ONHOOK = %s\n",
+                                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data);
+                    if (data && (strcmp(data, "ACTIVECALLS") == 0)) {
+                        getLineIdAndCallId(&line_id, &call_id);
+                        cc_onhook_ext(CC_SRC_UI, call_id, line_id, TRUE, CC_REASON_ACTIVECALL_LIST);
+                    } else {
+                        cc_feature(CC_SRC_UI, call_id, line_id, event, NULL);
+                    }
+                    break;
+
+        case CC_FEATURE_CONF:
+                    // for CUCM mode we will translate CONF to B2BCONF
+                    if ( gCCApp.mode == CC_MODE_CCM ) {
+                 if ( gCCApp.mode == CC_MODE_CCM && platGetFeatureAllowed(CC_SIS_B2B_CONF) ) {
+                     //CUCME can't support B2BCONF currently.
+                        event = CC_FEATURE_B2BCONF;
+                }
+                    }// DON'T ADD BREAK HERE. EVENT IS PASSED BELOW
+        case CC_FEATURE_B2BCONF:
+        case CC_FEATURE_XFER:
+                    getDigits(data,digits);
+                    if ( strlen(digits)) {
+                       cc_feature_data_t ftr_data;
+                       CCAPP_DEBUG(DEB_F_PREFIX"conf: sid=%s.\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),data);
+                       // if data is received we need to extract the call id
+                       // and send target_call_id in feature_data
+                       // Post the event on the original call ( received call id as string in data).
+                       if ( event == CC_FEATURE_B2BCONF ) {
+                           ftr_data.b2bconf.target_call_id = call_id;
+                       } else if ( event == CC_FEATURE_XFER ) {
+                           ftr_data.xfer.target_call_id = call_id;
+                       } else if ( event == CC_FEATURE_CONF) {
+                            //Legacy local conference.
+                           ftr_data.cnf.target_call_id = call_id;
+                       }
+
+                       errno = 0;
+                       strtol_result = strtol(digits, &strtol_end, 10);
+
+                       if (errno || digits == strtol_end || strtol_result < INT_MIN || strtol_result > INT_MAX) {
+                               CCAPP_ERROR(DEB_F_PREFIX"digits parse error %s.\n",DEB_F_PREFIX_ARGS(SIP_CC_PROV, __FUNCTION__), digits);
+                       } else {
+                               cc_feature(CC_SRC_UI, GET_CALLID((int) strtol_result), line_id, event, &ftr_data);
+                       }
+                       break;
+                    } else {
+                       CCAPP_DEBUG(DEB_F_PREFIX"conf: no sid.\n",DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+                    }// DON'T ADD BREAK HERE. EVENT IS PASSED BELOW
+
+         case CC_FEATURE_B2B_JOIN:
+         case CC_FEATURE_DIRTRXFR:
+         case CC_FEATURE_SELECT:
+         case CC_FEATURE_CANCEL:
+             cc_feature(CC_SRC_UI, call_id, line_id, event, NULL);
+             break;
+
+         case CC_FEATURE_HOLD:
+            if (data && ((strcmp(data, "TRANSFER") == 0) ||
+                (strcmp(data, "CONFERENCE") == 0) || (strcmp(data, "SWAP") == 0))) {
+                featdata.hold.call_info.data.hold_resume_reason = CC_REASON_SWAP;
+            } else {
+
+                featdata.hold.call_info.data.hold_resume_reason = CC_REASON_NONE;
+            }
+            featdata.hold.call_info.type = CC_FEAT_HOLD;
+            featdata.hold.msg_body.num_parts = 0;
+            cc_feature(CC_SRC_UI, call_id, line_id, event, &featdata);
+            break;
+
+         default:
+             break;
+    }
+}
+
+void CCAppShutdown()
+{
+}
+
+
+static char * ccapp_cmd_to_str(unsigned int cmd) {
+   switch (cmd) {
+     case CMD_INSERVICE:
+         return "CMD_INSERVICE";
+     case CMD_SHUTDOWN:
+         return "CMD_SHUTDOWN";
+     case CMD_RESTART:
+         return "CMD_RESTART";
+     case CMD_UNREGISTER_ALL_LINES:
+         return "CMD_UNREGISTER_ALL_LINES";
+     case CMD_REGISTER_ALL_LINES:
+         return "CMD_REGISTER_ALL_LINES";
+     default:
+         return "CMD_UNKNOWN";
+   }
+}
+/**
+ *
+ * CC Provider process service Cmds
+ *
+ * @param   cmd - Command
+ * @param   reason - reason
+ * @param   reasonStr - reason string
+ *
+ * @return  void
+ *
+ * @pre     None
+ */
+void CCApp_processCmds(unsigned int cmd, unsigned int reason, string_t reasonStr)
+{
+    static const char fname[] = "CCApp_processCmds";
+    CCAPP_DEBUG(DEB_F_PREFIX" Received Cmd %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV,
+           fname), ccapp_cmd_to_str(cmd));
+       switch (cmd) {
+         case CMD_INSERVICE:
+            ccsnap_device_init();
+            ccsnap_line_init();
+            gCCApp.state = CC_OOS_REGISTERING;
+            send_protocol_config_msg();
+            break;
+         case CMD_SHUTDOWN:
+         case CMD_UNREGISTER_ALL_LINES:
+            /* send a shutdown message to the SIP Task */
+            SIPTaskPostShutdown(SIP_EXTERNAL, reason, reasonStr);
+            break;
+         case CMD_RESTART:
+            SIPTaskPostRestart(TRUE);
+            break;
+         case CMD_BLF_INIT:
+            pres_sub_handler_initialized();
+            break;
+         default:
+            APP_ERR_MSG("CCApp_processCmds: Error: Unknown message %d\n", cmd);
+            break;
+       }
+}
+
+boolean isNoCallExist()
+{
+  hashItr_t itr;
+
+  hashItrInit(&itr);
+
+  return (hashItrNext(&itr)?FALSE:TRUE);
+}
+
+/**
+ * Make deep copy of session_data_t
+ */
+session_data_t * getDeepCopyOfSessionData(session_data_t *data)
+{
+   session_data_t *newData = (session_data_t *) cpr_malloc(sizeof(session_data_t));
+
+   if ( newData != NULL ) {
+       memset(newData, 0, sizeof(session_data_t));
+
+       if ( data != NULL ) {
+           *newData = *data;
+           newData->ref_count = 1;
+           newData->clg_name =  strlib_copy(data->clg_name);
+           newData->clg_number =  strlib_copy(data->clg_number);
+           newData->cld_name =  strlib_copy(data->cld_name);
+           newData->cld_number =  strlib_copy(data->cld_number);
+           newData->alt_number =  strlib_copy(data->alt_number);
+           newData->orig_called_name =  strlib_copy(data->orig_called_name);
+           newData->orig_called_number =  strlib_copy(data->orig_called_number);
+           newData->last_redir_name =  strlib_copy(data->last_redir_name);
+           newData->last_redir_number =  strlib_copy(data->last_redir_number);
+           newData->plcd_name =  strlib_copy(data->plcd_name);
+           newData->plcd_number =  strlib_copy(data->plcd_number);
+           newData->status =  strlib_copy(data->status);
+           calllogger_copy_call_log(&newData->call_log, &data->call_log);
+       } else {
+           newData->ref_count = 1;
+           newData->state = ONHOOK;
+           newData->security = CC_SECURITY_NONE;
+           newData->policy = CC_POLICY_NONE;
+           newData->clg_name =  strlib_empty();
+           newData->clg_number = strlib_empty();
+           newData->cld_name =  strlib_empty();
+           newData->cld_number = strlib_empty();
+           newData->alt_number = strlib_empty();
+           newData->orig_called_name = strlib_empty();
+           newData->orig_called_number = strlib_empty();
+           newData->last_redir_name = strlib_empty();
+           newData->last_redir_number = strlib_empty();
+           newData->plcd_name =  strlib_empty();
+           newData->plcd_number =  strlib_empty();
+           newData->status = strlib_empty();
+           calllogger_init_call_log(&newData->call_log);
+       }
+
+   }
+   return newData;
+}
+
+
+/**
+ *
+ * CCApp Provider search for session and deepfree data
+ *
+ * @return  ptr to session data
+ *
+ * @pre     None
+ */
+
+void cleanSessionData(session_data_t *data)
+{
+   if ( data != NULL ) {
+       strlib_free(data->clg_name);
+        data->clg_name = strlib_empty();
+       strlib_free(data->clg_number);
+        data->clg_number = strlib_empty();
+       strlib_free(data->alt_number);
+        data->alt_number = strlib_empty();
+       strlib_free(data->cld_name);
+        data->cld_name = strlib_empty();
+       strlib_free(data->cld_number);
+        data->cld_number = strlib_empty();
+       strlib_free(data->orig_called_name);
+        data->orig_called_name = strlib_empty();
+       strlib_free(data->orig_called_number);
+        data->orig_called_number = strlib_empty();
+       strlib_free(data->last_redir_name);
+        data->last_redir_name = strlib_empty();
+       strlib_free(data->last_redir_number);
+        data->last_redir_number = strlib_empty();
+       strlib_free(data->plcd_name);
+        data->plcd_name = strlib_empty();
+       strlib_free(data->plcd_number);
+        data->plcd_number = strlib_empty();
+       strlib_free(data->status);
+        data->status = strlib_empty();
+        calllogger_free_call_log(&data->call_log);
+    }
+}
+
+/**
+ *
+ * CCApp Provider check for CONNECTED calls for preservation
+ *
+ * @return  boolean - do we need to move in call preservation phase
+ *
+ * @pre     None
+ */
+
+boolean ccappPreserveCall()
+{
+    static const char fname[] = "ccappPreserveCall";
+    hashItr_t itr;
+    session_data_t *data;
+    boolean retVal = FALSE;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"called\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+    hashItrInit(&itr);
+    while ( (data = (session_data_t*)hashItrNext(&itr)) != NULL ) {
+      if ( data->state == CONNECTED || data->state == PRESERVATION ) {
+       // need to wait for this call to end.
+        CCAPP_DEBUG(DEB_F_PREFIX"inPreservation = true\n",
+            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+        gCCApp.inPreservation = TRUE;
+        gCCApp.preservID = data->sess_id;
+        capset_get_allowed_features(gCCApp.mode, PRESERVATION, data->allowed_features);
+       ccsnap_gen_callEvent(CCAPI_CALL_EV_PRESERVATION, CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id));
+        retVal = TRUE;
+      } else {
+        // End this call now
+        CCAPP_DEBUG(DEB_F_PREFIX"ending call %x\n",
+            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),  data->sess_id);
+        /*
+         * Note that for calls in state such as RIU, HOLD etc.
+         * we must send ON hook event (cc_onhook) instead of
+         * CC_FEATURE_END_CALL because corrsponding state machines
+         * handle only onhook event to clean up calls.
+         */
+        cc_onhook(CC_SRC_UI, GET_CALLID(data->sess_id),
+                  GET_LINEID(data->sess_id), TRUE);
+
+      }
+    }
+
+    return retVal;
+}
+
+/**
+ *
+ * CCApp Provider check if its safe to proceed with failover/fallback
+ *
+ * @return  void - do we need to move in call preservation phase
+ *
+ * @pre     None
+ */
+
+void proceedWithFOFB()
+{
+  static const char fname[] = "proceedWithFOFB";
+
+  CCAPP_DEBUG(DEB_F_PREFIX"called. preservation=%d in cucm mode=%s \n",
+        DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+        gCCApp.inPreservation,
+        gCCApp.cucm_mode == FAILOVER ? "FAILOVER":
+        gCCApp.cucm_mode == FALLBACK ? "FALLBACK":
+        gCCApp.cucm_mode == NO_CUCM_SRST_AVAILABLE ?
+            "NO_CUCM_SRST_AVAILABLE": "NONE");
+  gCCApp.state = CC_OOS_REGISTERING;
+
+  switch(gCCApp.cucm_mode)
+  {
+    case FAILOVER:
+      cc_fail_fallback_sip(CC_SRC_UI, RSP_START, CC_REG_FAILOVER_RSP, TRUE);
+      gCCApp.cause = CC_CAUSE_FAILOVER;
+    break;
+
+    case FALLBACK:
+      cc_fail_fallback_sip(CC_SRC_UI, RSP_START, CC_REG_FALLBACK_RSP, TRUE);
+      gCCApp.cause = CC_CAUSE_FALLBACK;
+    break;
+
+    case NO_CUCM_SRST_AVAILABLE:
+      gCCApp.cause = CC_CAUSE_REG_ALL_FAILED;
+      gCCApp.state = CC_OOS_IDLE;
+    break;
+
+    default:
+    break;
+  }
+
+  // Notify OOS state to Session Manager
+  switch (mapProviderState(gCCApp.state)) {
+  case CC_STATE_OOS:
+      ccpro_handleOOS();
+      break;
+  default:
+      break;
+  }
+  ccapp_hlapi_update_device_reg_state();
+}
+/**
+ *
+ * ccappHandleRegUpdates handles reg state changes.
+ *
+ * @param   featUpd - feature update with reg state msg
+ *
+ * @return  void
+ *
+ * @pre     None
+ */
+void ccappHandleRegStateUpdates(feature_update_t *featUpd)
+{
+  static const char fname[] = "ccappHandleRegStateUpdates";
+
+  CCAPP_DEBUG(DEB_F_PREFIX"called. feature=%d=%s, state=%d\n",
+       DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), featUpd->featureID, CCAPP_TASK_CMD_PRINT(featUpd->featureID), gCCApp.state);
+  gCCApp.cause = CC_CAUSE_NONE;
+
+  // Update state varaibles to track state
+  switch (featUpd->featureID)
+  {
+     case CCAPP_MODE_NOTIFY:
+        gCCApp.mode = featUpd->update.ccFeatUpd.data.line_info.info;
+        CCAPP_DEBUG(DEB_F_PREFIX"called. gCCApp.mode= %d gCCApp.state=%d. Returning\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), gCCApp.mode, gCCApp.state);
+        return;
+
+     case CCAPP_FAILOVER_IND:
+        gCCApp.state = CC_OOS_FAILOVER;
+        gCCApp.cucm_mode = FAILOVER;
+        gCCApp.cause = CC_CAUSE_FAILOVER;
+        if ( featUpd->update.ccFeatUpd.data.line_info.info == CC_TYPE_CCM ){
+           gCCApp.mode = CC_MODE_CCM;
+        }
+
+        else if (featUpd->update.ccFeatUpd.data.line_info.info == 3) {
+           gCCApp.mode = CC_MODE_NONCCM;
+        }
+
+        if ( ccappPreserveCall() == FALSE) {
+          gCCApp.state = CC_OOS_REGISTERING;
+          cc_fail_fallback_sip(CC_SRC_UI, RSP_START, CC_REG_FAILOVER_RSP, FALSE);
+        }
+        break;
+
+     case CCAPP_FALLBACK_IND:
+        gCCApp.cucm_mode = FALLBACK;
+        if ( featUpd->update.ccFeatUpd.data.line_info.info == CC_TYPE_CCM ){
+           gCCApp.mode = CC_MODE_CCM;
+        }
+        if ( isNoCallExist() ) {
+           gCCApp.state = CC_OOS_REGISTERING;
+           gCCApp.cause = CC_CAUSE_FALLBACK;
+           cc_fail_fallback_sip(CC_SRC_UI, RSP_START, CC_REG_FALLBACK_RSP, FALSE);
+        }
+        break;
+
+      case CCAPP_SHUTDOWN_ACK:
+        gCCApp.state = CC_OOS_IDLE;
+        gCCApp.cucm_mode = NONE_AVAIL;
+        gCCApp.inPreservation = FALSE;
+        gCCApp.cause = CC_CAUSE_SHUTDOWN;
+
+        break;
+      case CCAPP_REG_ALL_FAIL:
+        gCCApp.state = CC_OOS_IDLE;
+        gCCApp.cucm_mode = NO_CUCM_SRST_AVAILABLE;
+        gCCApp.inPreservation = FALSE;
+        if (ccappPreserveCall() == FALSE) {
+            gCCApp.cause = CC_CAUSE_REG_ALL_FAILED;
+        } else {
+            gCCApp.cause = CC_CAUSE_FAILOVER;
+        }
+        break;
+
+      case CCAPP_LOGOUT_RESET:
+        gCCApp.state = CC_OOS_IDLE;
+        gCCApp.cucm_mode = NONE_AVAIL;
+        /*gCCApp.inFailover = FALSE;
+        gCCApp.inFallback = FALSE;*/
+        gCCApp.inPreservation = FALSE;
+        gCCApp.cause = CC_CAUSE_LOGOUT_RESET;
+        break;
+    }
+    // Notify INS/OOS state to Session Manager if required
+    CCAPP_DEBUG(DEB_F_PREFIX"called. service_state=%d, mode=%d, cause=%d\n",
+            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+            mapProviderState(gCCApp.state),
+            gCCApp.mode,
+            gCCApp.cause);
+    switch (mapProviderState(gCCApp.state)) {
+    case CC_STATE_INS:
+        ccpro_handleINS();
+        break;
+    case CC_STATE_OOS:
+        ccpro_handleOOS();
+        break;
+    default:
+        break;
+    }
+    ccapp_hlapi_update_device_reg_state();
+}
+
+/*
+ * this function determine if the called number (i.e., dialed number) contains
+ * string x-cisco-serviceuri-cfwdall.
+ */
+static boolean ccappCldNumIsCfwdallString(string_t cld_number) {
+    static char cfwdAllString[STATUS_LINE_MAX_LEN] = "x-cisco-serviceuri-cfwdall";
+
+    if (strncmp(cld_number, cfwdAllString, strlen(cfwdAllString)) == 0) {
+        /* The Called Party number is the Call Forward all URI */
+        return ((int) TRUE);
+    }
+    return ((int) FALSE);
+}
+
+/**
+ * Api to update mute state of connected call
+ */
+cc_call_handle_t ccappGetConnectedCall(){
+    session_data_t * data;
+    hashItr_t itr;
+
+    hashItrInit(&itr);
+    while ( (data = (session_data_t*)hashItrNext(&itr)) != NULL ) {
+        if( data->state == CONNECTED ) {
+            return CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id);
+        }
+    }
+    return 0;
+}
+
+/**
+ *
+ * CCApp Provider cache session data to hashTable routine
+ *
+ * @param   sessUpd - Session update from CC
+ *
+ * @return  void
+ *
+ * @pre     None
+ */
+
+static void ccappUpdateSessionData (session_update_t *sessUpd)
+{
+    static const char fname[] = "ccappUpdateSessionData";
+       session_data_t * data = (session_data_t *)findhash(sessUpd->sessionID), *sess_data_p;
+    boolean createdSessionData = TRUE;
+    cc_deviceinfo_ref_t  handle = 0;
+    boolean previouslyInConference = FALSE;
+
+    if ( data == NULL ) {
+        cc_call_state_t call_state = sessUpd->update.ccSessionUpd.data.state_data.state;
+
+        if ( ( sessUpd->eventID == CALL_INFORMATION ) ||
+                ( sessUpd->eventID == CALL_STATE || sessUpd->eventID == CALL_NEWCALL
+                || sessUpd->eventID == CREATE_OFFER || sessUpd->eventID == CREATE_ANSWER
+                || sessUpd->eventID == SET_LOCAL_DESC  || sessUpd->eventID == SET_REMOTE_DESC
+                || sessUpd->eventID == REMOTE_STREAM_ADD)) {
+
+            CCAPP_DEBUG(DEB_F_PREFIX"CALL_SESSION_CREATED for session id 0x%x event is 0x%x \n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sessUpd->sessionID,
+                    sessUpd->eventID);
+
+            if (sessUpd->eventID == CALL_INFORMATION  ) {
+                call_state = RINGIN;
+
+            } else {
+                if ( sessUpd->update.ccSessionUpd.data.state_data.state == ONHOOK ) {
+                    CCAPP_DEBUG(DEB_F_PREFIX"NOT CREATING Session Ignoring event %d sessid %x as state is ONHOOK\n",
+                            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sessUpd->eventID, sessUpd->sessionID );
+                    //Even session data is not created, we need to send ONHOOK to application to terminate call.
+                    createdSessionData = FALSE;
+                }
+                call_state = sessUpd->update.ccSessionUpd.data.state_data.state;
+            }
+            //Create a new call.
+            if (createdSessionData == TRUE) {
+                cc_call_handle_t callHandle = CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID);
+                //<Added for automation test purpose, <CallBegin>
+                NOTIFY_CALL_DEBUG(DEB_NOTIFY_PREFIX"[line=%d][call.ref=%d]\n",
+                                "CallBegin", GET_LINE_ID(callHandle), GET_CALL_ID(callHandle));
+                //>
+        data = cpr_malloc(sizeof(session_data_t));
+        if ( data == NULL ) {
+            APP_ERR_MSG("ccappUpdateSessionData Error: cpr_malloc failed for session data\n");
+            return;
+        }
+        //Populate the session hash data the first time.
+        memset(data, 0, sizeof(session_data_t));
+        data->sess_id = sessUpd->sessionID;
+                               data->state = call_state;
+        data->line = sessUpd->update.ccSessionUpd.data.state_data.line_id;
+        if (sessUpd->eventID == CALL_NEWCALL || sessUpd->eventID == CREATE_OFFER ||
+            sessUpd->eventID == CREATE_ANSWER || sessUpd->eventID == SET_LOCAL_DESC ||
+            sessUpd->eventID == SET_REMOTE_DESC || sessUpd->eventID == REMOTE_STREAM_ADD ) {
+            data->attr = sessUpd->update.ccSessionUpd.data.state_data.attr;
+            data->inst = sessUpd->update.ccSessionUpd.data.state_data.inst;
+        }
+        data->cause = sessUpd->update.ccSessionUpd.data.state_data.cause;
+        data->clg_name = strlib_empty();
+        data->clg_number =  strlib_empty();
+        data->alt_number = strlib_empty();
+        data->cld_name = strlib_empty();
+        data->cld_number = strlib_empty();
+        data->orig_called_name = strlib_empty();
+        data->orig_called_number = strlib_empty();
+        data->last_redir_name = strlib_empty();
+        data->last_redir_number = strlib_empty();
+        data->plcd_name = strlib_empty();
+        data->plcd_number = strlib_empty();
+        data->status = strlib_empty();
+        data->gci[0] = 0;
+       data->vid_dir = SDP_DIRECTION_INACTIVE;
+        data->callref = 0;
+        calllogger_init_call_log(&data->call_log);
+
+        if ( sessUpd->eventID == CREATE_OFFER || sessUpd->eventID == CREATE_ANSWER
+            || sessUpd->eventID == SET_LOCAL_DESC  || sessUpd->eventID == SET_REMOTE_DESC
+            || sessUpd->eventID == REMOTE_STREAM_ADD) {
+               data->sdp = sessUpd->update.ccSessionUpd.data.state_data.sdp;
+               data->cause = sessUpd->update.ccSessionUpd.data.state_data.cause;
+            data->media_stream_track_id = sessUpd->update.ccSessionUpd.data.state_data.media_stream_track_id;
+            data->media_stream_id = sessUpd->update.ccSessionUpd.data.state_data.media_stream_id;
+        }
+
+        /*
+         * If phone was idle, we not going to active state
+         * send notification to resetmanager that we
+         * are no longer resetReady.
+         */
+        if ((CCAPI_DeviceInfo_isPhoneIdle(handle) == TRUE) && (sendResetUpdates)) {
+            resetNotReady();
+        }
+        (void) addhash(data->sess_id, data);
+            }
+
+            //The update accordingly
+            switch (sessUpd->eventID) {
+            case CALL_INFORMATION:
+                data->clg_name = ccsnap_EscapeStrToLocaleStr(data->clg_name, sessUpd->update.ccSessionUpd.data.call_info.clgName, LEN_UNKNOWN);
+                data->cld_name = ccsnap_EscapeStrToLocaleStr(data->cld_name, sessUpd->update.ccSessionUpd.data.call_info.cldName, LEN_UNKNOWN);
+                data->orig_called_name = ccsnap_EscapeStrToLocaleStr(data->orig_called_name, sessUpd->update.ccSessionUpd.data.call_info.origCalledName, LEN_UNKNOWN);
+                data->last_redir_name = ccsnap_EscapeStrToLocaleStr(data->last_redir_name, sessUpd->update.ccSessionUpd.data.call_info.lastRedirectingName, LEN_UNKNOWN);
+
+                data->clg_number = strlib_update(data->clg_number, sessUpd->update.ccSessionUpd.data.call_info.clgNumber);
+                               data->cld_number = strlib_update(data->cld_number, sessUpd->update.ccSessionUpd.data.call_info.cldNumber);
+                               data->alt_number = strlib_update(data->alt_number, sessUpd->update.ccSessionUpd.data.call_info.altClgNumber);
+                               data->orig_called_number = strlib_update(data->orig_called_number, sessUpd->update.ccSessionUpd.data.call_info.origCalledNumber);
+                               data->last_redir_number = strlib_update(data->last_redir_number, sessUpd->update.ccSessionUpd.data.call_info.lastRedirectingNumber);
+                               data->type = sessUpd->update.ccSessionUpd.data.call_info.call_type;
+                               data->inst = sessUpd->update.ccSessionUpd.data.call_info.instance_id;
+                               data->security = sessUpd->update.ccSessionUpd.data.call_info.security;
+                               data->policy = sessUpd->update.ccSessionUpd.data.call_info.policy;
+                break;
+            case CALL_STATE:
+                if (createdSessionData == FALSE) {
+                    return;
+                }
+                data->state = sessUpd->update.ccSessionUpd.data.state_data.state;
+                               data->line = sessUpd->update.ccSessionUpd.data.state_data.line_id;
+                break;
+            default:
+                break;
+            }
+        } else if (sessUpd->eventID == CALL_DELETE_LAST_DIGIT) {
+            CCAPP_DEBUG(DEB_F_PREFIX"CALL_DELETE_LAST_DIGIT: event %d sessid %x.\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sessUpd->eventID, sessUpd->sessionID );
+            return;
+        } else {
+            CCAPP_DEBUG(DEB_F_PREFIX"NOT CREATING Session Ignoring event %d sessid %x \n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sessUpd->eventID, sessUpd->sessionID );
+            return;
+        }
+
+        // send event to csf2g API
+        if (data != NULL) {
+            capset_get_allowed_features(gCCApp.mode, data->state, data->allowed_features);
+            ccsnap_gen_callEvent(CCAPI_CALL_EV_CREATED, CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id));
+        }
+        return;
+
+    }
+
+    CCAPP_DEBUG(DEB_F_PREFIX"Found data for sessid %x event %d\n",
+            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sessUpd->sessionID, sessUpd->eventID);
+       switch(sessUpd->eventID) {
+       case CALL_SESSION_CLOSED:
+           // find and deep free then delete
+        sess_data_p = (session_data_t *)findhash(sessUpd->sessionID);
+        if ( sess_data_p != NULL ){
+            cleanSessionData(sess_data_p);
+            if ( 0 >  delhash(sessUpd->sessionID) ) {
+                APP_ERR_MSG (DEB_F_PREFIX"failed to delete hash sessid=0x%08x\n",
+                        DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),sessUpd->sessionID);
+            }
+            cpr_free(sess_data_p);
+        }
+        if ( (gCCApp.inPreservation || (gCCApp.cucm_mode == FALLBACK)) && isNoCallExist()) {
+            /* The phone is now Idle. Clear the inPreservation Flag */
+            gCCApp.inPreservation = FALSE;
+            proceedWithFOFB();
+        }
+        if ((CCAPI_DeviceInfo_isPhoneIdle(handle) == TRUE) && (sendResetUpdates)) {
+            resetReady();
+        }
+        if (pending_action_type != NO_ACTION) {
+            perform_deferred_action();
+        }
+               break;
+
+       case CALL_STATE:
+           DEF_DEBUG(DEB_F_PREFIX"Call_STATE:. state=%d, attr=%d, cause=%d, instance=%d\n",
+                               DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                               sessUpd->update.ccSessionUpd.data.state_data.state,
+                               data->attr,
+                               sessUpd->update.ccSessionUpd.data.state_data.cause,
+                               data->inst);
+            if ( sessUpd->update.ccSessionUpd.data.state_data.state == HOLD &&
+                 (data->state == REMHOLD || data->state == REMINUSE)){
+                data->state = REMHOLD;
+            } else {
+                data->state = sessUpd->update.ccSessionUpd.data.state_data.state;
+            }
+            data->line = sessUpd->update.ccSessionUpd.data.state_data.line_id;
+            sessUpd->update.ccSessionUpd.data.state_data.attr = data->attr;
+            sessUpd->update.ccSessionUpd.data.state_data.inst = data->inst;
+
+            data->cause = sessUpd->update.ccSessionUpd.data.state_data.cause;
+
+            //Update call state
+             if ( data != NULL ){
+                 capset_get_allowed_features(gCCApp.mode, data->state, data->allowed_features);
+             }
+             calllogger_update(data);
+             ccsnap_gen_callEvent(CCAPI_CALL_EV_STATE, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+
+            //<Added for automation test purpose, <CallEnd>
+            if (data->state == ONHOOK) {
+                NOTIFY_CALL_DEBUG(DEB_NOTIFY_PREFIX"[line=%d][call.ref=%d]\n",
+                                "CallEND", data->line, data->id);
+            }
+            //>
+
+            if (data->state == ONHOOK) {
+                // find and deep free then delete
+                sess_data_p = (session_data_t *)findhash(sessUpd->sessionID);
+                if ( sess_data_p != NULL ){
+                    cleanSessionData(sess_data_p);
+                    if ( 0 >  delhash(sessUpd->sessionID) ) {
+                          APP_ERR_MSG (DEB_F_PREFIX"failed to delete hash sessid=0x%08x\n",
+                                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),sessUpd->sessionID);
+                       }
+                        cpr_free(sess_data_p);
+                     data = NULL;
+                 }
+                if ((gCCApp.inPreservation || (gCCApp.cucm_mode == FALLBACK)) && isNoCallExist()) {
+                    /* The phone is now Idle. Clear the inPreservation Flag */
+                    gCCApp.inPreservation = FALSE;
+                    proceedWithFOFB();
+                }
+                if ((CCAPI_DeviceInfo_isPhoneIdle(handle) == TRUE) && (sendResetUpdates)) {
+                    resetReady();
+                }
+                if (pending_action_type != NO_ACTION) {
+                    perform_deferred_action();
+                }
+             }
+            break;
+
+       case CALL_CALLREF:
+            data->callref = sessUpd->update.ccSessionUpd.data.callref;
+            break;
+       case CALL_GCID:
+               if ( ! strncasecmp(data->gci, sessUpd->update.ccSessionUpd.data.gcid, CC_MAX_GCID)) {
+                 // No change in gci we can ignore the update
+                 return;
+               }
+               sstrncpy(data->gci, sessUpd->update.ccSessionUpd.data.gcid, CC_MAX_GCID);
+               ccsnap_gen_callEvent(CCAPI_CALL_EV_GCID, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+               break;
+       case CALL_NEWCALL:
+           data->state = sessUpd->update.ccSessionUpd.data.state_data.state;
+        data->line = sessUpd->update.ccSessionUpd.data.state_data.line_id;
+        data->attr = sessUpd->update.ccSessionUpd.data.state_data.attr;
+        data->inst = sessUpd->update.ccSessionUpd.data.state_data.inst;
+        return;
+               break;
+
+       case CALL_ATTR:
+               data->attr = sessUpd->update.ccSessionUpd.data.state_data.attr;
+                calllogger_update(data);
+               ccsnap_gen_callEvent(CCAPI_CALL_EV_ATTR, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+               break;
+
+       case CALL_INFORMATION:
+        // check conference state, if it changes, we'll send a conference event notification
+        previouslyInConference = CCAPI_CallInfo_getIsConference(data);
+
+               data->clg_name = ccsnap_EscapeStrToLocaleStr(data->clg_name, sessUpd->update.ccSessionUpd.data.call_info.clgName, LEN_UNKNOWN);
+               data->cld_name = ccsnap_EscapeStrToLocaleStr(data->cld_name, sessUpd->update.ccSessionUpd.data.call_info.cldName, LEN_UNKNOWN);
+               data->orig_called_name = ccsnap_EscapeStrToLocaleStr(data->orig_called_name, sessUpd->update.ccSessionUpd.data.call_info.origCalledName, LEN_UNKNOWN);
+               data->last_redir_name = ccsnap_EscapeStrToLocaleStr(data->last_redir_name, sessUpd->update.ccSessionUpd.data.call_info.lastRedirectingName, LEN_UNKNOWN);
+               data->clg_number = strlib_update(data->clg_number, sessUpd->update.ccSessionUpd.data.call_info.clgNumber);
+               data->cld_number = strlib_update(data->cld_number, sessUpd->update.ccSessionUpd.data.call_info.cldNumber);
+               data->alt_number = strlib_update(data->alt_number, sessUpd->update.ccSessionUpd.data.call_info.altClgNumber);
+               data->orig_called_number = strlib_update(data->orig_called_number, sessUpd->update.ccSessionUpd.data.call_info.origCalledNumber);
+               data->last_redir_number = strlib_update(data->last_redir_number, sessUpd->update.ccSessionUpd.data.call_info.lastRedirectingNumber);
+               data->type = sessUpd->update.ccSessionUpd.data.call_info.call_type;
+               data->inst = sessUpd->update.ccSessionUpd.data.call_info.instance_id;
+               data->security = sessUpd->update.ccSessionUpd.data.call_info.security;
+               data->policy = sessUpd->update.ccSessionUpd.data.call_info.policy;
+        if ((data->cld_number[0]) && ccappCldNumIsCfwdallString(data->cld_number)) {
+            DEF_DEBUG(DEB_F_PREFIX"Not updating the UI. Called Number = %s\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->cld_number);
+            return;
+        }
+        /*
+         * For 3rd Gen and 4th Gen phones... Use sessUpd->update structure to pass call information
+         * For 5th Gen phones, the information will be localized by a 5th Gen Application, and the
+         *   dictionaries might not be equivalent.
+         */
+         calllogger_update(data);
+
+         ccsnap_gen_callEvent(CCAPI_CALL_EV_CALLINFO, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+
+         // if we're entering a conference, then indicate conference participant info (in case the info came earlier, app
+         // will receive the notification now.  If info comes later, then app will receive an additional subsequent info notice
+         // at that time.
+         if ((!previouslyInConference) && (CCAPI_CallInfo_getIsConference(data))) {
+            ccsnap_gen_callEvent(CCAPI_CALL_EV_CONF_PARTICIPANT_INFO, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+         }
+
+               break;
+
+       case CALL_PLACED_INFO:
+               data->plcd_number = strlib_update(data->plcd_number, sessUpd->update.ccSessionUpd.data.plcd_info.cldNum);
+               data->plcd_name = ccsnap_EscapeStrToLocaleStr(data->plcd_name, sessUpd->update.ccSessionUpd.data.plcd_info.cldName, LEN_UNKNOWN);
+                calllogger_setPlacedCallInfo(data);
+
+               break;
+
+       case CALL_SELECTED:
+               data->isSelected = sessUpd->update.ccSessionUpd.data.action;
+               ccsnap_gen_callEvent(CCAPI_CALL_EV_SELECT, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+               break;
+
+       case CALL_SELECT_FEATURE_SET:
+                // Not quite perfect but should do for now
+                if ( strcmp("CONNECTEDNOFEAT", sessUpd->update.ccSessionUpd.data.feat_set.featSet ) ) {
+                    data->allowed_features[CCAPI_CALL_CAP_HOLD] = FALSE;
+                    data->allowed_features[CCAPI_CALL_CAP_CONFERENCE] = FALSE;
+                    data->allowed_features[CCAPI_CALL_CAP_TRANSFER] = FALSE;
+                } else if ( sessUpd->update.ccSessionUpd.data.feat_set.featMask[0] == skConfrn ) {
+                    data->allowed_features[CCAPI_CALL_CAP_CONFERENCE] = FALSE;
+                }
+               ccsnap_gen_callEvent(CCAPI_CALL_EV_CAPABILITY, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+               break;
+
+       case CALL_STATUS:
+       /*
+        * For 5th Gen Phones, data->status is localized.
+        * 3rd Gen and 4th Gen phones should use sessUpd->update struct to pass the status to the application.
+        */
+       data->status = ccsnap_EscapeStrToLocaleStr(data->status, sessUpd->update.ccSessionUpd.data.status.status, LEN_UNKNOWN);
+        if (data->status != NULL) {
+            if(strncmp(data->status, UNKNOWN_PHRASE_STR, UNKNOWN_PHRASE_STR_SIZE) == 0){
+                    data->status = strlib_empty();
+            }
+            if(strcmp(data->status, strlib_empty()) != 0){
+                    ccsnap_gen_callEvent(CCAPI_CALL_EV_STATUS, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+            }
+        }
+        //<Added for automation test purpose, <CallStatusChanged>
+        NOTIFY_CALL_DEBUG(DEB_NOTIFY_PREFIX"[line=%d][call.ref=%d][status=%s]\n",
+                 "callStatusChange", data->line, data->id,
+                  NOTIFY_CALL_STATUS);
+        //>
+
+       break;
+       if(strcmp(data->status, strlib_empty()) != 0){
+          ccsnap_gen_callEvent(CCAPI_CALL_EV_STATUS, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+       }
+
+       case CALL_PRESERVATION_ACTIVE:
+               data->state = PRESERVATION;
+               ccsnap_gen_callEvent(CCAPI_CALL_EV_PRESERVATION, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+               break;
+
+    case RINGER_STATE:
+        data->ringer_start = sessUpd->update.ccSessionUpd.data.ringer.start;
+        data->ringer_mode = sessUpd->update.ccSessionUpd.data.ringer.mode;
+        data->ringer_once = sessUpd->update.ccSessionUpd.data.ringer.once;
+       ccsnap_gen_callEvent(CCAPI_CALL_EV_RINGER_STATE, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+
+    case VIDEO_AVAIL:
+        if ( data->vid_dir == sessUpd->update.ccSessionUpd.data.action ) {
+            // no change don't update
+            return;
+        }
+        data->vid_dir = sessUpd->update.ccSessionUpd.data.action;
+       ccsnap_gen_callEvent(CCAPI_CALL_EV_VIDEO_AVAIL, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case VIDEO_OFFERED:
+        data->vid_offer = sessUpd->update.ccSessionUpd.data.action;
+       ccsnap_gen_callEvent(CCAPI_CALL_EV_VIDEO_OFFERED, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case CALL_ENABLE_BKSP:
+        data->allowed_features[CCAPI_CALL_CAP_BACKSPACE] = TRUE;
+       ccsnap_gen_callEvent(CCAPI_CALL_EV_CAPABILITY, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case CALL_SECURITY:
+        data->security = sessUpd->update.ccSessionUpd.data.security;
+       ccsnap_gen_callEvent(CCAPI_CALL_EV_SECURITY, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case CALL_LOGDISP:
+        data->log_disp = sessUpd->update.ccSessionUpd.data.action;
+        calllogger_updateLogDisp(data);
+        // No need to generate this event anymore
+       // ccsnap_gen_callEvent(CCAPI_CALL_EV_LOG_DISP, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case CALL_DELETE_LAST_DIGIT:
+       ccsnap_gen_callEvent(CCAPI_CALL_EV_LAST_DIGIT_DELETED, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case CALL_FEATURE_CANCEL:
+       ccsnap_gen_callEvent(CCAPI_CALL_EV_XFR_OR_CNF_CANCELLED, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case CALL_RECV_INFO_LIST:
+        //Should not come here. It's dealed in CCAPP_RCVD_INFO.
+        break;
+    case MEDIA_INTERFACE_UPDATE_BEGIN:
+        ccsnap_gen_callEvent(CCAPI_CALL_EV_MEDIA_INTERFACE_UPDATE_BEGIN,
+            CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case MEDIA_INTERFACE_UPDATE_SUCCESSFUL:
+        ccsnap_gen_callEvent(CCAPI_CALL_EV_MEDIA_INTERFACE_UPDATE_SUCCESSFUL,
+            CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case MEDIA_INTERFACE_UPDATE_FAIL:
+        ccsnap_gen_callEvent(CCAPI_CALL_EV_MEDIA_INTERFACE_UPDATE_FAIL,
+            CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
+        break;
+    case CREATE_OFFER:
+    case CREATE_ANSWER:
+    case SET_LOCAL_DESC:
+    case SET_REMOTE_DESC:
+    case REMOTE_STREAM_ADD:
+        data->sdp = sessUpd->update.ccSessionUpd.data.state_data.sdp;
+        data->cause = sessUpd->update.ccSessionUpd.data.state_data.cause;
+        data->state = sessUpd->update.ccSessionUpd.data.state_data.state;
+        data->media_stream_track_id = sessUpd->update.ccSessionUpd.data.state_data.media_stream_track_id;
+        data->media_stream_id = sessUpd->update.ccSessionUpd.data.state_data.media_stream_id;
+        capset_get_allowed_features(gCCApp.mode, data->state, data->allowed_features);
+        ccsnap_gen_callEvent(CCAPI_CALL_EV_STATE, CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id));
+        break;
+    default:
+        DEF_DEBUG(DEB_F_PREFIX"Unknown event, id = %d\n",
+                            DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sessUpd->eventID);
+        break;
+       }
+    return;
+}
+
+static void freeSessionData(session_update_t *sessUpd)
+{
+    switch(sessUpd->eventID) {
+    case CALL_INFORMATION:
+        strlib_free(sessUpd->update.ccSessionUpd.data.call_info.clgName);
+        strlib_free(sessUpd->update.ccSessionUpd.data.call_info.clgNumber);
+        strlib_free(sessUpd->update.ccSessionUpd.data.call_info.cldName);
+        strlib_free(sessUpd->update.ccSessionUpd.data.call_info.cldNumber);
+        strlib_free(sessUpd->update.ccSessionUpd.data.call_info.altClgNumber);
+        strlib_free(sessUpd->update.ccSessionUpd.data.call_info.origCalledName);
+        strlib_free(sessUpd->update.ccSessionUpd.data.call_info.origCalledNumber);
+        strlib_free(sessUpd->update.ccSessionUpd.data.call_info.lastRedirectingName);
+        strlib_free(sessUpd->update.ccSessionUpd.data.call_info.lastRedirectingNumber);
+        break;
+    case CALL_PLACED_INFO:
+       strlib_free(sessUpd->update.ccSessionUpd.data.plcd_info.cldName);
+        strlib_free(sessUpd->update.ccSessionUpd.data.plcd_info.cldNum);
+        break;
+    case CALL_SELECT_FEATURE_SET:
+       strlib_free(sessUpd->update.ccSessionUpd.data.feat_set.featSet);
+        break;
+    case CALL_STATUS:
+        strlib_free(sessUpd->update.ccSessionUpd.data.status.status);
+        break;
+    case CALL_RECV_INFO_LIST:
+        strlib_free(sessUpd->update.ccSessionUpd.data.recv_info_list);
+        break;
+    }
+}
+
+static void freeSessionMgmtData(session_mgmt_t *sessMgmt)
+{
+    switch(sessMgmt->func_id) {
+    case SESSION_MGMT_APPLY_CONFIG:
+        strlib_free(sessMgmt->data.config.log_server);
+        strlib_free(sessMgmt->data.config.load_server);
+        strlib_free(sessMgmt->data.config.load_id);
+        strlib_free(sessMgmt->data.config.inactive_load_id);
+        strlib_free(sessMgmt->data.config.cucm_result);
+        strlib_free(sessMgmt->data.config.fcp_version_stamp);
+        strlib_free(sessMgmt->data.config.dialplan_version_stamp);
+        strlib_free(sessMgmt->data.config.config_version_stamp);
+        break;
+    case SESSION_MGMT_EXECUTE_URI:
+        strlib_free(sessMgmt->data.uri.uri);
+        break;
+    default:
+        break;
+    }
+}
+
+static void freeRcvdInfo(session_rcvd_info_t *rcvdInfo)
+{
+    switch(rcvdInfo->packageID) {
+    case INFO_PKG_ID_GENERIC_RAW:
+        strlib_free(rcvdInfo->info.generic_raw.info_package);
+        strlib_free(rcvdInfo->info.generic_raw.content_type);
+        strlib_free(rcvdInfo->info.generic_raw.message_body);
+        break;
+    }
+}
+
+void dump_msg(char * name, unsigned int *msg, int len, unsigned int cmd) {
+int i,j;
+  CCAPP_DEBUG(DEB_F_PREFIX"\n%s %x %d cmd=%d\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "dump_msg"), name, msg, len, cmd);
+  for ( j=0;j<10;j++) {
+    for(i=0;i<16;i++) {
+      CCAPP_DEBUG(DEB_F_PREFIX"%08X ", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "dump_msg"), msg[i+j]);
+      if ( (i+j+1)*4 >= len ) return;
+    }
+    CCAPP_DEBUG(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "dump_msg"));
+  }
+}
+
+
+/**
+ * A ccapp task listener
+ */
+void ccp_handler(void* msg, int type) {
+    static const char fname[] = "ccp_handler";
+    sessionProvider_cmd_t *cmdMsg;
+    session_feature_t *featMsg;
+    session_update_t *sessUpd;
+    feature_update_t *featUpd;
+    session_mgmt_t *sessMgmt;
+    session_send_info_t *sendInfo;
+    session_rcvd_info_t *rcvdInfo;
+    session_id_t sess_id;
+    session_data_t *data;
+    int length;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"Received Cmd %s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+        CCAPP_TASK_CMD_PRINT(type) );
+
+
+    switch (type) {
+    case CCAPP_SERVICE_CMD:
+        cmdMsg = (sessionProvider_cmd_t *) msg;
+        CCApp_processCmds (cmdMsg->cmd, cmdMsg->cmdData.ccData.reason,
+                         cmdMsg->cmdData.ccData.reason_info);
+        break;
+
+    case CCAPP_INVOKEPROVIDER_FEATURE:
+        featMsg = (session_feature_t *) msg;
+        processProviderEvent(GET_LINEID(featMsg->session_id), featMsg->featureID,
+                            featMsg->featData.ccData.state);
+        if (featMsg->featData.ccData.info != NULL) {
+        strlib_free(featMsg->featData.ccData.info);
+        }
+        if (featMsg->featData.ccData.info1 != NULL) {
+            strlib_free(featMsg->featData.ccData.info1);
+        }
+        break;
+    case CCAPP_INVOKE_FEATURE:
+        featMsg = (session_feature_t *) msg;
+        CCAPP_DEBUG(DEB_F_PREFIX"CCAPP_INVOKE_FEATURE:sid=%d, fid=%d, line=%d, cid=%d, info=%s info1=%s\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                featMsg->session_id,
+                featMsg->featureID,
+                GET_LINEID(featMsg->session_id),
+                GET_CALLID(featMsg->session_id),
+                ((featMsg->featureID == CC_FEATURE_KEYPRESS) ? "..." : featMsg->featData.ccData.info),
+                ((featMsg->featureID == CC_FEATURE_KEYPRESS) ? "..." : featMsg->featData.ccData.info1));
+        processSessionEvent(GET_LINEID(featMsg->session_id), GET_CALLID(featMsg->session_id),
+            featMsg->featureID, featMsg->featData.ccData.state,
+            featMsg->featData.ccData);
+        if (featMsg->featData.ccData.info != NULL) {
+            strlib_free(featMsg->featData.ccData.info);
+        }
+        if (featMsg->featData.ccData.info1 != NULL) {
+            strlib_free(featMsg->featData.ccData.info1);
+        }
+        break;
+
+    case CCAPP_CLOSE_SESSION:
+        sess_id = *(session_id_t*)msg;
+        cc_feature(CC_SRC_UI, GET_CALLID(sess_id),
+                GET_LINEID(sess_id), CC_FEATURE_END_CALL, NULL);
+        break;
+
+    case CCAPP_SESSION_UPDATE:
+        sessUpd = (session_update_t *) msg;
+        // Udpate the local cache
+        CCAPP_DEBUG(DEB_F_PREFIX"CCAPP_SESSION_UPDATE:type:%d.\n",
+              DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), GET_SESS_TYPE(sessUpd->sessionID));
+        // XXX Why do this when sessType is in session_update_t already?
+        if (GET_SESS_TYPE(sessUpd->sessionID) == SESSIONTYPE_CALLCONTROL) {
+            ccappUpdateSessionData(sessUpd);
+        }
+        else {
+            CCAPP_DEBUG(DEB_F_PREFIX"Unknown type:%d\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sessUpd->sessType);
+        }
+        freeSessionData(sessUpd);
+        break;
+
+    case CCAPP_FEATURE_UPDATE:
+
+        featUpd = (feature_update_t *) msg;
+        // Update Registration state
+        if (featUpd->featureID == DEVICE_REG_STATE)
+               CCAPP_DEBUG(DEB_F_PREFIX"DEVICE_REG_STATE\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+        if(featUpd->update.ccFeatUpd.data.line_info.info == CC_REGISTERED)
+               CCAPP_DEBUG(DEB_F_PREFIX"CC_REGISTERED\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+        if(gCCApp.state == (int) CC_INSERVICE)
+               CCAPP_DEBUG(DEB_F_PREFIX"CC_INSERVICE\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+        if ( featUpd->featureID == DEVICE_REG_STATE &&
+            featUpd->update.ccFeatUpd.data.line_info.info == CC_REGISTERED &&
+            gCCApp.state != (int) CC_INSERVICE )
+        {
+            cc_uint32_t major_ver=0, minor_ver=0,addtnl_ver=0;
+            char name[CC_MAX_LEN_REQ_SUPP_PARAM_CISCO_SISTAG]={0};
+            platGetSISProtocolVer( &major_ver, &minor_ver, &addtnl_ver, name);
+            CCAPP_DEBUG(DEB_F_PREFIX"The SIS verion is: %s, sis ver: %d.%d.%d \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), name, major_ver, minor_ver, addtnl_ver);
+            if(!strncmp(name, REQ_SUPP_PARAM_CISCO_CME_SISTAG, strlen(REQ_SUPP_PARAM_CISCO_CME_SISTAG))){
+                CCAPP_DEBUG(DEB_F_PREFIX"This is CUCME mode.\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+            } else if(!strncmp(name, REQ_SUPP_PARAM_CISCO_SISTAG, strlen(REQ_SUPP_PARAM_CISCO_SISTAG))){
+                CCAPP_DEBUG(DEB_F_PREFIX"This is CUCM mode.\n",DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+             } else {
+                CCAPP_DEBUG(DEB_F_PREFIX"This is unknown mode.\n",DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+             }
+
+            gCCApp.state = CC_INSERVICE;
+            gCCApp.cause = CC_CAUSE_NONE;
+
+            // Notify INS/OOS state to Session Manager if required
+            ccapp_hlapi_update_device_reg_state();
+            ccpro_handleINS();
+
+            if (gCCApp.cucm_mode == FAILOVER) {
+                cc_fail_fallback_sip(CC_SRC_UI, RSP_COMPLETE, CC_REG_FAILOVER_RSP, FALSE);
+            }
+            if (gCCApp.cucm_mode == FALLBACK) {
+                cc_fail_fallback_sip(CC_SRC_UI, RSP_COMPLETE, CC_REG_FALLBACK_RSP, FALSE);
+            }
+            gCCApp.cucm_mode = NONE_AVAIL;
+        }
+
+        ccappFeatureUpdated(featUpd);
+        break;
+
+    case CCAPP_SESSION_MGMT:
+        sessMgmt = (session_mgmt_t *) msg;
+        ccappSyncSessionMgmt(sessMgmt);
+        break;
+
+    case CCAPP_SEND_INFO:
+        sendInfo = (session_send_info_t *) msg;
+        data = (session_data_t *)findhash(sendInfo->sessionID);
+
+        if ( data != NULL && data->state == CONNECTED ) {
+            cc_int_info(CC_SRC_UI, CC_SRC_SIP,
+                    GET_CALLID(sendInfo->sessionID),
+                    GET_LINEID(sendInfo->sessionID),
+                    sendInfo->generic_raw.info_package,
+                    sendInfo->generic_raw.content_type,
+                    sendInfo->generic_raw.message_body);
+        }
+        strlib_free(sendInfo->generic_raw.message_body);
+        strlib_free(sendInfo->generic_raw.content_type);
+        strlib_free(sendInfo->generic_raw.info_package);
+    break;
+
+    case CCAPP_RCVD_INFO:
+        sendInfo = (session_send_info_t *) msg;
+        data = (session_data_t *)findhash(sendInfo->sessionID);
+        rcvdInfo = (session_rcvd_info_t *) msg;
+
+        length = strlen((const char*)rcvdInfo->info.generic_raw.message_body);
+        if (data != NULL) {
+            CCAPP_DEBUG(DEB_F_PREFIX"rcvdInfo: addr=%x length=%d, xml=%s\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONFPARSE"),
+                    &data->call_conference, length, rcvdInfo->info.generic_raw.message_body);
+
+            data->info_package = rcvdInfo->info.generic_raw.info_package;
+            data->info_type = rcvdInfo->info.generic_raw.content_type;
+            data->info_body = rcvdInfo->info.generic_raw.message_body;
+
+            ccsnap_gen_callEvent(CCAPI_CALL_EV_RECEIVED_INFO, CREATE_CALL_HANDLE_FROM_SESSION_ID(rcvdInfo->sessionID));
+
+            // most of the time isConference will be true at this point, we can notify the app of
+            // the updated participant info.  However, if we're transitioning from non conference into
+            // conference, then we'll delay this notification until we're fully in conference state
+            if (CCAPI_CallInfo_getIsConference(data))
+            {   // in conference - send the info
+                ccsnap_gen_callEvent(CCAPI_CALL_EV_CONF_PARTICIPANT_INFO, CREATE_CALL_HANDLE_FROM_SESSION_ID(rcvdInfo->sessionID));
+            }
+
+            // one shot notify cleanup after event generation
+            data->info_package = strlib_empty();
+            data->info_type = strlib_empty();
+            data->info_body = strlib_empty();
+        }
+        freeRcvdInfo(rcvdInfo);
+        break;
+
+    case CCAPP_UPDATELINES:
+        notify_register_update(*(int *)msg);
+        break;
+
+    case CCAPP_MODE_NOTIFY:
+    case CCAPP_FAILOVER_IND:
+    case CCAPP_SHUTDOWN_ACK:
+    case CCAPP_FALLBACK_IND:
+    case CCAPP_REG_ALL_FAIL:
+        featUpd = (feature_update_t *) msg;
+        ccappHandleRegStateUpdates(featUpd);
+        break;
+
+    case CCAPP_THREAD_UNLOAD:
+        destroy_ccapp_thread();
+        break;
+    default:
+        APP_ERR_MSG("CCApp_Task: Error: Unknown message %d msg =0x%x\n", type, msg);
+        break;
+    }
+}
+
+/*
+ *  Function: destroy_ccapp_thread
+ *  Description:  shutdown and kill ccapp thread
+ *  Parameters:   none
+ *  Returns: none
+ */
+void destroy_ccapp_thread()
+{
+    static const char fname[] = "destroy_ccapp_thread";
+    TNP_DEBUG(DEB_F_PREFIX"Unloading ccapp and destroying ccapp thread\n",
+        DEB_F_PREFIX_ARGS(SIP_CC_INIT, fname));
+    platform_initialized = FALSE;
+    CCAppShutdown();
+    (void)cprDestroyThread(ccapp_thread);
+}
+
+/**
+ * CCAPP wrapper to update device features.
+ * @param featUpd - feature_update_t
+ * @return void
+ */
+static
+void ccappFeatureUpdated (feature_update_t *featUpd) {
+    cc_line_info_t *line_info;
+
+    switch(featUpd->featureID) {
+    case DEVICE_FEATURE_CFWD:
+        line_info = ccsnap_getLineInfoFromBtn(featUpd->update.ccFeatUpd.data.cfwd.line);
+        if ( line_info != NULL ) {
+            line_info->isCFWD = featUpd->update.ccFeatUpd.data.cfwd.isFwd;
+            line_info->isLocalCFWD = featUpd->update.ccFeatUpd.data.cfwd.isLocal;
+            line_info->cfwd_dest = strlib_update(line_info->cfwd_dest, featUpd->update.ccFeatUpd.data.cfwd.cfa_num);
+           ccsnap_gen_lineEvent(CCAPI_LINE_EV_CFWDALL, line_info->button);
+        }
+        CC_Config_setStringValue(CFGID_LINE_CFWDALL+featUpd->update.ccFeatUpd.data.cfwd.line-1, featUpd->update.ccFeatUpd.data.cfwd.cfa_num);
+
+        break;
+    case DEVICE_FEATURE_MWI:
+        line_info = ccsnap_getLineInfoFromBtn(featUpd->update.ccFeatUpd.data.mwi_status.line);
+        if ( line_info != NULL ) {
+            line_info->mwi.status = featUpd->update.ccFeatUpd.data.mwi_status.status;
+            line_info->mwi.type = featUpd->update.ccFeatUpd.data.mwi_status.type;
+            line_info->mwi.new_count = featUpd->update.ccFeatUpd.data.mwi_status.newCount;
+            line_info->mwi.old_count = featUpd->update.ccFeatUpd.data.mwi_status.oldCount;
+            line_info->mwi.pri_new_count = featUpd->update.ccFeatUpd.data.mwi_status.hpNewCount;
+            line_info->mwi.pri_old_count = featUpd->update.ccFeatUpd.data.mwi_status.hpOldCount;
+           ccsnap_gen_lineEvent(CCAPI_LINE_EV_MWI, line_info->button);
+        }
+        //Added for automation test
+        NOTIFY_LINE_DEBUG(DEB_NOTIFY_PREFIX"[line=%d][state=%s]",
+                        "MWIChanged", featUpd->update.ccFeatUpd.data.mwi_status.line,
+                        (featUpd->update.ccFeatUpd.data.mwi_status.status)?"ON":"OFF");
+
+        break;
+    case DEVICE_FEATURE_MWILAMP:
+        g_deviceInfo.mwi_lamp = featUpd->update.ccFeatUpd.data.state_data.state;
+       ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_MWI_LAMP, CC_DEVICE_ID);
+        break;
+    case DEVICE_FEATURE_BLF:
+        sub_hndlr_NotifyBLFStatus(featUpd->update.ccFeatUpd.data.blf_data.request_id,
+                                  featUpd->update.ccFeatUpd.data.blf_data.state,
+                                  featUpd->update.ccFeatUpd.data.blf_data.app_id);
+        break;
+    case DEVICE_FEATURE_MNC_REACHED:
+        line_info = ccsnap_getLineInfoFromBtn(featUpd->update.ccFeatUpd.data.line_info.line);
+        if ( line_info != NULL ) {
+            ccsnap_handle_mnc_reached(line_info,
+                featUpd->update.ccFeatUpd.data.line_info.info, gCCApp.mode);
+           ccsnap_gen_lineEvent(CCAPI_LINE_EV_CAPSET_CHANGED, line_info->button);
+        }
+        break;
+    case DEVICE_SERVICE_CONTROL_REQ:
+        reset_type = (cc_srv_ctrl_req_t) featUpd->update.ccFeatUpd.data.reset_type;
+        ccpro_handleserviceControlNotify();
+        break;
+    case DEVICE_NOTIFICATION:
+        g_deviceInfo.not_prompt = ccsnap_EscapeStrToLocaleStr(g_deviceInfo.not_prompt, featUpd->update.ccFeatUpd.data.notification.prompt, LEN_UNKNOWN);
+        g_deviceInfo.not_prompt_prio = featUpd->update.ccFeatUpd.data.notification.priority;
+        g_deviceInfo.not_prompt_prog = featUpd->update.ccFeatUpd.data.notification.notifyProgress;
+       ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_NOTIFYPROMPT, CC_DEVICE_ID);
+        break;
+    case DEVICE_LABEL_N_SPEED:
+       // todo
+        break;
+    case DEVICE_REG_STATE:
+        line_info = ccsnap_getLineInfoFromBtn(featUpd->update.ccFeatUpd.data.line_info.line);
+        if ( line_info != NULL ) {
+            line_info->reg_state = featUpd->update.ccFeatUpd.data.line_info.info;
+           ccsnap_gen_lineEvent(CCAPI_LINE_EV_REG_STATE, line_info->button);
+        }
+        break;
+    case DEVICE_CCM_CONN_STATUS:
+        ccsnap_update_ccm_status(featUpd->update.ccFeatUpd.data.ccm_conn.addr,
+                       featUpd->update.ccFeatUpd.data.ccm_conn.status);
+       ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_SERVER_STATUS, CC_DEVICE_ID);
+        break;
+    default:
+        DEF_DEBUG(DEB_F_PREFIX"Unknown event: id= %d\n",
+                            DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccappFeatureUpdated"), featUpd->featureID);
+        break;
+
+    }
+    if ( featUpd->featureID == DEVICE_NOTIFICATION) {
+        strlib_free(featUpd->update.ccFeatUpd.data.notification.prompt);
+    }
+
+    if ( featUpd->featureID == DEVICE_CCM_CONN_STATUS) {
+        strlib_free(featUpd->update.ccFeatUpd.data.ccm_conn.addr);
+    }
+
+    if ( featUpd->featureID == DEVICE_FEATURE_CFWD) {
+        strlib_free(featUpd->update.ccFeatUpd.data.cfwd.cfa_num);
+    }
+
+    if (featUpd->featureID == DEVICE_SYNC_CONFIG_VERSION) {
+        strlib_free(featUpd->update.ccFeatUpd.data.cfg_ver_data.cfg_ver);
+        strlib_free(featUpd->update.ccFeatUpd.data.cfg_ver_data.dp_ver);
+        strlib_free(featUpd->update.ccFeatUpd.data.cfg_ver_data.softkey_ver);
+    }
+
+    if (featUpd->featureID == DEVICE_LABEL_N_SPEED) {
+        strlib_free(featUpd->update.ccFeatUpd.data.cfg_lbl_n_spd.speed);
+        strlib_free(featUpd->update.ccFeatUpd.data.cfg_lbl_n_spd.label);
+    }
+
+}
+
+/**
+ *
+ * CCApp Provider wrapper for synchronous calls.
+ *
+ * @param   sessMgmt - session management message
+ *
+ * @return  void
+ *
+ * @pre     None
+ */
+void ccappSyncSessionMgmt(session_mgmt_t *sessMgmt)
+{
+    cc_line_info_t *line_info;
+    CCAPP_DEBUG(DEB_F_PREFIX"ccappSyncSessionMgmt: func_id=%d \n",
+            DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccappSyncSessionMgmt"),
+            sessMgmt->func_id);
+
+    //sessionMgmt(sessMgmt);
+    switch (sessMgmt->func_id) {
+    case SESSION_MGMT_SET_TIME:
+        g_deviceInfo.reg_time = sessMgmt->data.time.gmt_time;
+        CCAPP_DEBUG(DEB_F_PREFIX"Setting reg_time to == %lld\n",
+                           DEB_F_PREFIX_ARGS(SIP_CC_PROV,
+                                "ccappSyncSessionMgmt"), g_deviceInfo.reg_time);
+        platSetCucmRegTime();
+        break;
+    case SESSION_MGMT_GET_PHRASE_TEXT:
+        sessMgmt->data.phrase_text.ret_val =
+        platGetPhraseText(sessMgmt->data.phrase_text.ndx,
+                sessMgmt->data.phrase_text.outstr,
+                sessMgmt->data.phrase_text.len);
+        break;
+    case SESSION_MGMT_GET_UNREG_REASON:
+        sessMgmt->data.unreg_reason.unreg_reason = platGetUnregReason();
+        break;
+    case SESSION_MGMT_UPDATE_KPMLCONFIG:
+        platSetKPMLConfig(sessMgmt->data.kpmlconfig.kpml_val);
+        break;
+    case SESSION_MGMT_GET_AUDIO_DEVICE_STATUS:
+        //Noop
+        break;
+    case SESSION_MGMT_CHECK_SPEAKER_HEADSET_MODE:
+        //Noop
+        break;
+    case SESSION_MGMT_LINE_HAS_MWI_ACTIVE:
+        line_info = ccsnap_getLineInfoFromBtn(sessMgmt->data.line_mwi_active.line);
+        if (line_info != NULL) {
+            sessMgmt->data.line_mwi_active.ret_val = line_info->mwi.status;
+        }
+        break;
+    case SESSION_MGMT_APPLY_CONFIG:
+        // save the proposed versions of fcp and dialplan to apply.  Will check against
+        // current versions and redownload if necessary
+
+    if (pending_action_type == NO_ACTION) {
+            configApplyConfigNotify(sessMgmt->data.config.config_version_stamp,
+                sessMgmt->data.config.dialplan_version_stamp,
+                sessMgmt->data.config.fcp_version_stamp,
+                sessMgmt->data.config.cucm_result,
+                sessMgmt->data.config.load_id,
+                sessMgmt->data.config.inactive_load_id,
+                sessMgmt->data.config.load_server,
+                sessMgmt->data.config.log_server,
+                sessMgmt->data.config.ppid);
+       }
+        break;
+    default:
+        break;
+    }
+    freeSessionMgmtData(sessMgmt);
+
+}
+
+/**
+ *  ccCreateSession
+ *
+ *      Called to create a CC session
+ *
+ *  @param param - ccSession_create_param_t
+ *               Contains the type of session and specific data
+ *
+ *  @return  ccSession_id_t - id of the session created
+ */
+session_id_t createSessionId(line_t line, callid_t call)
+{
+    return ( SESSIONTYPE_CALLCONTROL << SID_TYPE_SHIFT ) +
+             (line << SID_LINE_SHIFT ) + call;
+}
+
+/**
+ *  getLineIdAndCallId
+ *
+ *      get Line and call_id
+ *
+ *  @param *line_id
+ *  @param *call_id
+ *
+ */
+void getLineIdAndCallId (line_t *line_id, callid_t *call_id)
+{
+    // assign proper line_id and call_id if not already there
+    if ((*line_id) == 0 || (*line_id) == CC_ALL_LINES) {
+        /*
+         * If the filter is the All Calls Complex Filter and the primary line
+         * is at its configured call capacity, the next available line should
+         * be used. In this scenario, sessionUI/Mgr send the line_id as zero.
+         */
+        (*line_id) = lsm_get_available_line(FALSE);
+    }
+
+    if ((*call_id) == 0) {
+        (*call_id) = cc_get_new_call_id();
+    }
+}
diff --git a/libs/sipcc/core/ccapp/conf_roster.c b/libs/sipcc/core/ccapp/conf_roster.c
new file mode 100644 (file)
index 0000000..2cb1f26
--- /dev/null
@@ -0,0 +1,401 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include "sll_lite.h"
+#include "cc_constants.h"
+#include "cc_types.h"
+#include "cc_config.h"
+#include "phone_debug.h"
+#include "debug.h"
+#include "CCProvider.h"
+#include "ccapi_call_info.h"
+#include "conf_roster.h"
+#include "ccapi.h"
+#include "ccapp_task.h"
+
+cc_conf_participant_status_t
+convertStringToParticipantStatus(const char *data)
+{
+    if (strcmp(data, "connected") == 0) {
+        return CCAPI_CONFPARTICIPANT_CONNECTED;
+    } else if (strcmp(data, "alerting") == 0) {
+        return CCAPI_CONFPARTICIPANT_ALERTING;
+    } else if (strcmp(data, "dialing-out") == 0) {
+        return CCAPI_CONFPARTICIPANT_DIALING_OUT;
+    } else if (strcmp(data, "on-hold") == 0) {
+        return CCAPI_CONFPARTICIPANT_ON_HOLD;
+    } else if (strcmp(data, "disconnected") == 0) {
+        return CCAPI_CONFPARTICIPANT_DISCONNECTED;
+    } else {
+        return CCAPI_CONFPARTICIPANT_UNKNOWN;
+    }
+}
+
+cc_call_security_t
+convertStringToParticipantSecurity(const char *data)
+{
+
+    if (strcmp(data, "NotAuthenticated") == 0) {
+        return CC_SECURITY_NOT_AUTHENTICATED;
+    } else if (strcmp(data, "Authenticated") == 0) {
+        return CC_SECURITY_AUTHENTICATED;
+    } else if (strcmp(data, "Encrypted") == 0) {
+        return CC_SECURITY_ENCRYPTED;
+    } else if (strcmp(data, "Unknown") == 0) {
+        return CC_SECURITY_UNKNOWN;
+    } else {
+        return CC_SECURITY_NONE;
+    }
+}
+
+
+void conf_roster_init_call_conference (cc_call_conference_Info_t *info)
+{
+    CCAPP_DEBUG(DEB_F_PREFIX"in init_call_conference \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONFPARSE"));
+
+    info->participantMax = 0;
+    info->participantCount = 0;
+    info->myParticipantId = strlib_empty();
+
+    sll_lite_init(&info->currentParticipantsList);
+}
+
+void conf_roster_free_call_conference (cc_call_conference_Info_t *confInfo)
+{
+    cc_call_conferenceParticipant_Info_t *participant;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"in free_call_confrerence \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONFPARSE"));
+
+    while((participant=(cc_call_conferenceParticipant_Info_t *)
+                sll_lite_unlink_head(&confInfo->currentParticipantsList)) != NULL)
+    {
+        strlib_free(participant->participantName);
+        strlib_free(participant->endpointUri);
+        strlib_free(participant->callid);
+        strlib_free(participant->participantNumber);
+
+        participant->participantSecurity        = CC_SECURITY_NONE;
+        participant->participantStatus          = CCAPI_CONFPARTICIPANT_UNKNOWN;
+        participant->canRemoveOtherParticipants = FALSE;
+
+        cpr_free(participant);
+        participant = NULL;
+    }
+
+    strlib_free(confInfo->myParticipantId);
+    conf_roster_init_call_conference(confInfo);
+}
+
+void conf_roster_copy_call_conferance (cc_call_conference_Info_t *dest, cc_call_conference_Info_t * src)
+{
+    cc_call_conferenceParticipant_Info_t *destParticipant;
+    cc_call_conferenceParticipant_Info_t *srcParticipant;
+    sll_lite_node_t *iterator;
+    sll_lite_return_e sll_ret_val;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"in copy_call_confrerence \n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONFPARSE"));
+
+    iterator = src->currentParticipantsList.head_p;
+    conf_roster_init_call_conference(dest);
+
+    dest->participantMax = src->participantMax;
+    dest->participantCount = src->participantCount;
+    dest->myParticipantId = strlib_copy(src->myParticipantId);
+
+    while (iterator) {
+        srcParticipant = (cc_call_conferenceParticipant_Info_t *)iterator;
+
+        destParticipant = cpr_malloc(sizeof(cc_call_conferenceParticipant_Info_t));
+        if (destParticipant == NULL) {
+            CCAPP_ERROR(DEB_F_PREFIX" Malloc failure for participant\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONFPARSE"));
+            return;
+        } else {
+            destParticipant->participantName = strlib_copy(srcParticipant->participantName);
+            destParticipant->endpointUri = strlib_copy(srcParticipant->endpointUri);
+            destParticipant->callid = strlib_copy(srcParticipant->callid);
+
+            destParticipant->participantNumber          = strlib_copy(srcParticipant->participantNumber);
+            destParticipant->participantSecurity        = srcParticipant->participantSecurity;
+            destParticipant->participantStatus          = srcParticipant->participantStatus;
+            destParticipant->canRemoveOtherParticipants = srcParticipant->canRemoveOtherParticipants;
+        }
+
+        sll_ret_val = sll_lite_link_tail(&dest->currentParticipantsList, (sll_lite_node_t *)destParticipant);
+        if (sll_ret_val != SLL_LITE_RET_SUCCESS) {
+            CCAPP_ERROR(DEB_F_PREFIX" Error while trying to insert in the linked list\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONFPARSE"));
+            cpr_free(destParticipant);
+            return;
+        }
+
+        iterator = iterator->next_p;
+    }
+}
+
+// -------------------
+// API Implementation
+// -------------------
+
+/**
+* Get Conference Participants
+* @param [in] handle - call handle
+* @param [in/out] participantHandles - array of participant handles to be returned
+* @param [in/out] count - in:  size of array provided in participantHandles; out:  number of entries populated (up to original value provided)
+* @return void
+*/
+void CCAPI_CallInfo_getConfParticipants (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandles[], int* count)
+{
+   cc_call_conference_ref_t              callConference   = NULL;    // conference reference (from call info)
+   cc_call_conference_participant_ref_t  participant      = NULL;    // participant reference
+   cc_uint16_t                           participantIndex = 0;       // participant index
+   cc_uint16_t                           nodeCount        = 0;       // linked list node count
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering:  CCAPI_CallInfo_getConfParticipants\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+
+   // get conference reference from the call info
+   callConference = getCallConferenceRef(handle);
+   if (callConference == NULL)
+   {
+      CCAPP_ERROR(DEB_F_PREFIX"Unable to get conference handle\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+      *count = 0;
+      return;
+   }
+
+   nodeCount = SLL_LITE_NODE_COUNT(&(callConference->currentParticipantsList));
+   CCAPP_DEBUG(DEB_F_PREFIX"SLL NODE COUNT = [%d]\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"), nodeCount);
+   if (nodeCount <= 0)
+   {
+      *count = 0;
+      return;
+   }
+
+   participant = (cc_call_conference_participant_ref_t)SLL_LITE_LINK_HEAD(&callConference->currentParticipantsList);
+   while (participant != NULL)
+   {
+      if (participantIndex >= *count)
+      {
+          CCAPP_ERROR(DEB_F_PREFIX"Not Enough Room Provided To List All Participants.  Listed [%d] of [%d]\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"), count, nodeCount);
+          return;
+      }
+
+      // add this participant to our list of particpiants
+      participantHandles[participantIndex] = (participant->callid);
+
+      // step to the next stored participant in the list
+      participant = (cc_call_conference_participant_ref_t)SLL_LITE_LINK_NEXT_NODE(participant);
+      participantIndex++;
+   }
+
+   // sanity check
+   if (participantIndex != nodeCount)
+   {  // did not find the expected number of participants!
+       CCAPP_ERROR(DEB_F_PREFIX"Detected mismatch between counted participants [%d] and SLL returned nodecount [%d]\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"),
+                participantIndex, nodeCount);
+       *count = 0;
+       return;
+   }
+
+   // return number of participants
+   *count = nodeCount;
+   return;
+}
+
+/**
+* Get Maximum Number of Conference Participants ( in case gui wants to show %full conference info )
+* @param [in] handle - call handle
+* @return maximum number of conference participants
+*/
+cc_uint16_t CCAPI_CallInfo_getConfParticipantMax (cc_callinfo_ref_t handle)
+{  //
+   cc_call_conference_ref_t callConference;    // conference reference (from call info)
+
+   CCAPP_DEBUG(DEB_F_PREFIX"Entering:  CCAPI_CallInfo_getConfParticipantMax\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+
+   // get conference reference from the call info
+   callConference = getCallConferenceRef(handle);
+   if (callConference == NULL)
+   {
+      // no conference reference available
+      CCAPP_ERROR(DEB_F_PREFIX"Unable to get conference reference\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+      return (0);
+   }
+
+   // return the max
+   return (callConference->participantMax);
+}
+
+/**
+* Get Participant Name
+* @param [in] handle - call info handle
+* @param [in] participantHandle - specific handle for conference participant
+* @return display name of the conference participant
+*/
+cc_string_t CCAPI_CallInfo_getConfParticipantName (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle)
+{
+    cc_call_conference_participant_ref_t participant = getConferenceParticipantRef (handle, participantHandle);
+    if (participant == NULL)
+    {
+        return strlib_empty();
+    }
+
+    return (participant->participantName);
+}
+
+/**
+* Get Participant Number
+* @param [in] handle - handle of call
+* @param [in] participantHandle - handle of conference participant
+* @return display number of the conference participant
+*/
+cc_string_t CCAPI_CallInfo_getConfParticipantNumber (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle)
+{
+    cc_call_conference_participant_ref_t participant = getConferenceParticipantRef (handle, participantHandle);
+    if (participant == NULL)
+    {
+        return strlib_empty();
+    }
+
+    return (participant->participantNumber);
+}
+
+/**
+* Get Conference Participant Status
+* @param [in] handle - call handle
+* @param [in] participantHandle - handle of conference participant
+* @return conference participant status
+*/
+cc_conf_participant_status_t CCAPI_CallInfo_getConfParticipantStatus (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle)
+{
+    cc_call_conference_participant_ref_t participant = getConferenceParticipantRef (handle, participantHandle);
+    if (participant == NULL)
+    {
+        return (CCAPI_CONFPARTICIPANT_UNKNOWN);
+    }
+
+    return (participant->participantStatus);
+}
+
+/**
+* Get Participant Security
+* @param [in] handle - call handle
+* @param [in] participantHandle - handle of conference participant
+* @return security setting of the specific conference participant
+*/
+cc_call_security_t CCAPI_CallInfo_getConfParticipantSecurity (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle)
+{
+    cc_call_conference_participant_ref_t participant = getConferenceParticipantRef (handle, participantHandle);
+    if (participant == NULL)
+    {
+        return (CC_SECURITY_NONE);
+    }
+
+    return (participant->participantSecurity);
+}
+
+/**
+*/
+cc_boolean CCAPI_CallInfo_isConfSelfParticipant (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle)
+{
+   cc_call_conference_ref_t callConference;    // conference reference (from call info)
+
+   // get conference reference from the call info
+   callConference = getCallConferenceRef(handle);
+   if (callConference == NULL)
+   {
+      // error - log
+      CCAPP_ERROR(DEB_F_PREFIX"Unable to get conference reference\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+      return (FALSE);
+   }
+
+   return (strcmp((callConference->myParticipantId), participantHandle) == 0);
+}
+
+/**
+*/
+cc_participant_ref_t CCAPI_CallInfo_getConfSelfParticipant (cc_callinfo_ref_t handle)
+{
+   cc_call_conference_ref_t callConference;    // conference reference (from call info)
+
+   // get conference reference from the call info
+   callConference = getCallConferenceRef(handle);
+   if (callConference == NULL)
+   {
+      // unexpected error
+      CCAPP_ERROR(DEB_F_PREFIX"Unable to get conference reference\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+      return strlib_empty();
+   }
+
+   return (callConference->myParticipantId);
+}
+
+// -----
+/**
+ * Get the call conference reference
+ * @param [in] handle - call info handle
+ * @return cc_call_conference_Info_t
+ */
+cc_call_conference_ref_t  getCallConferenceRef(cc_callinfo_ref_t handle)
+{
+  session_data_t *data = (session_data_t *)handle;
+
+  if (!CCAPI_CallInfo_getIsConference(handle))
+  {
+      CCAPP_ERROR(DEB_F_PREFIX"Conference API Invoked, but Not In Conference Call\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+      return (NULL);
+  };
+
+  if (data == NULL)
+  {
+      return (NULL);
+  }
+
+  return (&data->call_conference);
+}
+
+// ------------------------------------------------------------------------------------------------------------------
+// getConferenceParticipantRef:  returns participant ref (pointer) to a specific participant handle
+// ------------------------------------------------------------------------------------------------------------------
+cc_call_conference_participant_ref_t getConferenceParticipantRef(cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle)
+{
+   cc_call_conference_ref_t              callConference;    // conference reference (from call info)
+   cc_call_conference_participant_ref_t  participant;
+
+   // get conference reference from the call info
+   callConference = getCallConferenceRef(handle);
+   if (callConference == NULL)
+   {
+      // no conference reference available
+      CCAPP_ERROR(DEB_F_PREFIX"Unable to get conference reference\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+      return (NULL);
+   }
+
+   // see if participantHandle is legit...
+   if (participantHandle == NULL)
+   {
+       CCAPP_DEBUG(DEB_F_PREFIX"Received query for null participant\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+       return (NULL);
+   }
+
+   if (SLL_LITE_NODE_COUNT(&(callConference->currentParticipantsList)) <= 0)
+   {
+      CCAPP_ERROR(DEB_F_PREFIX"Participant list node count is 0, returning NULL\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+      return (NULL);
+   }
+
+   participant = (cc_call_conference_participant_ref_t)SLL_LITE_LINK_HEAD(&callConference->currentParticipantsList);
+   while (participant != NULL)
+   {
+      // see if we've found the participant we're looking for
+      if (strcmp(participant->callid, participantHandle) == 0)
+      {
+         return (participant);
+      }
+
+      // no match so far, so look at the next item in the list...
+      participant = (cc_call_conference_participant_ref_t)SLL_LITE_LINK_NEXT_NODE(participant);
+   }
+
+   CCAPP_ERROR(DEB_F_PREFIX"    Did Not Find participant!\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "CCAPI-CONF"));
+   return (NULL);
+}
diff --git a/libs/sipcc/core/ccapp/conf_roster.h b/libs/sipcc/core/ccapp/conf_roster.h
new file mode 100644 (file)
index 0000000..4eda1bc
--- /dev/null
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __CONFROSTER_H__
+#define __CONFROSTER_H__
+
+#include "sll_lite.h"
+#include "cpr_string.h"
+#include "cc_constants.h"
+#include "cpr_stdio.h"
+#include "ccapi_conf_roster.h"
+
+// structure for individual participant/user info
+typedef struct cc_call_conferenceParticipant_Info_t_ {
+    sll_lite_node_t              node;
+    cc_participant_ref_t         callid;
+    string_t                     participantName;
+    string_t                     participantNumber;
+    cc_conf_participant_status_t participantStatus;
+    cc_call_security_t           participantSecurity;
+    string_t                     endpointUri;
+    cc_boolean                   canRemoveOtherParticipants;
+} cc_call_conferenceParticipant_Info_t;
+
+// reference to above structure
+typedef struct cc_call_conferenceParticipant_Info_t_* cc_call_conference_participant_ref_t;
+
+// main structure (one instance kept per conference (per call))
+typedef struct cc_call_conference_Info_t_ {
+    int32_t              participantMax;
+    int32_t              participantCount;
+    cc_participant_ref_t myParticipantId;
+    sll_lite_list_t      currentParticipantsList;
+} cc_call_conference_Info_t;
+
+// reference to above structure
+typedef struct cc_call_conference_Info_t_* cc_call_conference_ref_t;
+
+void conf_roster_init_call_conference (cc_call_conference_Info_t *info);
+cc_call_conference_ref_t             getCallConferenceRef(cc_callinfo_ref_t handle);
+cc_call_conference_participant_ref_t getConferenceParticipantRef(cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle);
+void conf_roster_free_call_conference (cc_call_conference_Info_t *confInfo);
+void conf_roster_copy_call_conferance (cc_call_conference_Info_t *dest, cc_call_conference_Info_t * src);
+
+#endif
+
+
+
diff --git a/libs/sipcc/core/ccapp/sessionHash.c b/libs/sipcc/core/ccapp/sessionHash.c
new file mode 100755 (executable)
index 0000000..972a5cd
--- /dev/null
@@ -0,0 +1,305 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef UNIT_TEST
+#define cpr_malloc malloc
+#define cpr_free   free
+#define CCAPP_DEBUG printf
+#else
+#include "cpr_stdlib.h"
+#endif
+
+#include "sessionHash.h"
+
+#define HASHBUCKETS 67
+
+hash_table_t *hashtable[HASHBUCKETS]={0};
+
+void hashItrInit(hashItr_t *itr)
+{
+  itr->bucket = 0;
+  itr->node = NULL;
+}
+
+void * hashItrNext(hashItr_t *itr)
+{
+   int i;
+
+   if ( itr->node != NULL ) {
+     if ( itr->node->next != NULL ) {
+       itr->node = itr->node->next;
+       return itr->node->data;
+     }
+     // We just iterated to the end of the list.
+     // Increment the bucket to search next
+     itr->bucket++;
+   }
+
+   for(i=itr->bucket; i< HASHBUCKETS; i++) {
+     if (hashtable[i] != NULL) {
+       itr->bucket = i;
+       itr->node = hashtable[i];
+       return itr->node->data;
+     }
+   }
+   return NULL;
+}
+
+
+/**
+ * sessionHash
+ *      function to add generate hash given the key
+ *
+ * @param key -
+ *
+ * @return the hash index
+ */
+unsigned int sessionHash (unsigned int key)
+{
+ // since the key is session_id create the hashval to be line_id + call_id
+   unsigned int hashval = key + ((key & 0xFFFF0000)>>16);
+
+   return hashval%67;
+}
+
+/**
+ * addhash
+ *      function to add data for a given key in the table
+ *
+ * @param key
+ * @param data - pointer to data stored
+ *
+ * @return - 0 for success
+ */
+
+int addhash (unsigned int key, void *data)
+{
+   hash_table_t *newhash;
+   hash_table_t *cur_hash;
+   unsigned int hashval;
+
+   newhash = (hash_table_t *)(cpr_malloc(sizeof(hash_table_t)));
+   if (newhash == NULL) {
+     return -1;
+   }
+
+   newhash->key = key;
+
+   newhash->data = data;
+
+   hashval = sessionHash(key);
+
+   if (hashtable[hashval] == NULL) {
+      hashtable[hashval] = newhash;
+      hashtable[hashval]->prev = NULL;
+      hashtable[hashval]->next = NULL;
+   }
+   else {
+      cur_hash=hashtable[hashval];
+      while(cur_hash->next != NULL) {
+         cur_hash=cur_hash->next;
+      }
+      cur_hash->next = newhash;
+      newhash->next = NULL;
+      newhash->prev = cur_hash;
+   }
+
+   return 0;
+}
+
+/**
+ * returns the session id given a callid
+ * @param call_id
+ * @return sessionID or 0
+ */
+
+unsigned int ccpro_get_sessionId_by_callid(unsigned short call_id) {
+   int i;
+   hash_table_t *cur_hash;
+
+   for ( i=0; i<HASHBUCKETS ;i++){
+       cur_hash = hashtable[i];
+       while ( cur_hash) {
+         if ( (cur_hash->key & 0xffff) == call_id ) {
+           return cur_hash->key;
+         }
+         cur_hash = cur_hash->next;
+       }
+   }
+   return 0;
+}
+
+
+/**
+ * findhash
+ *      function retrieve the data for the given key
+ *
+ * @param key
+ *
+ * @return the data ptr or NULL
+ */
+
+void *findhash(unsigned int key)
+{
+   unsigned int hashval;
+   hash_table_t *cur_hash;
+
+   hashval = 0;
+
+   hashval = sessionHash(key);
+
+
+   cur_hash = hashtable[hashval];
+   while ( cur_hash != NULL ) {
+       if ( cur_hash->key == key) {
+            return cur_hash->data;
+        }
+        cur_hash = cur_hash->next;
+   }
+
+   return NULL;
+}
+
+/**
+ * delhash
+ *      function to remove the hash entry for a given key
+ *
+ * @param key
+ *
+ * @return - 0 for success
+ */
+
+int delhash(unsigned int  key)
+{
+   unsigned int hashval;
+   hash_table_t *cur_hash;
+
+   hashval = 0;
+
+   hashval = sessionHash(key);
+
+
+   if (hashtable[hashval] == NULL) {
+      return -1;
+   }
+
+   if (hashtable[hashval]->key == key) {
+      cur_hash = hashtable[hashval];
+      hashtable[hashval] = cur_hash->next;
+      if ( hashtable[hashval] != NULL ) {
+        hashtable[hashval]->prev = NULL;
+      }
+      cpr_free(cur_hash);
+      return 0;
+   }
+   else {
+
+      cur_hash = hashtable[hashval]->next;
+
+      while (cur_hash != NULL) {
+         if (cur_hash->key == key) {
+            cur_hash->prev->next = cur_hash->next;
+            if (cur_hash->next != NULL) {
+               cur_hash->next->prev = cur_hash->prev;
+            }
+            cpr_free(cur_hash);
+            return 0;
+         }
+         cur_hash = cur_hash->next;
+      }
+   }
+   return -1;
+}
+
+#ifdef UNIT_TEST
+
+void hashstats(int detail)
+{
+   static const char *fname="hashstats";
+   int max, total, i, nodes, used;
+   double avg;
+   hash_table_t *cur_hash;
+
+   max = total = i = nodes = used = 0;
+   avg = 0;
+
+   if (detail > 0) {
+      for (i = 0; i < HASHBUCKETS; i++) {
+         if (hashtable[i] != NULL) {
+            used++;
+            nodes = 0;
+            cur_hash = hashtable[i];
+            while(cur_hash != NULL) {
+               nodes++;
+               if (detail > 3) {
+                  CCAPPDEBUG(DEB_F_PREFIX"%lx -> %lx: (%lx) (%lx) -> %lx\n",
+                             DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname),
+                             cur_hash->prev, cur_hash, cur_hash->key, cur_hash->data, cur_hash->next);
+               }
+               cur_hash = cur_hash->next;
+            }
+            if (nodes != 0) total += nodes;
+            if (nodes > max) {
+               max = nodes;
+            }
+            if (detail > 1) {
+               CCAPPDEBUG(DEB_F_PREFIX"i: %d\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), i);
+            }
+         }
+      }
+      avg = (double)(total) / (double)(used);
+      CCAPPDEBUG(DEB_F_PREFIX"total: %d\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), total);
+      CCAPPDEBUG(DEB_F_PREFIX"max: %d\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), max);
+      CCAPPDEBUG(DEB_F_PREFIX"used: %lf\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), 100 * ((double)(used) / (double)(HASHBUCKETS)));
+      CCAPPDEBUG(DEB_F_PREFIX"average: %lf\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), avg);
+   }
+}
+
+int main()
+{
+  static const char *fname="main";
+  hashItr_t itr;
+  void * data;
+
+  addhash(0x01010001,0x1234);
+  addhash(0x01060001,0x4567);
+  addhash(0x01060002,0x9324);
+  addhash(0x01070002,0x4321);
+  addhash(0x01070004,0x2134);
+  addhash(0x01080005,0x1324);
+  addhash(0x01030001,0x1243);
+  hashstats(7);
+
+  hashItrInit(&itr);
+  while ( data = hashItrNext(&itr) ) {
+    CCAPPDEBUG(DEB_F_PREFIX"Itr found %lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), data);
+  }
+
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01010001));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01060001));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01060002));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01070002));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01070004));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01080005));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01030001));
+
+  delhash(0x01030001);
+  delhash(0x01060001);
+  hashstats(7);
+
+  hashItrInit(&itr);
+  while ( data = hashItrNext(&itr) ) {
+    CCAPPDEBUG(DEB_F_PREFIX"Itr found %lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HAS, fname), data);
+  }
+
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01010001));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01060001));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01060002));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01070002));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01070004));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01080005));
+  CCAPPDEBUG(DEB_F_PREFIX"%lx\n", DEB_F_PREFIX_ARGS(SIP_SES_HASH, fname), findhash(0x01030001));
+}
+#endif
+
diff --git a/libs/sipcc/core/ccapp/sessionHash.h b/libs/sipcc/core/ccapp/sessionHash.h
new file mode 100755 (executable)
index 0000000..4722bb4
--- /dev/null
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+typedef struct hash_table {
+   struct hash_table *next;
+   struct hash_table *prev;
+   unsigned int key;
+   void *data;
+} hash_table_t;
+
+typedef struct {
+  unsigned int bucket;
+  hash_table_t *node;
+} hashItr_t;
+
+
+extern void hashItrInit(hashItr_t *itr) ;
+extern void * hashItrNext(hashItr_t *itr);
+extern int addhash (unsigned int key, void *data) ;
+extern int delhash(unsigned int  key);
+extern void *findhash(unsigned int key);
+extern unsigned int ccpro_get_sessionId_by_callid(unsigned short call_id);
diff --git a/libs/sipcc/core/common/cfgfile_utils.c b/libs/sipcc/core/common/cfgfile_utils.c
new file mode 100755 (executable)
index 0000000..a31f802
--- /dev/null
@@ -0,0 +1,415 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_socket.h"
+#include "cpr_in.h"
+#include <text_strings.h>
+#include <cfgfile_utils.h>
+#include <config.h>
+#include <phone_debug.h>
+#include "util_string.h"
+
+#define IN6ADDRSZ   16
+#define INT16SZ     2
+#define        INADDRSZ        4
+#define IS_DIGIT(ch)   ((ch >= '0') && (ch <= '9'))
+
+/*
+ * Parse ascii dotted ip address notation into binary representation
+ * only parses and makes sure the address is in the form:
+ *      digits.digits.digits.digits
+ * Requires minimum of 1 digit per each section and digits cannot
+ * exceed 255.  It does NOT attempt to validate if the end result
+ * is a valid ip address or not (eg. 0.0.0.0) is accepted.
+ * The parsed address is returned in the Telecaster "byte reversed"
+ * order.  Eg. 0xf8332ca1 = 161.44.51.248
+ */
+int
+str2ip (const char *str, cpr_ip_addr_t *cpr_addr)
+{
+    uint32_t ip_addr;
+    unsigned int num;
+    int dot_cnt;
+    char ch;
+    int digit_flag;
+    uint32_t *addr = (uint32_t *)&(cpr_addr->u.ip4);
+
+    dot_cnt = 0;
+    num = 0;
+    ip_addr = 0;
+    digit_flag = 0;
+    cpr_addr->type = CPR_IP_ADDR_INVALID;
+
+    while (1) {
+        ch = *str++;
+        if (!ch)
+            break;              /* end of string */
+        /*
+         * Check for digits 0 through 9
+         */
+        if (IS_DIGIT(ch)) {
+            digit_flag = 1;
+            num = num * 10 + (ch - '0');
+            if (num > 255) {
+                return (1);
+            }
+            continue;
+        } else if (ch == ':') {
+            //must be ipv6 address
+            cpr_addr->type = CPR_IP_ADDR_IPV6;
+            return(cpr_inet_pton(AF_INET6, str, addr));
+        }
+
+        /*
+         * Check for DOT.  Must also have seen at least 1 digit prior
+         */
+        if ((ch == '.') && (digit_flag)) {
+            dot_cnt++;
+            ip_addr = ((ip_addr << 8) | num);
+            num = 0;
+            digit_flag = 0;
+            continue;
+        }
+
+        /* if get here invalid dotted IP character or missing digit */
+        return (1);
+    }
+
+    /*
+     * Must have seen 3 dots exactly and at least 1 trailing digit
+     */
+    if ((dot_cnt != 3) || (!digit_flag)) {
+        return (1);
+    }
+
+    ip_addr = ((ip_addr << 8) | num);
+
+    ip_addr = ntohl(ip_addr);   /* convert to Telecaster format */
+    cpr_addr->type = CPR_IP_ADDR_IPV4;
+    *addr = ip_addr;
+    return (0);
+}
+
+
+/*
+ * Parse an IP address.
+ * If the IP address value is set to "" or to "UNPROVISIONED" it
+ * is set to its' default value.
+ */
+int
+cfgfile_parse_ip (const var_t *entry, const char *value)
+{
+// RAC - Defaults will need to be handled on the Java Side.
+//    if ((*value == NUL) || (cpr_strcasecmp(value, "UNPROVISIONED") == 0)) {
+//        cfgfile_set_default(entry);
+//        return (0);
+//    } else {
+    return (str2ip(value, (cpr_ip_addr_t *) entry->addr));
+//    }
+}
+
+/*
+ * Print (format) an IP address.
+ * The IP address to be printed is in the Telecaster "byte reversed"
+ * order.  Eg. 0xf8332ca1 = 248.51.44.161
+ */
+int
+cfgfile_print_ip (const var_t *entry, char *buf, int len)
+{
+    // RT phones receive the IP address in this order: 0xf8332ca1 = 161.44.51.248
+    cpr_ip_addr_t *cprIpAddrPtr = (cpr_ip_addr_t *)entry->addr;
+
+    if (cprIpAddrPtr->type == CPR_IP_ADDR_IPV4) {
+        sprint_ip(buf, cprIpAddrPtr->u.ip4);
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Print (format) an IP address.
+ * The IP address to be printed is in the non-Telecaster "byte reversed"
+ * order - which is really network order.  Eg. 0xa12c33f8 = 161.44.51.248
+ */
+int
+cfgfile_print_ip_ntohl (const var_t *entry, char *buf, int len)
+{
+    uint32_t ip;
+
+    ip = *(uint32_t *) entry->addr;
+    return (snprintf(buf, len, get_debug_string(DEBUG_IP_PRINT),
+                     ((ip >> 24) & (0xff)), ((ip >> 16) & (0xff)),
+                     ((ip >> 8) & (0xff)), ((ip >> 0) & (0xff))));
+}
+
+/*
+ * parse (copy) an ascii string
+ */
+int
+cfgfile_parse_str (const var_t *entry, const char *value)
+{
+    int str_len;
+
+    /* fixme: this could use malloc, or offer a different */
+    /* fixme: parser routine that does like parse_str_ptr */
+    /* fixme: in that case, free the old string and */
+    /* fixme: strdup the new string */
+
+    str_len = strlen(value);
+    if (str_len + 1 > entry->length) {
+        err_msg(get_debug_string(DEBUG_PARSER_STRING_TOO_LARGE),
+                entry->length, str_len);
+        return (1);
+    }
+
+    /*
+     * Copy string into config block
+     */
+    sstrncpy((char *)entry->addr, value, entry->length);
+    return (0);
+
+
+}
+
+/*
+ * Print (format) at string
+ */
+int
+cfgfile_print_str (const var_t *entry, char *buf, int len)
+{
+    return (snprintf(buf, len, "%s", (char *)entry->addr));
+}
+
+/*
+ * Parse an ascii integer into binary
+ */
+int
+cfgfile_parse_int (const var_t *entry, const char *value)
+{
+    unsigned int num;
+    char ch;
+
+    num = 0;
+
+    if (strcmp(value, "UNPROVISIONED") == 0) {
+        num = 0;
+    } else {
+        while (1) {
+            ch = *value++;
+            if (!ch)
+                break;          /* end of string */
+            /*
+             * Check for digits 0 through 9
+             */
+            if (IS_DIGIT(ch)) {
+                num = num * 10 + (ch - '0');
+                continue;
+            }
+
+            /* if get here invalid decimal character */
+            return (1);
+        }
+    }
+    switch (entry->length) {
+    case 1:
+        *(uint8_t *) entry->addr = (uint8_t) num;
+        break;
+    case 2:
+        *(uint16_t *) entry->addr = (uint16_t) num;
+        break;
+    case 4:
+        *(uint32_t *) entry->addr = num;
+        break;
+    default:
+        *(unsigned int *) entry->addr = num;
+        break;
+    }
+
+    return (0);
+}
+
+/*
+ * print (format) an Integer
+ */
+int
+cfgfile_print_int (const var_t *entry, char *buf, int len)
+{
+    unsigned int value;
+
+    switch (entry->length) {
+    case 1:
+        value = *(uint8_t *) entry->addr;
+        break;
+    case 2:
+        value = *(uint16_t *) entry->addr;
+        break;
+    case 4:
+        value = *(uint32_t *) entry->addr;
+        break;
+    default:
+        value = *(unsigned int *) entry->addr;
+        break;
+    }
+    return (snprintf(buf, len, "%u", value));
+}
+
+/*
+ * Parse a keytable.  A key table is a list of keywords.  For each
+ * keyword there is an associated enum value (key value).
+ * search the keyword table for a matching keyword, and if found
+ * set the variable to the matching emum value.
+ */
+int
+cfgfile_parse_key (const var_t *entry, const char *value)
+{
+    const key_table_entry_t *keytable;
+
+    keytable = entry->key_table;
+
+    if (keytable == NULL) {
+        err_msg(get_debug_string(DEBUG_PARSER_NULL_KEY_TABLE));
+        return (1);
+    }
+
+// RAC - This (If Needed) Will need to be moved to the Java Side.
+//    /* check for nulled out keys and set to the default value */
+//    if ((cpr_strcasecmp(value,"UNPROVISIONED") == 0) ||
+//        (value[0] == 0)) {
+//       err_msg(get_debug_string(DEBUG_PARSER_SET_DEFAULT),
+//                  entry->name, entry->default_value);
+//       cfgfile_set_default(entry);
+//       return(0);
+//    }
+
+    while (keytable->name) {
+        if (cpr_strcasecmp(value, keytable->name) == 0) {
+            *(unsigned int *) entry->addr = keytable->value;
+            return (0);
+        }
+        keytable++;
+    }
+
+    err_msg(get_debug_string(DEBUG_PARSER_UNKNOWN_KEY), value);
+    return (1);
+}
+
+/*
+ * print (format) a key value.  Search the table for the matching
+ * enum type, then format as output the keyname associated with it.
+ */
+int
+cfgfile_print_key (const var_t *entry, char *buf, int len)
+{
+    const key_table_entry_t *keytable;
+    int value;
+
+    keytable = entry->key_table;
+    value = *(int *) entry->addr;
+
+    while (keytable->name) {
+        if (value == keytable->value) {
+            return (snprintf(buf, len, "%s", keytable->name));
+        }
+        keytable++;
+    }
+
+    err_msg(get_debug_string(DEBUG_PARSER_UNKNOWN_KEY_ENUM), value);
+    return (0);
+}
+
+/*
+ * Sprintf an IP address in dotted notation.
+ */
+int
+sprint_ip (char *buf, uint32_t ip)
+{
+    return (sprintf(buf, get_debug_string(DEBUG_IP_PRINT),
+                    ((ip >> 0) & (0xff)), ((ip >> 8) & (0xff)),
+                    ((ip >> 16) & (0xff)), ((ip >> 24) & (0xff))));
+}
+
+/*
+ * print (format) a MAC address
+ */
+int
+cfgfile_print_mac (const var_t *entry, char *buf, int len)
+{
+    return (snprintf(buf, len, get_debug_string(DEBUG_MAC_PRINT),
+                     ((uint8_t *) entry->addr)[0] * 256 +
+                     ((uint8_t *) entry->addr)[1],
+                     ((uint8_t *) entry->addr)[2] * 256 +
+                     ((uint8_t *) entry->addr)[3],
+                     ((uint8_t *) entry->addr)[4] * 256 +
+                     ((uint8_t *) entry->addr)[5]));
+}
+
+/**
+ * Parse a keytable.  A key table is a list of keywords.  For each
+ * keyword there is an associated enum value (key value).
+ * search the keyword table for a matching keyword, and if found
+ * save the entire key etnry into the table.
+ *
+ * @param[in] entry - pointer ot var_t.
+ * @param[in] value - pointer to const. string of configuration value.
+ *
+ * @return 1 - failed to parsed the configuration.
+ *         0 - succesfull parsed the configuration value.
+ *
+ * @pre    (entry != NULL)
+ * @pre    (value != NULL)
+ */
+int
+cfgfile_parse_key_entry (const var_t *entry, const char *value)
+{
+    const key_table_entry_t *keytable;
+
+    keytable = entry->key_table;
+
+    if (keytable == NULL) {
+        err_msg(get_debug_string(DEBUG_PARSER_NULL_KEY_TABLE));
+        return (1);
+    }
+
+    while (keytable->name) {
+        if (cpr_strcasecmp(value, keytable->name) == 0) {
+            /* keep the entire entry */
+            *(key_table_entry_t *)entry->addr = *keytable;
+            return (0);
+        }
+        keytable++;
+    }
+
+    err_msg(get_debug_string(DEBUG_PARSER_UNKNOWN_KEY), value);
+    return (1);
+}
+
+/**
+ * print (format) a key value. Print the name of the key out.
+ *
+ * @param[in] entry - pointer ot var_t.
+ * @param[in] value - pointer to const. string of configuration value.
+ *
+ * @return always return 0.
+ *
+ * @pre    (entry != NULL)
+ * @pre    (value != NULL)
+ */
+int
+cfgfile_print_key_entry (const var_t *entry, char *buf, int len)
+{
+    key_table_entry_t *key;
+
+    key = (key_table_entry_t *) entry->addr;
+    if (key->name != NULL) {
+        return (snprintf(buf, len, "%s", key->name));
+    } else {
+        /* the entry is not even configured */
+        return (0);
+    }
+}
diff --git a/libs/sipcc/core/common/cfgfile_utils.h b/libs/sipcc/core/common/cfgfile_utils.h
new file mode 100755 (executable)
index 0000000..63e5465
--- /dev/null
@@ -0,0 +1,96 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CFGFILE_UTILS_H_
+#define _CFGFILE_UTILS_H_
+
+#include "cpr_types.h"
+
+//=============================================================================
+//
+//  Structure/Type definitions
+//
+//-----------------------------------------------------------------------------
+
+struct var_struct;
+
+typedef int (*parse_func_t)(const struct var_struct *, const char *);
+typedef int (*print_func_t)(const struct var_struct *, char *, int);
+
+typedef struct {
+    const char *name;
+    int         value;
+} key_table_entry_t;
+
+#define NULL_KEY  (-1)
+
+typedef struct var_struct {
+    const char  *name;
+    void        *addr;
+    int          length;
+    parse_func_t parse_func;
+    print_func_t print_func;
+    const key_table_entry_t *key_table;
+} var_t;
+
+/*********************************************************
+ *
+ *  Config Table "Helper" Routines
+ *
+ *  These #defines are routines that are called from the
+ *  config table entries to parse (PA), print (PR),
+ *  and export (XP), different config entries.  These are the
+ *  "common" helper routines.  Protocol-specific routines
+ *  are located in prot_configmgr_private.h
+ *
+ *********************************************************/
+#define PA_IP       cfgfile_parse_ip
+#define PR_IP       cfgfile_print_ip
+#define PR_IPN      cfgfile_print_ip_ntohl
+#define PA_STR      cfgfile_parse_str
+#define PR_STR      cfgfile_print_str
+#define PA_INT      cfgfile_parse_int
+#define PR_INT      cfgfile_print_int
+#define PA_KEY      cfgfile_parse_key
+#define PA_KEYE     cfgfile_parse_key_entry
+#define PR_KEY      cfgfile_print_key
+#define PR_KEYE     cfgfile_print_key_entry
+#define PR_MAC      cfgfile_print_mac
+#define XP_NONE     0
+
+/*********************************************************
+ *
+ *  Config Table "Helper" Macros
+ *
+ *  These macros are used to help build the actual config
+ *  table.  They provide the address and length of the
+ *  entries.  They also tell which table the entry is
+ *  stored in.
+ *
+ *********************************************************/
+#define CFGADDR(field) ((void*)&(prot_cfg_block.field))
+#define CFGLEN(field)  (sizeof(prot_cfg_block.field))
+#define CFGVAR(field)  CFGADDR(field),CFGLEN(field)
+
+/* generic config file parsing functions */
+int cfgfile_parse_ip(const var_t *, const char *value);
+int cfgfile_parse_str(const var_t *, const char *value);
+int cfgfile_parse_int(const var_t *, const char *value);
+int cfgfile_parse_key(const var_t *, const char *value);
+int cfgfile_parse_key_entry(const var_t *, const char *value);
+
+/* generic config file printing functions */
+int cfgfile_print_ip(const var_t *, char *buf, int);
+int cfgfile_print_ip_ntohl(const var_t *, char *buf, int);
+int cfgfile_print_str(const var_t *, char *buf, int);
+int cfgfile_print_int(const var_t *, char *buf, int);
+int cfgfile_print_key(const var_t *, char *buf, int);
+int cfgfile_print_key_entry(const var_t *, char *buf, int);
+
+/* generic config file export (print) functions */
+int sprint_ip(char *, uint32_t ip);
+int sprint_mac(char *, const unsigned char *ptr);
+int cfgfile_print_mac(const var_t *entry, char *buf, int);
+
+#endif /* _CFGFILE_UTILS_H_ */
diff --git a/libs/sipcc/core/common/config_api.c b/libs/sipcc/core/common/config_api.c
new file mode 100755 (executable)
index 0000000..bd4d87c
--- /dev/null
@@ -0,0 +1,498 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "config.h"
+#include "dns_utils.h"
+#include "phone_debug.h"
+#include "ccapi.h"
+#include "debug.h"
+
+cc_int32_t ConfigDebug;
+
+/*
+ * This file contains the API routines that are used to
+ * access the config table.
+ *
+ * Avoid writing more of these routines.
+ * Try and reuse these routines as much as possible.
+ *
+ * We should be able to set and retrieve any type of value
+ * using one of these routines.
+ */
+
+
+/*
+ *  Function: config_get_string()
+ *
+ *  Description: Get any arbitrary config entry as a string
+ *
+ *  Parameters: id - The id of the config string to get
+ *              buffer - Empty buffer where string will be copied
+ *              buffer_len -  length of the buffer where string will be copied
+ *
+ *  Returns: None
+ */
+void
+config_get_string (int id, char *buffer, int buffer_len)
+{
+    const var_t *entry;
+    char *buf_start;
+
+    /*
+     * Set the result to be empty in case we can't find anything
+     */
+    buffer[0] = 0;
+    if ((id >= 0) && (id < CFGID_PROTOCOL_MAX)) {
+        entry = &prot_cfg_table[id];
+        if (entry->length > buffer_len) {
+            CONFIG_ERROR(CFG_F_PREFIX"insufficient buffer: %d\n", "config_get_string",
+                    id);
+        } else {
+            buf_start = buffer;
+            entry->print_func(entry, buffer, buffer_len);
+            CONFIG_DEBUG(DEB_F_PREFIX"CFGID %d: get str: %s = %s\n", DEB_F_PREFIX_ARGS(CONFIG_API, "config_get_string"), id, entry->name,
+                         buf_start);
+        }
+    } else {
+        CONFIG_ERROR(CFG_F_PREFIX"Invalid ID: %d\n", "config_get_string", id);
+    }
+}
+
+
+/*
+ *  Function: config_set_string()
+ *
+ *  Parameters: id - The id of the config string to set
+ *              buffer - The new value for the string
+ *
+ *  Description: Set any arbitrary config entry as a string
+ *
+ *  Returns: None
+ */
+void
+config_set_string (int id, char *buffer)
+{
+    const var_t *entry;
+
+    if ((id >= 0) && (id < CFGID_PROTOCOL_MAX)) {
+        entry = &prot_cfg_table[id];
+        if (entry->parse_func(entry, buffer)) {
+            /* Parse function returned an error */
+            CONFIG_ERROR(CFG_F_PREFIX"Parse function failed. ID: %d %s:%s\n", "config_set_string", id, entry->name, buffer);
+        } else {
+            CONFIG_DEBUG(DEB_F_PREFIX"CFGID %d: %s set str to %s\n", DEB_F_PREFIX_ARGS(CONFIG_API, "config_set_string"), id, entry->name,
+                         buffer);
+        }
+    } else {
+        CONFIG_ERROR(CFG_F_PREFIX"Invalid ID: %d\n", "config_set_string", id);
+    }
+}
+
+#define MAX_CONFIG_VAL_PRINT_LEN 256
+/*
+ *  Function: print_config_value()
+ *
+ *  Description: If debug is enabled then print value contained in
+ *               the buffer. Cast and dereference the buffer ptr
+ *               according to length. If no match to char, short,
+ *               int or long then just print each byte (ex: MacAddr).
+ *               Called by config_set/get_value() function.
+ *
+ *  Parameters: id         - the id of the config value to get
+ *              get_set    - config action (get val or set val)
+ *              entry_name - config id name
+ *              buffer     - buffer containing the value
+ *              length     - number of bytes in the buffer
+ *
+ *  Returns: none
+ */
+/*
+ * Some logical upper limit to avoid long print out in case
+ * of large length value
+ */
+void
+print_config_value (int id, char *get_set, const char *entry_name,
+                    void *buffer, int length)
+{
+    long  long_val  = 0;
+    int   int_val   = 0;
+    short short_val = 0;
+    char  char_val  = 0;
+    char  str[MAX_CONFIG_VAL_PRINT_LEN];
+    char *in_ptr;
+    char *str_ptr;
+
+    if (length == sizeof(char)) {
+        char_val = *(char *) buffer;
+        long_val = (long) char_val;
+        CONFIG_DEBUG(DEB_F_PREFIX"CFGID %d: %s: %s = %ld\n", DEB_F_PREFIX_ARGS(CONFIG_API, "print_config_value"), id, get_set, entry_name,
+                     long_val);
+    } else if (length == sizeof(short)) {
+        short_val = *(short *) buffer;
+        long_val = (long) short_val;
+        CONFIG_DEBUG(DEB_F_PREFIX"CFGID %d: %s: %s = %ld\n", DEB_F_PREFIX_ARGS(CONFIG_API, "print_config_value"), id, get_set, entry_name,
+                     long_val);
+    } else if (length == sizeof(int)) {
+        int_val = *(int *) buffer;
+        long_val = (long) int_val;
+        CONFIG_DEBUG(DEB_F_PREFIX"CFGID %d: %s: %s = %ld\n", DEB_F_PREFIX_ARGS(CONFIG_API, "print_config_value"), id, get_set, entry_name,
+                     long_val);
+    } else if (length == sizeof(long)) {
+        long_val = *(long *) buffer;
+        CONFIG_DEBUG(DEB_F_PREFIX"CFGID %d: %s: %s = %ld\n", DEB_F_PREFIX_ARGS(CONFIG_API, "print_config_value"), id, get_set, entry_name,
+                     long_val);
+    } else if (length < MAX_CONFIG_VAL_PRINT_LEN / 2) {
+
+        in_ptr = (char *) buffer;
+        str_ptr = &str[0];
+        while (length--) {
+            sprintf(str_ptr++, "%02x", *in_ptr++);
+            str_ptr++;
+        }
+        *str_ptr = '\0';
+        CONFIG_DEBUG(DEB_F_PREFIX"CFGID %d: %s: %s = %s\n", DEB_F_PREFIX_ARGS(CONFIG_API, "print_config_value"), id, get_set, entry_name, str);
+    } else {
+        CONFIG_ERROR(CFG_F_PREFIX"cfg_id = %d length too long -> %d\n", "print_config_value",
+                id, length);
+    }
+}
+
+/*
+ *  Function: config_get_value()
+ *
+ *  Description: Get any arbitrary config entry as a raw data value.
+ *    If the length doesn't match the actual length of the field,
+ *    nothing will be copied.
+ *
+ *  Parameters: id     - The id of the config value to get
+ *              buffer - Empty buffer where value will be copied
+ *              length - The number of bytes to get
+ *
+ *  Returns: None
+ */
+void
+config_get_value (int id, void *buffer, int length)
+{
+    const var_t *entry;
+
+    /*
+     *  Retrieve raw entry from table.....
+     */
+    if ((id >= 0) && (id < CFGID_PROTOCOL_MAX)) {
+        entry = &prot_cfg_table[id];
+        if (length == entry->length) {
+            memcpy(buffer, entry->addr, entry->length);
+
+            if (ConfigDebug) {
+                print_config_value(id, "Get Val", entry->name, buffer, length);
+            }
+        } else {
+            CONFIG_ERROR(CFG_F_PREFIX"%s size error\n", "config_get_value",
+                    entry->name);
+        }
+    } else {
+        CONFIG_ERROR(CFG_F_PREFIX"Invalid ID: %d\n", "config_get_value", id);
+    }
+}
+
+
+/*
+ *  Function: config_set_value()
+ *
+ *  Description: Set arbitrary config entry as a raw data value.
+ *    If the length doesn't match the actual length of the field,
+ *    nothing will be copied.
+ *
+ *  Parameters: id     - The id of the config value to set
+ *              buffer - The new value to be set
+ *              length - The number of bytes to set
+ *
+ *  Returns: None
+ */
+void
+config_set_value (int id, void *buffer, int length)
+{
+    const var_t *entry;
+
+    /*
+     *  Retrieve entry from table.....
+     */
+    if ((id >= 0) && (id < CFGID_PROTOCOL_MAX)) {
+        entry = &prot_cfg_table[id];
+        if (entry->length != length) {
+            CONFIG_ERROR(CFG_F_PREFIX" %s size error entry size=%d, len=%d\n",
+                    "config_set_value", entry->name, entry->length, length);
+            return;
+        }
+        memcpy(entry->addr, buffer, entry->length);
+        if (ConfigDebug) {
+            print_config_value(id, "Set Val", entry->name, buffer, length);
+        }
+    } else {
+        CONFIG_ERROR(CFG_F_PREFIX"Invalid ID: %d\n", "config_set_value", id);
+    }
+}
+
+/* Function: get_printable_cfg()
+ *
+ *  Description: prints the config value in the buf
+ *
+ *  Parameters:  indx, buf, len
+ *
+ *  Returns:     buf
+ */
+char *
+get_printable_cfg(unsigned int indx, char *buf, unsigned int len)
+{
+   const var_t *table;
+   buf[0]=0;
+
+   table = &prot_cfg_table[indx];
+   // If this field has a password, print the param name, but NOT the
+   // real password
+   if (indx>=CFGID_LINE_PASSWORD && indx < CFGID_LINE_PASSWORD+MAX_CONFIG_LINES) {
+     // and add an invisible one
+     sstrncpy(buf, "**********", MAX_CONFIG_VAL_PRINT_LEN);
+   } else if ( table->print_func ) {
+     table->print_func(table, buf, len);
+   }
+
+   if ( buf[0] == 0 ) {
+     sstrncpy(buf,"EMPTY", len);
+   }
+   return buf;
+}
+
+/*
+ *  Function: show_config_cmd()
+ *
+ *  Description: Callback passed in the config init routine for show config
+ *
+ *  Parameters:  argc, argv
+ *
+ *  Returns:     zero(0)
+ */
+
+cc_int32_t
+show_config_cmd (cc_int32_t argc, const char *argv[])
+{
+    const var_t *table;
+    char buf[MAX_CONFIG_VAL_PRINT_LEN];
+    int i, feat;
+
+    debugif_printf("\n------ Current *Cache* Configuration ------\n");
+    table = prot_cfg_table;
+
+    for ( i=0; i < CFGID_LINE_FEATURE; i++ ) {
+        if (table->print_func) {
+            table->print_func(table, buf, sizeof(buf));
+
+            // If this field has a password, print the param name, but NOT the
+            // real password
+            if (strstr(table->name, "Password") != 0) {
+                // and add an invisible one
+                sstrncpy(buf, "**********", sizeof(buf));
+            }
+            debugif_printf("%s : %s\n", table->name, buf);
+        }
+        table++;
+    }
+
+    debugif_printf("%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n",
+        prot_cfg_table[CFGID_LINE_INDEX].name,
+        prot_cfg_table[CFGID_LINE_FEATURE].name,
+        prot_cfg_table[CFGID_LINE_MAXNUMCALLS].name,
+        prot_cfg_table[CFGID_LINE_BUSY_TRIGGER].name,
+        prot_cfg_table[CFGID_PROXY_ADDRESS].name,
+        prot_cfg_table[CFGID_PROXY_PORT].name,
+        prot_cfg_table[CFGID_LINE_CALL_WAITING].name,
+        prot_cfg_table[CFGID_LINE_MSG_WAITING_LAMP].name,
+        prot_cfg_table[CFGID_LINE_MESSAGE_WAITING_AMWI].name,
+        prot_cfg_table[CFGID_LINE_RING_SETTING_IDLE].name,
+        prot_cfg_table[CFGID_LINE_RING_SETTING_ACTIVE].name,
+        prot_cfg_table[CFGID_LINE_NAME].name,
+        prot_cfg_table[CFGID_LINE_AUTOANSWER_ENABLED].name,
+        prot_cfg_table[CFGID_LINE_AUTOANSWER_MODE].name,
+        prot_cfg_table[CFGID_LINE_AUTHNAME].name,
+        prot_cfg_table[CFGID_LINE_PASSWORD].name,
+        prot_cfg_table[CFGID_LINE_DISPLAYNAME].name,
+        prot_cfg_table[CFGID_LINE_CONTACT].name);
+
+    for (i=0; i< MAX_CONFIG_LINES; i++) {
+      config_get_value(CFGID_LINE_FEATURE+i, &feat, sizeof(feat));
+      if ( feat != CC_FEATURE_NONE ){
+        debugif_printf("%3s ", get_printable_cfg(CFGID_LINE_INDEX+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%4s ", get_printable_cfg(CFGID_LINE_FEATURE+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%3s ", get_printable_cfg(CFGID_LINE_MAXNUMCALLS+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%3s ", get_printable_cfg(CFGID_LINE_BUSY_TRIGGER+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%12s ", get_printable_cfg(CFGID_PROXY_ADDRESS+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%s ", get_printable_cfg(CFGID_PROXY_PORT+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%3s ", get_printable_cfg(CFGID_LINE_CALL_WAITING+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%6s ", get_printable_cfg(CFGID_LINE_MSG_WAITING_LAMP+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%6s ", get_printable_cfg(CFGID_LINE_MESSAGE_WAITING_AMWI+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%6s ", get_printable_cfg(CFGID_LINE_RING_SETTING_IDLE+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%6s ", get_printable_cfg(CFGID_LINE_RING_SETTING_ACTIVE+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("     %s ", get_printable_cfg(CFGID_LINE_NAME+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%s ", get_printable_cfg(CFGID_LINE_AUTOANSWER_ENABLED+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%s ", get_printable_cfg(CFGID_LINE_AUTOANSWER_MODE+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%s ", get_printable_cfg(CFGID_LINE_AUTHNAME+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%s ", get_printable_cfg(CFGID_LINE_PASSWORD+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%s ", get_printable_cfg(CFGID_LINE_DISPLAYNAME+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+        debugif_printf("%s\n", get_printable_cfg(CFGID_LINE_CONTACT+i, buf, MAX_CONFIG_VAL_PRINT_LEN));
+      }
+    }
+
+    return (0);
+}
+
+
+/**********************************************
+ *  Line-Based Config API
+ **********************************************/
+
+/*
+ *  Function: config_get_line_id()
+ *
+ *  Description: Given the line and the line-specific ID, this function
+ *               will return the actual ID used to access the value in the
+ *               config table.
+ *
+ *  Parameters: id   - The id config value to get
+ *              line - The line that the ID is associated with
+ *
+ *  Returns: TRUE if the entry is found
+ *           FALSE otherwise.
+ */
+static int
+config_get_line_id (int id, int line)
+{
+    int line_id = 0;
+    const var_t *entry;
+
+    if ((line == 0) || (line > MAX_REG_LINES)) {
+        entry = &prot_cfg_table[id];  // XXX set but not used
+        (void) entry;
+        CONFIG_ERROR(CFG_F_PREFIX"ID=%d- line %d out of range\n", "config_get_line_id", id, line);
+        return (0);
+    }
+    line_id = id + line - 1;
+
+    return (line_id);
+}
+
+
+/*
+ *  Function: config_get_line_string()
+ *
+ *  Description: Get any arbitrary line config entry as a string
+ *
+ *  Parameters: id         - The id of the config string to get
+ *              buffer     - Empty buffer where string will be copied
+ *              line       - The line that the ID is associated with
+ *              buffer_len - length of the output buffer
+ *
+ *  Returns: None
+ */
+void
+config_get_line_string (int id, char *buffer, int line, int buffer_len)
+{
+    int line_id = 0;
+
+    line_id = config_get_line_id(id, line);
+    if (line_id) {
+        config_get_string(line_id, buffer, buffer_len);
+    }
+}
+
+
+/*
+ *  Function: config_set_line_string()
+ *
+ *  Description: Set any arbitrary line config entry as a string
+ *
+ *  Parameters: id     - The id of the config string to set
+ *              buffer - The new value for the string
+ *              line   - The line that the ID is associated with
+ *
+ *  Returns: None
+ */
+void
+config_set_line_string (int id, char *buffer, int line)
+{
+    int line_id = 0;
+
+    line_id = config_get_line_id(id, line);
+    if (line_id) {
+        config_set_string(line_id, buffer);
+    }
+}
+
+/*
+ *  Function: config_get_line_value()
+ *
+ *  Parameters: id - The id of the config value to get
+ *              *buffer - Empty buffer where value will be copied
+ *              length - The number of bytes to get
+ *              line - The line that the ID is associated with
+ *
+ *  Description: Get any arbitrary line config entry as a raw data value.
+ *    If the length doesn't match the actual length of the field,
+ *    nothing will be copied.
+ *
+ *  Returns: None
+ */
+void
+config_get_line_value (int id, void *buffer, int length, int line)
+{
+    int line_id = 0;
+
+    line_id = config_get_line_id(id, line);
+    if (line_id) {
+        config_get_value(line_id, buffer, length);
+    }
+}
+
+/*
+ *  Function: config_set_line_value()
+ *
+ *  Description: Set arbitrary config entry as a raw data value.
+ *    If the length doesn't match the actual length of the field,
+ *    nothing will be copied.
+ *
+ *  Parameters: id     - The id of the config value to set
+ *              buffer - The new value to be set
+ *              length - The number of bytes to set
+ *              line   - The line that the ID is associated with
+ *
+ *  Returns: None
+ */
+void
+config_set_line_value (int id, void *buffer, int length, int line)
+{
+    int line_id = 0;
+
+    line_id = config_get_line_id(id, line);
+    if (line_id) {
+        config_set_value(line_id, buffer, length);
+    }
+}
+
+/*
+ *  Function: config_init()
+ *
+ *  Description: Initialize the Config Debug command
+ *
+ *  Parameters:  none
+ *
+ *  Returns:     none
+ *
+ */
+void
+config_init (void)
+{
+    /* Place holder for future init related actions */
+}
diff --git a/libs/sipcc/core/common/config_parser.c b/libs/sipcc/core/common/config_parser.c
new file mode 100644 (file)
index 0000000..4f50fdd
--- /dev/null
@@ -0,0 +1,640 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include "cc_constants.h"
+#include "cc_types.h"
+#include "cc_config.h"
+#include "phone_debug.h"
+#include "debug.h"
+#include "ccapi.h"
+#include "prot_configmgr.h"
+#include "call_logger.h"
+#include "sip_common_transport.h"
+#include "sip_ccm_transport.h"
+#include "config_parser.h"
+#include "cc_device_feature.h"
+#include "ccapi_snapshot.h"
+#include "config_api.h"
+#include "capability_set.h"
+#include "util_string.h"
+
+#define MAC_ADDR_SIZE   6
+#define FILE_PATH 256
+#define MAX_MULTI_LEVEL_CONFIG 2
+
+#define MLCFG_VIDEO_CAPABILITY    0
+#define MLCFG_CISCO_CAMERA        1
+#define MLCFG_CAPABILITY_MAX      2
+#define MLCFG_NOT_SET -1
+
+#define VERSION_LENGTH_MAX 100
+
+#define ID_BLOCK_PREF1   1
+#define ID_BLOCK_PREF3   3
+/*
+ * File location is hardcoded for getting mac and IP addr
+ */
+#define IP_ADDR_FILE  "/sdcard/myip.txt"
+static char autoreg_name[MAX_LINE_NAME_SIZE];
+
+static char fcpTemplateFile[FILE_PATH] = "";
+
+char g_cfg_version_stamp[MAX_CFG_VERSION_STAMP_LEN + 1] = {0};
+int line = -1;  //initialize line to -1, as 0 is valid line
+boolean apply_config = FALSE;
+cc_apply_config_result_t apply_config_result = APPLY_CONFIG_NONE;
+extern var_t prot_cfg_table[];
+void print_config_value (int id, char *get_set, const char *entry_name, void *buffer, int length);
+
+static int sip_port[MAX_CCM];
+static int secured_sip_port[MAX_CCM];
+static int security_mode = 3; /*SECURE*/
+extern accessory_cfg_info_t g_accessoryCfgInfo;
+
+// Configurable settings
+static int gTransportLayerProtocol = 4;   //  4 = tcp, 2 = udp
+static boolean gP2PSIP = FALSE;
+static boolean gSDPMODE = FALSE;
+static int gVoipControlPort = 5060;
+static int gCcm1_sip_port = 5060;
+
+/*
+ * This function determine whether the passed config parameter should be used
+ * in comparing the new and old config value for apply-config purpose.  Only
+ * those config ids on whose change phone needs to restart are part of this
+ * function. For remaining parameters, it is assumed that any change can be
+ * applied dynamically.
+ *
+ */
+boolean is_cfgid_in_restart_list(int cfgid) {
+
+    if ((cfgid >= CFGID_LINE_FEATURE && cfgid < (CFGID_LINE_FEATURE + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_INDEX && cfgid < (CFGID_LINE_INDEX + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_PROXY_ADDRESS && cfgid < (CFGID_PROXY_ADDRESS + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_PROXY_PORT && cfgid < (CFGID_PROXY_PORT + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_NAME && cfgid < (CFGID_LINE_NAME + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_DISPLAYNAME && cfgid < (CFGID_LINE_DISPLAYNAME + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_SPEEDDIAL_NUMBER && cfgid < (CFGID_LINE_SPEEDDIAL_NUMBER + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_MESSAGES_NUMBER && cfgid < (CFGID_LINE_MESSAGES_NUMBER + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_FWD_CALLER_NAME_DIPLAY && cfgid < (CFGID_LINE_FWD_CALLER_NAME_DIPLAY + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_FWD_CALLER_NUMBER_DIPLAY && cfgid < (CFGID_LINE_FWD_CALLER_NUMBER_DIPLAY + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_FWD_REDIRECTED_NUMBER_DIPLAY && cfgid < (CFGID_LINE_FWD_REDIRECTED_NUMBER_DIPLAY + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_FWD_DIALED_NUMBER_DIPLAY && cfgid < (CFGID_LINE_FWD_DIALED_NUMBER_DIPLAY + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_CALL_WAITING && cfgid < (CFGID_LINE_CALL_WAITING + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_AUTHNAME && cfgid < (CFGID_LINE_AUTHNAME + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_PASSWORD && cfgid < (CFGID_LINE_PASSWORD + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_FEATURE_OPTION_MASK && cfgid < (CFGID_LINE_FEATURE_OPTION_MASK + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_MSG_WAITING_LAMP && cfgid < (CFGID_LINE_MSG_WAITING_LAMP + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_MESSAGE_WAITING_AMWI && cfgid < (CFGID_LINE_MESSAGE_WAITING_AMWI + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_RING_SETTING_IDLE && cfgid < (CFGID_LINE_RING_SETTING_IDLE + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_RING_SETTING_ACTIVE && cfgid < (CFGID_LINE_RING_SETTING_ACTIVE + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_CONTACT && cfgid < (CFGID_LINE_CONTACT + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_MAXNUMCALLS && cfgid < (CFGID_LINE_MAXNUMCALLS + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_BUSY_TRIGGER && cfgid < (CFGID_LINE_BUSY_TRIGGER + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_AUTOANSWER_ENABLED && cfgid < (CFGID_LINE_AUTOANSWER_ENABLED + MAX_CONFIG_LINES)) ||
+        (cfgid >= CFGID_LINE_AUTOANSWER_MODE && cfgid < (CFGID_LINE_AUTOANSWER_MODE + MAX_CONFIG_LINES))
+       )
+    {
+        return TRUE;
+    }
+    switch (cfgid) {
+        case CFGID_CCM1_ADDRESS:
+        case CFGID_CCM2_ADDRESS:
+        case CFGID_CCM3_ADDRESS:
+        case CFGID_CCM1_SIP_PORT:
+        case CFGID_CCM2_SIP_PORT:
+        case CFGID_CCM3_SIP_PORT:
+
+        case CFGID_PROXY_BACKUP:
+        case CFGID_PROXY_BACKUP_PORT:
+        case CFGID_PROXY_EMERGENCY:
+        case CFGID_PROXY_EMERGENCY_PORT:
+        case CFGID_OUTBOUND_PROXY:
+        case CFGID_OUTBOUND_PROXY_PORT:
+
+        case CFGID_PROXY_REGISTER:
+        case CFGID_REMOTE_CC_ENABLED:
+
+        case CFGID_SIP_INVITE_RETX:
+        case CFGID_SIP_RETX:
+        case CFGID_TIMER_INVITE_EXPIRES:
+        case CFGID_TIMER_KEEPALIVE_EXPIRES:
+        case CFGID_TIMER_SUBSCRIBE_EXPIRES:
+        case CFGID_TIMER_SUBSCRIBE_DELTA:
+        case CFGID_TIMER_T1:
+        case CFGID_TIMER_T2:
+
+        case CFGID_SIP_MAX_FORWARDS:
+        case CFGID_REMOTE_PARTY_ID:
+        case CFGID_REG_USER_INFO:
+
+        case CFGID_PREFERRED_CODEC:
+        case CFGID_VOIP_CONTROL_PORT:
+        case CFGID_NAT_ENABLE:
+        case CFGID_NAT_ADDRESS:
+        case CFGID_NAT_RECEIVED_PROCESSING:
+
+        case CFGID_DTMF_AVT_PAYLOAD:
+        case CFGID_DTMF_DB_LEVEL:
+        case CFGID_DTMF_OUTOFBAND:
+
+        case CFGID_KPML_ENABLED:
+        case CFGID_MEDIA_PORT_RANGE_START:
+        case CFGID_TRANSPORT_LAYER_PROT:
+
+        case CFGID_TIMER_REGISTER_EXPIRES:
+        case CFGID_TIMER_REGISTER_DELTA:
+        case CFGID_DSCP_FOR_CALL_CONTROL:
+            return TRUE;
+        default:
+            return FALSE;
+    }
+}
+
+
+/*
+ * This function either compare the new and old config value or set the value
+ * for the config_id passed depending upon whether apply-config is true or not.
+ */
+void compare_or_set_byte_value(int cfgid, unsigned char value, const unsigned char * config_name) {
+    int temp_value ;
+    const var_t *entry;
+    if (apply_config == TRUE) {
+        if (is_cfgid_in_restart_list(cfgid) == TRUE) {
+            config_get_value(cfgid, &temp_value, sizeof(temp_value));
+            if (((int)value) !=  temp_value) {
+                apply_config_result = RESTART_NEEDED;
+                entry = &prot_cfg_table[cfgid];
+                print_config_value(cfgid, "changed Get Val", entry->name, &temp_value, sizeof(temp_value));
+                DEF_DEBUG(CFG_F_PREFIX "config %s[%d] changed. Old value=%d new value=%d\n", "compare_or_set_byte_value", config_name, cfgid, temp_value, value);
+            }
+        }
+    } else {
+        CC_Config_setByteValue(cfgid, value);
+    }
+}
+
+/*
+ * This function either compare the new and old config value or set the value
+ * for the config_id passed depending upon whether apply-config is true or not.
+ */
+void compare_or_set_boolean_value(int cfgid, cc_boolean value, const unsigned char * config_name) {
+    int temp_value ;
+    const var_t *entry;
+    if (apply_config == TRUE) {
+        if (is_cfgid_in_restart_list(cfgid) == TRUE) {
+            config_get_value(cfgid, &temp_value, sizeof(temp_value));
+            if (((int)value) !=  temp_value) {
+                apply_config_result = RESTART_NEEDED;
+                entry = &prot_cfg_table[cfgid];
+                print_config_value(cfgid, "changed Get Val", entry->name, &temp_value, sizeof(temp_value));
+                DEF_DEBUG(CFG_F_PREFIX "config %s[%d] changed. Old value=%d new value=%d\n", "compare_or_set_boolean_value", config_name, cfgid, temp_value, value);
+            }
+        }
+    } else {
+        CC_Config_setBooleanValue(cfgid, value);
+    }
+}
+
+/*
+ * This function either compare the new and old config value or set the value
+ * for the config_id passed depending upon whether apply-config is true or not.
+ */
+void compare_or_set_int_value(int cfgid, int value, const unsigned char * config_name) {
+    int temp_value;
+    const var_t *entry;
+    if (apply_config == TRUE) {
+        if (is_cfgid_in_restart_list(cfgid) == TRUE) {
+            config_get_value(cfgid, &temp_value, sizeof(temp_value));
+            if (value !=  temp_value) {
+                apply_config_result = RESTART_NEEDED;
+                entry = &prot_cfg_table[cfgid];
+                print_config_value(cfgid, "changed Get Val", entry->name, &temp_value, sizeof(temp_value));
+
+                DEF_DEBUG(CFG_F_PREFIX "config %s[%d] changed. new value=%d Old value=%d\n", "compare_or_set_int_value", config_name, cfgid, value, temp_value);
+            }
+        }
+    } else {
+        CC_Config_setIntValue(cfgid, value);
+    }
+}
+
+/*
+ * This function either compare the new and old config value or set the value
+ * for the config_id passed depending upon whether apply-config is true or not.
+ */
+void compare_or_set_string_value (int cfgid, const char* value, const unsigned char * config_name) {
+    static char temp_value[MAX_SIP_URL_LENGTH];
+    const var_t *entry;
+    if (apply_config == TRUE ) {
+        if (is_cfgid_in_restart_list(cfgid) == TRUE) {
+            config_get_string(cfgid, temp_value, MAX_SIP_URL_LENGTH);
+            if (strcmp(value, temp_value) != 0) {
+                apply_config_result = RESTART_NEEDED;
+                entry = &prot_cfg_table[cfgid];
+                print_config_value(cfgid, "changed Get Val", entry->name, &temp_value, sizeof(temp_value));
+                DEF_DEBUG(CFG_F_PREFIX "config %s[%d] changed. new value=%s Old value=%s\n", "compare_or_set_string_value", config_name, cfgid, value, temp_value);
+            }
+        }
+    } else {
+        CC_Config_setStringValue(cfgid, value);
+    }
+}
+
+int lineConfig = 0;
+int portConfig = 0;
+int proxyConfig = 0;
+
+/*
+ * config_set_autoreg_properties
+ *
+ */
+void config_set_autoreg_properties ()
+{
+    CC_Config_setIntValue(CFGID_LINE_INDEX + 0, 1);
+       CC_Config_setIntValue(CFGID_LINE_FEATURE + 0, 9);
+    CC_Config_setStringValue(CFGID_PROXY_ADDRESS + 0, "USECALLMANAGER");
+    CC_Config_setIntValue(CFGID_PROXY_PORT + 0, 5060);
+    CC_Config_setStringValue(CFGID_LINE_NAME + 0, autoreg_name);
+    CC_Config_setBooleanValue(CFGID_PROXY_REGISTER, 1);
+    CC_Config_setIntValue(CFGID_TRANSPORT_LAYER_PROT, 2);
+
+    /* timerRegisterExpires = 3600 */
+    CC_Config_setIntValue(CFGID_TIMER_REGISTER_EXPIRES, 3600);
+    /* sipRetx = 10 */
+   CC_Config_setIntValue(CFGID_SIP_RETX, 10);
+    /* sipInviteRetx = 6 */
+    CC_Config_setIntValue(CFGID_SIP_INVITE_RETX, 6);
+    /* timerRegisterDelta = 5 */
+    CC_Config_setIntValue(CFGID_TIMER_REGISTER_DELTA, 5);
+    /* MaxRedirects = 70 */
+    CC_Config_setIntValue(CFGID_SIP_MAX_FORWARDS, 70);
+    /* timerInviteExpires = 180 */
+    CC_Config_setIntValue(CFGID_TIMER_INVITE_EXPIRES, 180);
+       /* timerSubscribeDelta = 5 */
+    CC_Config_setIntValue(CFGID_TIMER_SUBSCRIBE_DELTA, 5);
+       /* timerSubscribeExpires = 120 */
+    CC_Config_setIntValue(CFGID_TIMER_SUBSCRIBE_EXPIRES, 120);
+
+    CC_Config_setIntValue(CFGID_REMOTE_CC_ENABLED, 1);
+    CC_Config_setIntValue(CFGID_VOIP_CONTROL_PORT, 5060);
+}
+
+/*
+ * update_security_mode_and_ports
+ *
+ */
+void update_security_mode_and_ports(void) {
+        sec_level_t sec_level = NON_SECURE;
+
+    // convert security mode (from UCM xml) into internal enum
+    switch (security_mode)
+    {
+       case 1:  sec_level = NON_SECURE; break;
+       case 2:  sec_level = AUTHENTICATED; break;
+       case 3:  sec_level = ENCRYPTED; break;
+       default:
+          CONFIG_ERROR(CFG_F_PREFIX "unable to translate securite mode [%d]\n", "update_security_mode_and_ports", (int)security_mode);
+          break;
+        }
+
+       compare_or_set_int_value(CFGID_CCM1_SEC_LEVEL, sec_level,
+                                                        (const unsigned char *)"deviceSecurityMode");
+       compare_or_set_int_value(CFGID_CCM2_SEC_LEVEL, sec_level,
+                                                        (const unsigned char *)"deviceSecurityMode");
+       compare_or_set_int_value(CFGID_CCM3_SEC_LEVEL, sec_level,
+                                                        (const unsigned char *)"deviceSecurityMode");
+
+       if (sec_level == NON_SECURE) {
+               compare_or_set_int_value(CFGID_CCM1_SIP_PORT, sip_port[0],
+                                                                (const unsigned char *)"ccm1_sip_port");
+               compare_or_set_int_value(CFGID_CCM2_SIP_PORT, sip_port[1],
+                                                                (const unsigned char *)"ccm2_sip_port");
+               compare_or_set_int_value(CFGID_CCM3_SIP_PORT, sip_port[2],
+                                                                (const unsigned char *)"ccm3_sip_port");
+       } else {
+               compare_or_set_int_value(CFGID_CCM1_SIP_PORT, secured_sip_port[0],
+                                                                (const unsigned char *)"ccm1_secured_sip_port");
+               compare_or_set_int_value(CFGID_CCM2_SIP_PORT, secured_sip_port[1],
+                                                                (const unsigned char *)"ccm2_secured_sip_port");
+               compare_or_set_int_value(CFGID_CCM3_SIP_PORT, secured_sip_port[2],
+                                                                (const unsigned char *)"ccm3_secured_sip_port");
+       }
+}
+
+
+#define MISSEDCALLS "Application:Cisco/MissedCalls"
+#define PLACEDCALLS "Application:Cisco/PlacedCalls"
+#define RECEIVEDCALLS "Application:Cisco/ReceivedCalls"
+
+/*
+ * config_get_mac_addr
+ *
+ * Get the filename that has the mac address and parse the string
+ * convert it into an mac address stored in the bytearray maddr
+*/
+void config_get_mac_addr (char *maddr)
+{
+    platGetMacAddr(maddr);
+
+}
+
+/*
+ * Set the MAC address in the config table
+ */
+void config_set_ccm_ip_mac ()
+{
+
+    char macaddr[MAC_ADDR_SIZE];
+
+    compare_or_set_int_value(CFGID_DSCP_FOR_CALL_CONTROL , 1, (const unsigned char *) "DscpCallControl");
+    compare_or_set_int_value(CFGID_SPEAKER_ENABLED, 1, (const unsigned char *) "speakerEnabled");
+
+    if (apply_config == FALSE) {
+        config_get_mac_addr(macaddr);
+
+        CONFIG_DEBUG(CFG_F_PREFIX ": MAC Address IS:  %x:%x:%x:%x:%x:%x  \n",
+                          "config_get_mac_addr", macaddr[0], macaddr[1],
+                           macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
+
+        CC_Config_setArrayValue(CFGID_MY_MAC_ADDR, macaddr, MAC_ADDR_SIZE);
+        CC_Config_setArrayValue(CFGID_MY_ACTIVE_MAC_ADDR, macaddr, MAC_ADDR_SIZE);
+    }
+}
+
+/*
+ * config_setup_element
+ * Setup elements that once were downloaded from CUCM in an XML file.
+ * Settings are stored in config.h
+ */
+void config_setup_elements (const char *sipUser, const char *sipPassword, const char *sipDomain)
+{
+    unsigned int i;
+    char buf[MAX_SIP_URL_LENGTH] = {'\0'};
+    char ip[MAX_SIP_URL_LENGTH] = {'\0'};
+    char option[MAX_SIP_URL_LENGTH] = {'\0'};
+    int line = 0;
+    cc_boolean isSecure = FALSE, isValid = TRUE;
+    char macaddr[MAC_ADDR_SIZE];
+
+    compare_or_set_int_value(CFGID_MEDIA_PORT_RANGE_START, gStartMediaPort, (const unsigned char *) "startMediaPort");
+    compare_or_set_int_value(CFGID_MEDIA_PORT_RANGE_END, gStopMediaPort, (const unsigned char *) "stopMediaPort");
+    compare_or_set_boolean_value(CFGID_CALLERID_BLOCKING, gCallerIdBlocking, (const unsigned char *) "callerIdBlocking");
+    compare_or_set_boolean_value(CFGID_ANONYMOUS_CALL_BLOCK, gAnonblock, (const unsigned char *) "anonymousCallBlock");
+    compare_or_set_string_value(CFGID_PREFERRED_CODEC, gPreferredCodec, (const unsigned char *) "preferredCodec");
+    compare_or_set_string_value(CFGID_DTMF_OUTOFBAND, gDtmfOutOfBand, (const unsigned char *) "dtmfOutofBand");
+    compare_or_set_int_value(CFGID_DTMF_AVT_PAYLOAD, gDtmfAvtPayload, (const unsigned char *) "dtmfAvtPayload");
+    compare_or_set_int_value(CFGID_DTMF_DB_LEVEL, gDtmfDbLevel, (const unsigned char *) "dtmfDbLevel");
+    compare_or_set_int_value(CFGID_SIP_RETX, gSipRetx, (const unsigned char *) "sipRetx");
+    compare_or_set_int_value(CFGID_SIP_INVITE_RETX, gSipInviteRetx, (const unsigned char *) "sipInviteRetx");
+    compare_or_set_int_value(CFGID_TIMER_T1, gTimerT1, (const unsigned char *) "timerT1");
+    compare_or_set_int_value(CFGID_TIMER_T2, gTimerT2, (const unsigned char *) "timerT2");
+    compare_or_set_int_value(CFGID_TIMER_INVITE_EXPIRES, gTimerInviteExpires, (const unsigned char *) "timerInviteExpires");
+    compare_or_set_int_value(CFGID_TIMER_REGISTER_EXPIRES, gTimerRegisterExpires, (const unsigned char *) "timerRegisterExpires");
+    compare_or_set_boolean_value(CFGID_PROXY_REGISTER, gRegisterWithProxy, (const unsigned char *) "registerWithProxy");
+    compare_or_set_string_value(CFGID_PROXY_BACKUP, gBackupProxy, (const unsigned char *) "backupProxy");
+    compare_or_set_int_value(CFGID_PROXY_BACKUP_PORT, gBackupProxyPort, (const unsigned char *) "backupProxyPort");
+    compare_or_set_string_value(CFGID_PROXY_EMERGENCY, gEmergencyProxy, (const unsigned char *) "emergencyProxy");
+    compare_or_set_int_value(CFGID_PROXY_EMERGENCY_PORT, gEmergencyProxyPort, (const unsigned char *) "emergencyProxyPort");
+    compare_or_set_string_value(CFGID_OUTBOUND_PROXY, gOutboundProxy, (const unsigned char *) "outboundProxy");
+    compare_or_set_int_value(CFGID_OUTBOUND_PROXY_PORT, gOutboundProxyPort, (const unsigned char *) "outboundProxyPort");
+    compare_or_set_boolean_value(CFGID_NAT_RECEIVED_PROCESSING, gNatRecievedProcessing, (const unsigned char *) "natRecievedProcessing");
+    compare_or_set_string_value(CFGID_REG_USER_INFO, gUserInfo, (const unsigned char *) "userInfo");
+    compare_or_set_boolean_value(CFGID_REMOTE_PARTY_ID, gRemotePartyID, (const unsigned char *) "remotePartyID");
+    compare_or_set_boolean_value (CFGID_SEMI_XFER, gSemiAttendedTransfer, (const unsigned char *) "semiAttendedTransfer");
+    compare_or_set_int_value(CFGID_CALL_HOLD_RINGBACK, gCallHoldRingback, (const unsigned char *) "callHoldRingback");
+    compare_or_set_boolean_value(CFGID_STUTTER_MSG_WAITING, gStutterMsgWaiting, (const unsigned char *) "stutterMsgWaiting");
+    compare_or_set_string_value(CFGID_CALL_FORWARD_URI, gCallForwardURI, (const unsigned char *) "callForwardURI");
+    compare_or_set_boolean_value(CFGID_CALL_STATS, gCallStats, (const unsigned char *) "callStats");
+    compare_or_set_int_value(CFGID_TIMER_REGISTER_DELTA, gTimerRegisterDelta, (const unsigned char *) "timerRegisterDelta");
+    compare_or_set_int_value(CFGID_SIP_MAX_FORWARDS, gMaxRedirects, (const unsigned char *) "maxRedirects");
+    compare_or_set_boolean_value(CFGID_2543_HOLD, gRfc2543Hold, (const unsigned char *) "rfc2543Hold");
+    compare_or_set_boolean_value(CFGID_LOCAL_CFWD_ENABLE, gLocalCfwdEnable, (const unsigned char *) "localCfwdEnable");
+    compare_or_set_int_value(CFGID_CONN_MONITOR_DURATION, gConnectionMonitorDuration, (const unsigned char *) "connectionMonitorDuration");
+    compare_or_set_int_value(CFGID_CALL_LOG_BLF_ENABLED, gCallLogBlfEnabled, (const unsigned char *) "callLogBlfEnabled");
+    compare_or_set_boolean_value(CFGID_RETAIN_FORWARD_INFORMATION, gRetainForwardInformation, (const unsigned char *) "retainForwardInformation");
+    compare_or_set_int_value(CFGID_REMOTE_CC_ENABLED, gRemoteCcEnable, (const unsigned char *) "remoteCcEnable");
+    compare_or_set_int_value(CFGID_TIMER_KEEPALIVE_EXPIRES, gTimerKeepAliveExpires, (const unsigned char *) "timerKeepAliveExpires");
+    compare_or_set_int_value(CFGID_TIMER_SUBSCRIBE_EXPIRES, gTimerSubscribeExpires, (const unsigned char *) "timerSubscribeExpires");
+    compare_or_set_int_value(CFGID_TIMER_SUBSCRIBE_DELTA, gTimerSubscribeDelta, (const unsigned char *) "timerSubscribeDelta");
+    compare_or_set_int_value(CFGID_TRANSPORT_LAYER_PROT, gTransportLayerProtocol, (const unsigned char *) "transportLayerProtocol");
+    compare_or_set_int_value(CFGID_KPML_ENABLED, gKpml, (const unsigned char *) "kpml");
+    compare_or_set_boolean_value(CFGID_NAT_ENABLE, gNatEnabled, (const unsigned char *) "natEnabled");
+    compare_or_set_string_value(CFGID_NAT_ADDRESS, gNatAddress, (const unsigned char *) "natAddress");
+    compare_or_set_int_value(CFGID_VOIP_CONTROL_PORT, gVoipControlPort, (const unsigned char *) "voipControlPort");
+    compare_or_set_boolean_value(CFGID_ENABLE_VAD, gAnableVad, (const unsigned char *) "enableVad");
+    compare_or_set_boolean_value(CFGID_AUTOANSWER_IDLE_ALTERNATE, gAutoAnswerAltBehavior, (const unsigned char *) "autoAnswerAltBehavior");
+    compare_or_set_int_value(CFGID_AUTOANSWER_TIMER, gAutoAnswerTimer, (const unsigned char *) "autoAnswerTimer");
+    compare_or_set_boolean_value(CFGID_AUTOANSWER_OVERRIDE, gAutoAnswerOverride, (const unsigned char *) "autoAnswerOverride");
+    compare_or_set_int_value(CFGID_OFFHOOK_TO_FIRST_DIGIT_TIMER, gOffhookToFirstDigitTimer, (const unsigned char *) "offhookToFirstDigitTimer");
+    compare_or_set_int_value(CFGID_CALL_WAITING_SILENT_PERIOD, gSilentPeriodBetweenCallWaitingBursts, (const unsigned char *) "silentPeriodBetweenCallWaitingBursts");
+    compare_or_set_int_value(CFGID_RING_SETTING_BUSY_POLICY, gRingSettingBusyStationPolicy, (const unsigned char *) "ringSettingBusyStationPolicy");
+    compare_or_set_int_value (CFGID_BLF_ALERT_TONE_IDLE, gBlfAudibleAlertSettingOfIdleStation, (const unsigned char *) "blfAudibleAlertSettingOfIdleStation");
+    compare_or_set_int_value (CFGID_BLF_ALERT_TONE_BUSY, gBlfAudibleAlertSettingOfBusyStation, (const unsigned char *) "blfAudibleAlertSettingOfBusyStation");
+    compare_or_set_int_value (CFGID_JOIN_ACROSS_LINES, gJoinAcrossLines, (const unsigned char *) "joinAcrossLines");
+    compare_or_set_boolean_value(CFGID_CNF_JOIN_ENABLE, gCnfJoinEnabled, (const unsigned char *) "cnfJoinEnabled");
+    compare_or_set_int_value (CFGID_ROLLOVER, gRollover, (const unsigned char *) "rollover");
+    compare_or_set_boolean_value(CFGID_XFR_ONHOOK_ENABLED, gTransferOnhookEnabled, (const unsigned char *) "transferOnhookEnabled");
+    compare_or_set_int_value(CFGID_DSCP_AUDIO, gDscpForAudio, (const unsigned char *) "dscpForAudio");
+    compare_or_set_int_value(CFGID_DSCP_VIDEO, gDscpVideo, (const unsigned char *) "dscpVideo");
+    compare_or_set_int_value(CFGID_INTER_DIGIT_TIMER, gT302Timer, (const unsigned char *) "T302Timer");
+
+    // TODO(emannion): You had line=1; line<= ....
+    // Debugging suggests that alghouth *line* is 1-indexed, the config entries
+    // are 1-indexed. See. config_get_line_id().
+    // You may want to rewrite this in terms of config_get_line_id().
+    // Please check -- EKR
+    for(line = 0; line < MAX_REG_LINES; line++) {
+
+           compare_or_set_int_value(CFGID_LINE_INDEX + line, gLineIndex, (const unsigned char *)"lineIndex");
+        compare_or_set_int_value(CFGID_LINE_FEATURE + line, gFeatureID, (const unsigned char *) "featureID");
+        compare_or_set_string_value(CFGID_PROXY_ADDRESS + line, gProxy, (const unsigned char *) "proxy");
+        compare_or_set_int_value(CFGID_PROXY_PORT + line, gPort, (const unsigned char *) "port");
+
+        if ( apply_config == FALSE )  {
+            ccsnap_set_line_label(line+1, "LINELABEL");
+        }
+
+        compare_or_set_string_value(CFGID_LINE_NAME + line, sipUser, (const unsigned char *) "name");
+        compare_or_set_string_value(CFGID_LINE_DISPLAYNAME + line, gDisplayName, (const unsigned char *) "displayName");
+        compare_or_set_string_value(CFGID_LINE_MESSAGES_NUMBER + line, gMessagesNumber, (const unsigned char *) "messagesNumber");
+        compare_or_set_boolean_value(CFGID_LINE_FWD_CALLER_NAME_DIPLAY + line, gCallerName, (const unsigned char *) "callerName");
+        compare_or_set_boolean_value(CFGID_LINE_FWD_CALLER_NUMBER_DIPLAY + line, gCallerNumber, (const unsigned char *) "callerNumber");
+        compare_or_set_boolean_value(CFGID_LINE_FWD_REDIRECTED_NUMBER_DIPLAY + line, gRedirectedNumber, (const unsigned char *) "redirectedNumber");
+        compare_or_set_boolean_value(CFGID_LINE_FWD_DIALED_NUMBER_DIPLAY + line, gDialedNumber, (const unsigned char *) "dialedNumber");
+        compare_or_set_byte_value(CFGID_LINE_MSG_WAITING_LAMP + line, gMessageWaitingLampPolicy, (const unsigned char *) "messageWaitingLampPolicy");
+        compare_or_set_byte_value(CFGID_LINE_MESSAGE_WAITING_AMWI + line, gMessageWaitingAMWI, (const unsigned char *) "messageWaitingAMWI");
+        compare_or_set_byte_value(CFGID_LINE_RING_SETTING_IDLE + line, gRingSettingIdle, (const unsigned char *) "ringSettingIdle");
+        compare_or_set_byte_value(CFGID_LINE_RING_SETTING_ACTIVE + line, gRingSettingActive, (const unsigned char *) "ringSettingActive");
+        compare_or_set_string_value(CFGID_LINE_CONTACT + line, sipUser, (const unsigned char *) "contact");
+        compare_or_set_int_value(CFGID_LINE_MAXNUMCALLS + line, gMaxNumCalls, (const unsigned char *) "maxNumCalls");
+        compare_or_set_int_value(CFGID_LINE_BUSY_TRIGGER + line, gBusyTrigger, (const unsigned char *) "busyTrigger");
+        compare_or_set_byte_value(CFGID_LINE_AUTOANSWER_ENABLED + line, gAutoAnswerEnabled, (const unsigned char *) "autoAnswerEnabled");
+        compare_or_set_byte_value(CFGID_LINE_CALL_WAITING + line, gCallWaiting, (const unsigned char *) "callWaiting");
+        compare_or_set_string_value(CFGID_LINE_AUTHNAME + line, sipUser, (const unsigned char *)"authName");
+        compare_or_set_string_value(CFGID_LINE_PASSWORD + line, sipPassword, (const unsigned char *)"authPassword");
+    }
+
+    compare_or_set_int_value(CFGID_CCM1_SEC_LEVEL, gDeviceSecurityMode,(const unsigned char *)"deviceSecurityMode");
+    compare_or_set_int_value(CFGID_CCM1_SIP_PORT, gCcm1_sip_port,(const unsigned char *)"ccm1_sip_port");
+    compare_or_set_int_value(CFGID_CCM2_SIP_PORT, gCcm2_sip_port,(const unsigned char *)"ccm2_sip_port");
+    compare_or_set_int_value(CFGID_CCM3_SIP_PORT, gCcm3_sip_port, (const unsigned char *)"ccm3_sip_port");
+
+
+    isSecure = FALSE;
+    sstrncpy(ip, "", MAX_SIP_URL_LENGTH);
+    sstrncpy(option, "User Specific", MAX_SIP_URL_LENGTH);
+
+    compare_or_set_string_value(CFGID_CCM1_ADDRESS+0, sipDomain, (const unsigned char *) "ccm1_addr");
+    compare_or_set_boolean_value(CFGID_CCM1_IS_VALID + 0, gCcm1_isvalid, (const unsigned char *)"ccm1_isvalid");
+    compare_or_set_int_value(CFGID_DSCP_FOR_CALL_CONTROL , gDscpCallControl, (const unsigned char *) "DscpCallControl");
+    compare_or_set_int_value(CFGID_SPEAKER_ENABLED, gSpeakerEnabled, (const unsigned char *) "speakerEnabled");
+
+    if (apply_config == FALSE) {
+        config_get_mac_addr(macaddr);
+
+        CONFIG_DEBUG(CFG_F_PREFIX ": MAC Address IS:  %x:%x:%x:%x:%x:%x  \n",
+                                  "config_get_mac_addr", macaddr[0], macaddr[1],
+                                   macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
+
+        CC_Config_setArrayValue(CFGID_MY_MAC_ADDR, macaddr, MAC_ADDR_SIZE);
+        CC_Config_setArrayValue(CFGID_MY_ACTIVE_MAC_ADDR, macaddr, MAC_ADDR_SIZE);
+    }
+
+    CONFIG_DEBUG(CFG_F_PREFIX "%s \n", "config_parse_element", "phoneServices");
+    CONFIG_DEBUG(CFG_F_PREFIX "%s \n", "config_parse_element", "versionStamp");
+    CONFIG_ERROR(CFG_F_PREFIX "%s new=%s old=%s \n", "config_parser_element", "versionStamp",
+                       "1284570837-bbc096ed-7392-427d-9694-5ce49d5c3acb", g_cfg_version_stamp);
+
+    if (apply_config == FALSE) {
+        memset(g_cfg_version_stamp, 0, sizeof(g_cfg_version_stamp));
+        i = strlen("1284570837-bbc096ed-7392-427d-9694-5ce49d5c3acb");
+        if (i > MAX_CFG_VERSION_STAMP_LEN) {
+            CONFIG_ERROR(CFG_F_PREFIX "config version %d, bigger than allocated space %d\n", "config_parser_element", i, MAX_CFG_VERSION_STAMP_LEN);
+        }
+
+        sstrncpy(g_cfg_version_stamp, "1284570837-bbc096ed-7392-427d-9694-5ce49d5c3acb", sizeof(g_cfg_version_stamp));
+    }
+    else {
+        CONFIG_ERROR(CFG_F_PREFIX "got NULL value for %s\n", "config_parser_element", "versionStamp");
+    }
+
+    CONFIG_DEBUG(CFG_F_PREFIX "%s \n", "config_parser_element", "externalNumberMask");
+    compare_or_set_string_value(CFGID_CCM_EXTERNAL_NUMBER_MASK, gExternalNumberMask, (const unsigned char *) "externalNumberMask");
+
+    /* Set SIP P2P boolean */
+    compare_or_set_boolean_value(CFGID_P2PSIP, gP2PSIP, (const unsigned char *) "p2psip");
+
+    /* Set product version */
+    compare_or_set_string_value(CFGID_VERSION, gVersion, (const unsigned char *) "version");
+
+    /* Set rtcp-mux, right now to always true */
+    compare_or_set_boolean_value(CFGID_RTCPMUX, gRTCPMUX, (const unsigned char *) "rtcpmux");
+
+    /* Set RTP/SAVPF, right now to always true */
+    compare_or_set_boolean_value(CFGID_RTPSAVPF, gRTPSAVPF, (const unsigned char *) "rtpsavpf");
+
+    compare_or_set_boolean_value(CFGID_MAXAVBITRATE, gMAXAVBITRATE, (const unsigned char *) "maxavbitrate");
+
+    compare_or_set_boolean_value(CFGID_MAXCODEDAUDIOBW, gMAXCODEDAUDIOBW, (const unsigned char *) "maxcodedaudiobw");
+
+    compare_or_set_boolean_value(CFGID_USEDTX, gUSEDTX, (const unsigned char *) "usedtx");
+
+    compare_or_set_boolean_value(CFGID_STEREO, gSTEREO, (const unsigned char *) "stereo");
+
+    compare_or_set_boolean_value(CFGID_USEINBANDFEC, gUSEINBANDFEC, (const unsigned char *) "useinbandfec");
+
+    compare_or_set_boolean_value(CFGID_CBR, gCBR, (const unsigned char *) "cbr");
+
+    compare_or_set_boolean_value(CFGID_MAXPTIME, gMAXPTIME, (const unsigned char *) "maxptime");
+
+    compare_or_set_int_value(CFGID_SCTP_PORT, gSCTPPort, (const unsigned char *) "sctp_port");
+
+    compare_or_set_int_value(CFGID_NUM_DATA_STREAMS, gNumDataStreams, (const unsigned char *) "num_data_streams");
+
+    (void) isSecure; // XXX set but not used
+    (void) isValid; // XXX set but not used
+}
+
+void config_setup_server_address (const char *sipDomain) {
+       compare_or_set_string_value(CFGID_CCM1_ADDRESS+0, sipDomain, (const unsigned char *) "ccm1_addr");
+}
+
+void config_setup_transport_udp(const cc_boolean is_udp) {
+       gTransportLayerProtocol = is_udp ? 2 : 4;
+       compare_or_set_int_value(CFGID_TRANSPORT_LAYER_PROT, gTransportLayerProtocol, (const unsigned char *) "transportLayerProtocol");
+}
+
+void config_setup_local_voip_control_port(const int voipControlPort) {
+       gVoipControlPort = voipControlPort;
+       compare_or_set_int_value(CFGID_VOIP_CONTROL_PORT, voipControlPort, (const unsigned char *) "voipControlPort");
+}
+
+void config_setup_remote_voip_control_port(const int voipControlPort) {
+       gCcm1_sip_port = voipControlPort;
+       compare_or_set_int_value(CFGID_CCM1_SIP_PORT, voipControlPort,(const unsigned char *)"ccm1_sip_port");
+}
+
+int config_get_local_voip_control_port() {
+       return gVoipControlPort;
+}
+
+int config_get_remote_voip_control_port() {
+       return gCcm1_sip_port;
+}
+
+const char* config_get_version() {
+       return gVersion;
+}
+
+void config_setup_p2p_mode(const cc_boolean is_p2p) {
+       gP2PSIP = is_p2p;
+       compare_or_set_boolean_value(CFGID_P2PSIP, is_p2p, (const unsigned char *) "p2psip");
+}
+
+void config_setup_sdp_mode(const cc_boolean is_sdp) {
+       gSDPMODE = is_sdp;
+       compare_or_set_boolean_value(CFGID_SDPMODE, is_sdp, (const unsigned char *) "sdpsip");
+}
+
+void config_setup_avp_mode(const cc_boolean is_rtpsavpf) {
+       gRTPSAVPF = is_rtpsavpf;
+       compare_or_set_boolean_value(CFGID_RTPSAVPF, is_rtpsavpf, (const unsigned char *) "rtpsavpf");
+}
+
+/**
+ * Process/Parse the FCP file if specified in the master config file.
+*/
+void config_parser_handle_fcp_file (char* fcpTemplateFile)
+{
+
+    // if no fcp file specified in master config file, then set the default dialplan
+    if (strcmp (fcpTemplateFile, "") == 0)
+    {
+        CC_Config_setFcp(NULL, 0);
+        ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_CONFIG_CHANGED, CC_DEVICE_ID);
+
+        return;
+    }
+
+    ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_CONFIG_CHANGED, CC_DEVICE_ID);
+}
+
+
+/**
+ * Function called as part of registration without using cnf device file download.
+ */
+int config_setup_main( const char *sipUser, const char *sipPassword, const char *sipDomain)
+{
+    config_setup_elements(sipUser, sipPassword, sipDomain);
+       update_security_mode_and_ports();
+
+    // Take care of Fetch and apply of FCP and DialPlan if configured and necessary
+    if (apply_config == FALSE) {
+        config_parser_handle_fcp_file (fcpTemplateFile);
+    }
+
+    return 0;
+}
diff --git a/libs/sipcc/core/common/config_parser.h b/libs/sipcc/core/common/config_parser.h
new file mode 100644 (file)
index 0000000..35730ea
--- /dev/null
@@ -0,0 +1,173 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CONFIG_PARSER_H_
+#define CONFIG_PARSER_H_
+
+#include "cc_constants.h"
+#include "cc_types.h"
+#include "cc_config.h"
+#include "ccapi.h"
+#include "phone_debug.h"
+#include "debug.h"
+#include "prot_configmgr.h"
+#include "call_logger.h"
+#include "sip_common_transport.h"
+#include "sip_ccm_transport.h"
+
+#define MAX_CFG_VERSION_STAMP_LEN 80
+
+/*
+ * This function determine whether the passed config parameter should be used
+ * in comparing the new and old config value for apply-config purpose.  Only
+ * those config ids on whose change phone needs to restart are part of this
+ * function. For remaining parameters, it is assumed that any change can be
+ * applied dynamically.
+ *
+ */
+boolean is_cfgid_in_restart_list(int cfgid);
+
+/*
+ * This function either compare the new and old config value or set the value
+ * for the config_id passed depending upon whether apply-config is true or not.
+ */
+void compare_or_set_byte_value(int cfgid, unsigned char value, const unsigned char * config_name);
+
+/*
+ * This function either compare the new and old config value or set the value
+ * for the config_id passed depending upon whether apply-config is true or not.
+ */
+void compare_or_set_boolean_value(int cfgid, cc_boolean value, const unsigned char * config_name);
+
+/*
+ * This function either compare the new and old config value or set the value
+ * for the config_id passed depending upon whether apply-config is true or not.
+ */
+void compare_or_set_int_value(int cfgid, int value, const unsigned char * config_name);
+
+/*
+ * This function either compare the new and old config value or set the value
+ * for the config_id passed depending upon whether apply-config is true or not.
+ */
+void compare_or_set_string_value (int cfgid, const char* value, const unsigned char * config_name);
+
+/*
+ * config_fetch_dialplan() called to retrieve the dialplan
+ *
+ */
+void config_fetch_dialplan(char *filename);
+
+/*
+ * config_fetch_fcp() called to retrieve the fcp
+ *
+ */
+void config_fetch_fcp(char *filename);
+
+/*
+ * config_set_autoreg_properties
+ *
+ */
+void config_set_autoreg_properties ();
+
+/*
+ * update_security_mode_and_ports
+ *
+ */
+void update_security_mode_and_ports(void);
+
+/*
+ * config_get_mac_addr
+ *
+ * Get the filename that has the mac address and parse the string
+ * convert it into an mac address stored in the bytearray maddr
+*/
+void config_get_mac_addr (char *maddr);
+
+/*
+ * Set the IP and MAC address in the config table
+ */
+void config_set_ccm_ip_mac ();
+
+
+/*
+ * Set up configuration without XML config file.
+ */
+void config_setup_elements ( const char *sipUser, const char *sipPassword, const char *sipDomain);
+
+/*
+ * Set server ip address into config
+ * Same ip address is also used to make a P2P call
+ */
+void config_setup_server_address (const char *sipDomain);
+
+/*
+ * set transport protocol, limited to udp or tcp for now
+ */
+void config_setup_transport_udp(const cc_boolean is_udp);
+
+/*
+ * set local voip port defaults to 5060
+ */
+void config_setup_local_voip_control_port(const int voipControlPort);
+
+/*
+ * set remote voip port defaults to 5060
+ */
+void config_setup_remote_voip_control_port(const int voipControlPort);
+
+/*
+ * get local voip port defaults to 5060
+ */
+int config_get_local_voip_control_port();
+
+/*
+ * get remote voip port defaults to 5060
+ */
+int config_get_remote_voip_control_port();
+
+/*
+ * get ikran version
+ */
+const char* config_get_version();
+
+/*
+ * set p2p mode on or off
+ */
+void config_setup_p2p_mode(const cc_boolean is_p2p);
+
+/*
+ * set sdp mode on or off
+ */
+void config_setup_sdp_mode(const cc_boolean is_sdp);
+
+/*
+ * set avp mode (true == RTP/SAVPF,  false = RTP/SAVP)
+ */
+void config_setup_avp_mode(const cc_boolean is_rtpsavpf);
+
+/**
+* config_minimum_check:
+*
+* @a_node: the initial xml node to consider.
+* @doc: The DOM tree of the xml file
+*
+* Check if minimum set of elements are present in the config file and
+* have values that can be used by sipstack
+*
+*/
+
+/**
+ * Parse the file that is passed in,
+ * walk down the DOM that is created , and get the
+ * xml elements nodes.
+ */
+int config_parser_main( char *config, int complete_config);
+
+/*
+ * Set up configuration without XML config file.
+ */
+int config_setup_main( const char *sipUser, const char *sipPassword, const char *sipDomain);
+
+
+#endif /* CONFIG_PARSER_H_ */
diff --git a/libs/sipcc/core/common/init.c b/libs/sipcc/core/common/init.c
new file mode 100755 (executable)
index 0000000..87770c6
--- /dev/null
@@ -0,0 +1,576 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_in.h"
+#include "cpr_stdlib.h"
+#include "cpr_ipc.h"
+#include "phntask.h"
+#include <stdarg.h>
+#include "configmgr.h"
+#include "debug.h"
+#include "config.h"
+#include "vcm.h"
+#include "dialplan.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "CCProvider.h"
+#include "ccsip_task.h"
+#include "gsm.h"
+#include "misc_apps_task.h"
+#include "plat_api.h"
+#include "ccapp_task.h"
+
+#include "phone_platform_constants.h"
+/** The following defines are used to tune the total memory that pSIPCC
+ * allocates and uses. */
+/** Block size for emulated heap space, i.e. 1kB */
+#define BLK_SZ  1024
+
+/** 5 MB Heap Based on 0.5 MB initial use + 4 MB for calls (20K * 200) + 0.5 MB misc */
+/** The number of supported blocks for the emulated heap space */
+#define MEM_BASE_BLK 500 //500 blocks, ~ 0.5M
+#define MEM_MISC_BLK 500 //500 blocks, ~ 0.5M
+/** Size of the emulated heap space. This is the value passed to the memory
+ * management pre-init procedure. The total memory allocated is
+ * "PRIVATE_SYS_MEM_SIZE + 2*64"  where the additional numbers are for a gaurd
+ * band. */
+#define MEM_PER_CALL_BLK 20 //20 block, ~20k
+#define PRIVATE_SYS_MEM_SIZE ((MEM_BASE_BLK + MEM_MISC_BLK + (MEM_PER_CALL_BLK) * MAX_CALLS) * BLK_SZ)
+
+// used in early init code where config has not been setup
+const boolean gHardCodeSDPMode = TRUE;
+boolean gStopTickTask = FALSE;
+
+
+/*--------------------------------------------------------------------------
+ * Local definitions
+ *--------------------------------------------------------------------------
+ */
+#define  GSMSTKSZ   61440
+
+/*
+ * CNU thread queue sizes.
+ *
+ * On CNU, the message queue size can hold up to 31 entries only.
+ * This is too small for a phone that can support the MAX_CALLS that
+ * is close to or higher than 31 calls simultaneously. In a scenario when
+ * the number of active calls on the phone reaches its MAC_CALLS then
+ * there can be a potential MAX_CALLS that is great ther than 31 events on
+ * a queue to GSM or SIP thread.
+ *
+ * One known scenario is a 7970 phone having 50 held calls where all
+ * calls are not from the same CCM that this phone registers with but
+ * still within the same cluster. If that CCM is restarted, the CCM that
+ * this phone registers with will terminate these calls on the phone by
+ * sending BYEs, SUBSCRIBE (to terminate) DTMF digi collection and etc. to
+ * all these calls very quickly together. Handling these SIP messages
+ * can generate many internal events between SIP and GSM threads including
+ * events that are sent to self (such as GSM which includes applications
+ * that runs as part of GSM). The amount of the events posted on the queues
+ * during this time can be far exceeding than 31 and MAX_CALLS entries.
+ *
+ * The followings define queue sizes for GSM, SIP and the other threads.
+ * The GSM's queue size is defined to be larger than SIP's queue size to
+ * account for self sending event. The formular below is based on the
+ * testing with the above scenario and with 8-3-x phone load. The maximum
+ * queue depth observed for GSM under
+ * this condition is 129 entries and the maximum queue depth of
+ * SIP under the same condition is about 67 entries. Therefore, the queue
+ * depth of GSM thread is given to 3 times MAX_CALLS (or 153) and
+ * 2 times (or 102) for SIP thread for 7970 case.
+ *
+ */
+#define  GSMQSZ        (MAX_CALLS*3) /* GSM message queue size           */
+#define  SIPQSZ        (MAX_CALLS*2) /* SIP message queue size           */
+#define  DEFQSZ         0            /* default message queue size       */
+#define  DEFAPPQSZ     MAX_REG_LINES
+
+/*--------------------------------------------------------------------------
+ * Global data
+ *--------------------------------------------------------------------------
+ */
+
+cprMsgQueue_t ccapp_msgq;
+cprThread_t ccapp_thread;
+
+cprMsgQueue_t sip_msgq;
+cprThread_t sip_thread;
+#ifdef NO_SOCKET_POLLING
+cprThread_t sip_msgqwait_thread;
+#endif
+
+cprMsgQueue_t gsm_msgq;
+cprThread_t gsm_thread;
+
+cprMsgQueue_t misc_app_msgq;
+cprThread_t misc_app_thread;
+
+#ifdef JINDO_DEBUG_SUPPORTED
+cprMsgQueue_t debug_msgq;
+cprThread_t debug_thread;
+#endif
+
+#ifdef EXTERNAL_TICK_REQUIRED
+cprMsgQueue_t ticker_msgq;
+cprThread_t ticker_thread;
+#endif
+
+/* Platform initialized flag */
+boolean platform_initialized = FALSE;
+
+static int thread_init(void);
+
+/*--------------------------------------------------------------------------
+ * External data references
+ * -------------------------------------------------------------------------
+ */
+
+
+/*--------------------------------------------------------------------------
+ * External function prototypes
+ *--------------------------------------------------------------------------
+ */
+extern void gsm_set_initialized(void);
+extern void vcm_init(void);
+extern void dp_init(void *);
+extern cprBuffer_t SIPTaskGetBuffer(uint16_t size);
+
+extern void sip_platform_task_loop(void *arg);
+#ifdef NO_SOCKET_POLLING
+extern void sip_platform_task_msgqwait(void *arg);
+#endif
+extern void GSMTask(void *);
+#ifndef VENDOR_BUILD
+extern void debug_task(void *);
+#endif
+extern void MiscAppTask(void *);
+extern void cpr_timer_tick(void);
+
+extern void cprTimerSystemInit(void);
+extern int32_t ui_clear_mwi(int32_t argc, const char *argv[]);
+void gsm_shutdown(void);
+void dp_shutdown(void);
+void MiscAppTaskShutdown(void);
+void CCAppShutdown(void);
+cprBuffer_t gsm_get_buffer (uint16_t size);
+
+
+
+
+/*--------------------------------------------------------------------------
+ * Local scope function prototypes
+ *--------------------------------------------------------------------------
+ */
+#ifdef EXTERNAL_TICK_REQUIRED
+int TickerTask(void *);
+#endif
+
+void send_protocol_config_msg(void);
+
+/**
+ * ccMemInit()
+ */
+extern
+int ccMemInit(size_t size) {
+    return CPR_SUCCESS;
+}
+
+/**
+ * ccPreInit
+ *
+ * Initialization routine to call before any application level
+ * code initializes.
+ *
+ * Parameters: None
+ *
+ * Return Value: CPR_SUCCESS or CPR_FAILURE
+ */
+
+int
+ccPreInit ()
+{
+       static boolean ccPreInit_called = FALSE;
+
+       if (ccPreInit_called == FALSE) {
+               ccPreInit_called = TRUE;
+               //Initializes the memory first
+               ccMemInit(PRIVATE_SYS_MEM_SIZE);
+               cprPreInit();
+       }
+
+    return CPR_SUCCESS;
+}
+
+int
+ccInit ()
+{
+
+    TNP_DEBUG(DEB_F_PREFIX"started init of SIP call control\n", DEB_F_PREFIX_ARGS(SIP_CC_INIT, "ccInit"));
+
+    platInit();
+
+    strlib_init();
+
+    /*
+     * below should move to cprPreInit. keep it here until then
+     */
+#ifdef _WIN32
+    cprTimerSystemInit();
+#endif
+
+    /* Initialize threads, queues etc. */
+    (void) thread_init();
+
+    platform_initialized = TRUE;
+
+    return 0;
+}
+
+static int
+thread_init ()
+{
+    gStopTickTask = FALSE;
+    /*
+     * This will have already been called for CPR CNU code,
+     * but may be called here for Windows emulation.
+     */
+    (void) cprPreInit();
+
+
+    PHNChangeState(STATE_FILE_CFG);
+
+    /* initialize message queues */
+    sip_msgq = cprCreateMessageQueue("SIPQ", SIPQSZ);
+    gsm_msgq = cprCreateMessageQueue("GSMQ", GSMQSZ);
+
+    if (FALSE == gHardCodeSDPMode) {
+        misc_app_msgq = cprCreateMessageQueue("MISCAPPQ", DEFQSZ);
+    }
+    ccapp_msgq = cprCreateMessageQueue("CCAPPQ", DEFQSZ);
+#ifdef JINDO_DEBUG_SUPPORTED
+    debug_msgq = cprCreateMessageQueue("DEBUGAPPQ", DEFQSZ);
+#endif
+#ifdef EXTERNAL_TICK_REQUIRED
+    ticker_msgq = cprCreateMessageQueue("Ticker", DEFQSZ);
+#endif
+
+
+    /*
+     * Initialize the command parser and debug infrastructure
+     */
+    debugInit();
+
+    /* create threads */
+    ccapp_thread = cprCreateThread("CCAPP Task",
+                                 (cprThreadStartRoutine) CCApp_task,
+                                 GSMSTKSZ, CCPROVIDER_THREAD_RELATIVE_PRIORITY /* pri */, ccapp_msgq);
+    if (ccapp_thread == NULL) {
+        err_msg("failed to create CCAPP task \n");
+    }
+
+#ifdef JINDO_DEBUG_SUPPORTED
+#ifndef VENDOR_BUILD
+    debug_thread = cprCreateThread("Debug Task",
+                                   (cprThreadStartRoutine) debug_task, STKSZ,
+                                   0 /*pri */ , debug_msgq);
+
+    if (debug_thread == NULL) {
+        err_msg("failed to create debug task\n");
+    }
+#endif
+#endif
+
+    /* SIP main thread */
+    sip_thread = cprCreateThread("SIPStack task",
+                                 (cprThreadStartRoutine) sip_platform_task_loop,
+                                 STKSZ, SIP_THREAD_RELATIVE_PRIORITY /* pri */, sip_msgq);
+    if (sip_thread == NULL) {
+        err_msg("failed to create sip task \n");
+    }
+
+#ifdef NO_SOCKET_POLLING
+    /* SIP message wait queue task */
+    sip_msgqwait_thread = cprCreateThread("SIP MsgQueueWait task",
+                                          (cprThreadStartRoutine)
+                                          sip_platform_task_msgqwait,
+                                          STKSZ, SIP_THREAD_RELATIVE_PRIORITY /* pri */, sip_msgq);
+    if (sip_msgqwait_thread == NULL) {
+        err_msg("failed to create sip message queue wait task\n");
+    }
+#endif
+
+    gsm_thread = cprCreateThread("GSM Task",
+                                 (cprThreadStartRoutine) GSMTask,
+                                 GSMSTKSZ, GSM_THREAD_RELATIVE_PRIORITY /* pri */, gsm_msgq);
+    if (gsm_thread == NULL) {
+        err_msg("failed to create gsm task \n");
+    }
+
+    if (FALSE == gHardCodeSDPMode) {
+       misc_app_thread = cprCreateThread("MiscApp Task",
+                       (cprThreadStartRoutine) MiscAppTask,
+                       STKSZ, 0 /* pri */, misc_app_msgq);
+       if (misc_app_thread == NULL) {
+               err_msg("failed to create MiscApp task \n");
+       }
+    }
+
+#ifdef EXTERNAL_TICK_REQUIRED
+    ticker_thread = cprCreateThread("Ticker task",
+                                    (cprThreadStartRoutine) TickerTask,
+                                    STKSZ, 0, ticker_msgq);
+    if (ticker_thread == NULL) {
+        err_msg("failed to create ticker task \n");
+    }
+#endif
+
+    /* Associate the threads with the message queues */
+    (void) cprSetMessageQueueThread(sip_msgq, sip_thread);
+    (void) cprSetMessageQueueThread(gsm_msgq, gsm_thread);
+
+    if (FALSE == gHardCodeSDPMode) {
+       (void) cprSetMessageQueueThread(misc_app_msgq, misc_app_thread);
+    }
+
+    (void) cprSetMessageQueueThread(ccapp_msgq, ccapp_thread);
+#ifdef JINDO_DEBUG_SUPPORTED
+    (void) cprSetMessageQueueThread(debug_msgq, debug_thread);
+#endif
+#ifdef EXTERNAL_TICK_REQUIRED
+    (void) cprSetMessageQueueThread(ticker_msgq, ticker_thread);
+#endif
+
+    /*
+     * initialize debugs of other modules.
+     *
+     * dp_init needs the gsm_msgq id. This
+     * is set in a global variable by the
+     * GSM task running. However due to timing
+     * issues dp_init is sometimes run before
+     * the GSM task has set this variable resulting
+     * in a NULL msgqueue ptr being passed to CPR
+     * which returns an error and does not create
+     * the dialplan timer. Thus pass is the same
+     * data to dp_init that is passed into the
+     * cprCreateThread call for GSM above.
+     */
+    config_init();
+    vcmInit();
+    dp_init(gsm_msgq);
+
+    if (sip_minimum_config_check() != 0) {
+        PHNChangeState(STATE_UNPROVISIONED);
+    } else {
+        PHNChangeState(STATE_CONNECTED);
+    }
+
+    (void) cprPostInit();
+
+    if ( vcmGetVideoCodecList(VCM_DSP_FULLDUPLEX) ) {
+        cc_media_update_native_video_support(TRUE);
+    }
+
+    return (0);
+}
+
+
+#ifdef EXTERNAL_TICK_REQUIRED
+
+uint16_t SecTimer = 50;
+
+unsigned long timeofday_in_seconds = 0;
+
+void
+MAIN0Timer (void)
+{
+    if (SecTimer-- == 0) {
+        SecTimer = 50;
+        timeofday_in_seconds++;
+    }
+    //gtick +=2;
+    cpr_timer_tick();
+}
+
+int
+TickerTask (void *a)
+{
+    TNP_DEBUG(DEB_F_PREFIX"Ticker Task initialized..\n", DEB_F_PREFIX_ARGS(SIP_CC_INIT, "TickerTask"));
+    while (FALSE == gStopTickTask) {
+        cprSleep(20);
+        MAIN0Timer();
+    }
+    return 0;
+}
+#endif
+
+void
+send_protocol_config_msg (void)
+{
+    const char *fname = "send_protocol_config_msg";
+    char *msg;
+
+    TNP_DEBUG(DEB_F_PREFIX"send TCP_DONE message to sip thread..\n", DEB_F_PREFIX_ARGS(SIP_CC_INIT, fname));
+
+    msg = (char *) SIPTaskGetBuffer(4);
+    if (msg == NULL) {
+        TNP_DEBUG(DEB_F_PREFIX"failed to allocate message..\n", DEB_F_PREFIX_ARGS(SIP_CC_INIT, fname));
+        return;
+    }
+    /* send a config done message to the SIP Task */
+    if (SIPTaskSendMsg(TCP_PHN_CFG_TCP_DONE, msg, 0, NULL) == CPR_FAILURE) {
+        err_msg("%s: notify SIP stack ready failed", fname);
+        cpr_free(msg);
+    }
+    gsm_set_initialized();
+    PHNChangeState(STATE_CONNECTED);
+}
+
+
+
+
+
+/*
+ *  Function: send_task_unload_msg
+ *
+ *  Description:
+ *         - send shutdown and thread destroy msg to sip, gsm, ccapp, misc
+ *           threads
+ *  Parameters:  destination thread
+ *
+ *  Returns: none
+ *
+ */
+void
+send_task_unload_msg(cc_srcs_t dest_id)
+{
+    const char *fname = "send_task_unload_msg";
+    uint16_t len = 4;
+    cprBuffer_t  msg =  gsm_get_buffer(len);
+    int  sdpmode = 0;
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    if (msg == NULL) {
+        err_msg("%s: failed to allocate  msg cprBuffer_t\n", fname);
+        return;
+    }
+
+    DEF_DEBUG(DEB_F_PREFIX"send Unload message to %s task ..\n",
+        DEB_F_PREFIX_ARGS(SIP_CC_INIT, fname),
+        dest_id == CC_SRC_SIP ? "SIP" :
+        dest_id == CC_SRC_GSM ? "GSM" :
+        dest_id == CC_SRC_MISC_APP ? "Misc App" :
+        dest_id == CC_SRC_CCAPP ? "CCApp" : "Unknown");
+
+    switch(dest_id) {
+        case CC_SRC_SIP:
+        {
+            /* send this msg so phone can send unRegister msg */
+            SIPTaskPostShutdown(SIP_EXTERNAL, CC_CAUSE_SHUTDOWN, "");
+            /* allow unRegister msg to sent out and shutdown to complete */
+
+            if (!sdpmode) {
+                cprSleep(2000);
+            }
+            /* send a unload message to the SIP Task to kill sip thread*/
+            msg =  SIPTaskGetBuffer(len);
+            if (msg == NULL) {
+                err_msg("%s:%d: failed to allocate sip msg buffer\n", fname);
+                return;
+            }
+
+            if (SIPTaskSendMsg(THREAD_UNLOAD, (cprBuffer_t)msg, len, NULL) == CPR_FAILURE)
+            {
+                cpr_free(msg);
+                err_msg("%s: Unable to send THREAD_UNLOAD msg to sip thread", fname);
+            }
+        }
+        break;
+        case CC_SRC_GSM:
+        {
+            msg =  gsm_get_buffer(len);
+            if (msg == NULL) {
+                err_msg("%s: failed to allocate  gsm msg cprBuffer_t\n", fname);
+                return;
+            }
+            if (CPR_FAILURE == gsm_send_msg(THREAD_UNLOAD, msg, len)) {
+                err_msg("%s: Unable to send THREAD_UNLOAD msg to gsm thread", fname);
+            }
+        }
+        break;
+        case CC_SRC_MISC_APP:
+        {
+            msg = cpr_malloc(len);
+            if (msg == NULL) {
+                err_msg("%s: failed to allocate  misc msg cprBuffer_t\n", fname);
+                return;
+            }
+            if (CPR_FAILURE == MiscAppTaskSendMsg(THREAD_UNLOAD, msg, len)) {
+                err_msg("%s: Unable to send THREAD_UNLOAD msg to Misc App thread", fname);
+            }
+        }
+        break;
+        case CC_SRC_CCAPP:
+        {
+            msg = cpr_malloc(len);
+            if (msg == NULL) {
+                err_msg("%s: failed to allocate  ccapp msg cprBuffer_t\n", fname);
+                return;
+            }
+            if (ccappTaskPostMsg(CCAPP_THREAD_UNLOAD, msg, len, CCAPP_CCPROVIER) == CPR_FAILURE )
+            {
+                err_msg("%s: Unable to send THREAD_UNLOAD msg to CCapp thread", fname);
+            }
+            err_msg("%s:  send UNLOAD msg to CCapp thread good", fname);
+        }
+        break;
+
+        default:
+            err_msg("%s: Unknown destination task passed=%d.", fname, dest_id);
+        break;
+    }
+}
+
+/*
+ *  Function: ccUnload
+ *
+ *  Description:
+ *         - deinit portable runtime.
+ *         - Cleanup call control modules, GSM and SIp Stack
+ *
+ *  Parameters: none
+ *
+ *  Returns: none
+ *
+ */
+void
+ccUnload (void)
+{
+    static const char fname[] = "ccUnload";
+
+    DEF_DEBUG(DEB_F_PREFIX"ccUnload called..\n", DEB_F_PREFIX_ARGS(SIP_CC_INIT, fname));
+    if (platform_initialized == FALSE)
+    {
+        TNP_DEBUG(DEB_F_PREFIX"system is not loaded, ignore unload\n", DEB_F_PREFIX_ARGS(SIP_CC_INIT, fname));
+        return;
+    }
+    /*
+     * We are going to send an unload msg to each of the thread, which on
+     * receiving the msg, will kill itself.
+    */
+    send_task_unload_msg(CC_SRC_SIP);
+    send_task_unload_msg(CC_SRC_GSM);
+
+    if (FALSE == gHardCodeSDPMode) {
+       send_task_unload_msg(CC_SRC_MISC_APP);
+    }
+
+    send_task_unload_msg(CC_SRC_CCAPP);
+
+    cprSleep(200);
+
+    gStopTickTask = TRUE;
+}
+
diff --git a/libs/sipcc/core/common/logger.c b/libs/sipcc/core/common/logger.c
new file mode 100755 (executable)
index 0000000..6715c62
--- /dev/null
@@ -0,0 +1,87 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_string.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "stdarg.h"
+#include "logger.h"
+#include "logmsg.h"
+#include "phone_debug.h"
+#include "text_strings.h"
+#include "uiapi.h"
+#include "platform_api.h"
+#include "prot_configmgr.h"
+
+#define MAX_LOG_CACHE_ENTRIES 20
+
+/*
+ * Log a message.
+ * Cause the message to be printed to the console port
+ * as well, the log is stored in a local data area for
+ * later viewing
+ */
+void
+log_msg (int phrase_index, ...)
+{
+    char phrase_buf[LOG_MAX_LEN * 4];
+    char status_msg[LOG_MAX_LEN * 4];
+    va_list ap;
+
+    /*
+     * Make sure that the phrase index is valid.
+     */
+    if (phrase_index == 0) {
+        return;
+    }
+
+    /*
+     * Get the translated phrase index from the Java code.
+     */
+    if (platGetPhraseText(phrase_index, phrase_buf, (LOG_MAX_LEN * 4)) == CPR_FAILURE) {
+        return;
+    }
+
+    /*
+     * If extra data is required, sprintf this into the status message buffer
+     */
+    va_start(ap, phrase_index);
+    vsprintf(status_msg, phrase_buf, ap);
+    va_end(ap);
+
+    err_msg("%%%s\n", status_msg);
+
+    /*
+     * For now, do not send the Registration messages over to the Java Status
+     * Logs.  They come out too fast and will overwhelm the existing logging
+     * mechanism.  We will need to implement a new mechanism in order to put
+     * these in the phone's status menu.
+     */
+    switch (phrase_index) {
+    case LOG_REG_MSG:
+    case LOG_REG_RED_MSG:
+    case LOG_REG_AUTH_MSG:
+    case LOG_REG_AUTH_HDR_MSG:
+    case LOG_REG_AUTH_SCH_MSG:
+    case LOG_REG_CANCEL_MSG:
+    case LOG_REG_AUTH:
+    case LOG_REG_AUTH_ACK_TMR:
+    case LOG_REG_AUTH_NO_CRED:
+    case LOG_REG_AUTH_UNREG_TMR:
+    case LOG_REG_RETRY:
+    case LOG_REG_UNSUPPORTED:
+    case LOG_REG_AUTH_SERVER_ERR:
+    case LOG_REG_AUTH_GLOBAL_ERR:
+    case LOG_REG_AUTH_UNKN_ERR:
+        return;
+
+    default:
+        break;
+    }
+
+    ui_log_status_msg(status_msg);
+}
+
+
diff --git a/libs/sipcc/core/common/logger.h b/libs/sipcc/core/common/logger.h
new file mode 100644 (file)
index 0000000..3a1465b
--- /dev/null
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _LOGGER_INCLUDED_H
+#define _LOGGER_INCLUDED_H
+
+#define LOG_MAX_LEN 64
+
+/*
+ * short form helper macros
+ */
+void log_msg(int log, ...);
+void log_clear(int msg);
+char *get_device_name();
+
+#endif /* _LOGGER_INCLUDED_H */
diff --git a/libs/sipcc/core/common/logmsg.h b/libs/sipcc/core/common/logmsg.h
new file mode 100644 (file)
index 0000000..29ee709
--- /dev/null
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _LOGMSG_INCLUDED_H
+#define _LOGMSG_INCLUDED_H
+
+#include "logger.h"
+
+/*
+ * Config messages
+ * The number denotes the string location in the Phone dictionary file
+ */
+#define LOG_CFG_PARSE_DIAL 1074 //"%d Error(s) Parsing: %s"
+
+/*
+ * SIP messages
+ * The number denotes the string location in the Phone dictionary file
+ */
+#define LOG_REG_MSG             1058 //"REG send failure: REGISTER"
+#define LOG_REG_RED_MSG         1059 //"REG send failure: REGISTER Redirected"
+#define LOG_REG_AUTH_MSG        1060 //"REG send failure: REGISTER auth"
+#define LOG_REG_AUTH_HDR_MSG    1061 //"REG send failure: REGISTER auth hdr"
+#define LOG_REG_AUTH_SCH_MSG    1062 //"REG send failure: REGISTER auth scheme"
+#define LOG_REG_CANCEL_MSG      1063 //"REG send failure: CANCEL REGISTER"
+
+#define LOG_REG_AUTH            1064 //"REG auth failed: %s"
+#define LOG_REG_AUTH_ACK_TMR    1065 //"REG auth failed: ack timer"
+#define LOG_REG_AUTH_NO_CRED    1066 //"REG auth failed: no more credentials"
+#define LOG_REG_AUTH_UNREG_TMR  1067 //"REG auth failed: unreg ack timer"
+#define LOG_REG_RETRY           1068 //"REG retries exceeded"
+#define LOG_REG_UNSUPPORTED     1069 //"REG msg unsupported: %s"
+
+#define LOG_REG_AUTH_SERVER_ERR 1070 //"REG auth failed: in %d, server error"
+#define LOG_REG_AUTH_GLOBAL_ERR 1071 //"REG auth failed: in %d, global error"
+#define LOG_REG_AUTH_UNKN_ERR   1072 //"REG auth failed: in %d, ??? error"
+
+#define LOG_REG_BACKUP          1073 //"REG Not Registered to Backup Proxy"
+
+#define LOG_REG_EXPIRE          1076 //"REG Expires time too small"
+
+#endif /* _LOGMSG_INCLUDED_H */
diff --git a/libs/sipcc/core/common/misc.c b/libs/sipcc/core/common/misc.c
new file mode 100644 (file)
index 0000000..0fe4bf1
--- /dev/null
@@ -0,0 +1,389 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "cpr.h"
+#include "phone_debug.h"
+#include "cc_debug.h"
+#include "phone.h"
+#include "cpr_socket.h"
+#include "prot_configmgr.h"
+#include "debug.h"
+#include "cpr_string.h"
+#include "cpr_stdlib.h"
+
+/*--------------------------------------------------------------------------
+ * Local definitions
+ *--------------------------------------------------------------------------
+ */
+
+/* NTP related local data */
+#define MAX_NTP_MONTH_STR_LEN     4
+#define MAX_NTP_MONTH_ARRAY_SIZE  12
+#define MAX_NTP_DATE_HDR_STR_LEN  128
+#define MAX_NTP_TOKEN_BUF_LEN     16
+
+/* The number of arguments (argc) used in the show command */
+#define NUM_OF_SHOW_ARGUMENTS 2
+
+static int last_month = 99;
+static char last_month_str[MAX_NTP_MONTH_STR_LEN] = "";
+static const char *month_ar[MAX_NTP_MONTH_ARRAY_SIZE] = {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/*--------------------------------------------------------------------------
+ * External function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+extern void platform_set_time(int32_t gmt_time);
+void SipNtpUpdateClockFromCCM(void);
+
+
+/*--------------------------------------------------------------------------
+ * Local scope function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+
+/*
+ * This function finds month (0 to 11) from month name
+ * listed above in month_ar[]. This is used to convert the
+ * date header from CCM to derive time/date.
+ */
+static boolean
+set_month_from_str (char *month_str)
+{
+    boolean ret_val = FALSE;
+    const char * fname = "set_month_from_str";
+    int i;
+
+    if (month_str) {
+        if (strncmp(month_str, last_month_str, 3) != 0) {
+            for (i = 0; i < 12; i++) {
+                if (strncmp(month_str, month_ar[i], 3) == 0) {
+                    sstrncpy(last_month_str, month_str, sizeof(last_month_str));
+                    last_month = i;
+                    ret_val = TRUE;
+                    break;
+                }
+            }
+        } else {
+            ret_val = TRUE;
+        }
+    } else {
+        TNP_DEBUG(DEB_F_PREFIX "Input month_str is NULL!!!! \n", DEB_F_PREFIX_ARGS(PLAT_API, fname));
+    }
+    return (ret_val);
+}
+
+
+
+/*
+ * MISC platform stubs
+ * These stubs are mostly NOPs for TNP but they are referred from common code
+ * and this avoids ifdefs in the code.
+ *
+ */
+static uint16_t PHNState = STATE_CONNECTED;
+
+/* ccsip_core.o */
+uint16_t
+PHNGetState (void)
+{
+    return (PHNState);
+}
+
+void
+PHNChangeState (uint16_t state)
+{
+    PHNState = state;
+}
+
+/* ccsip_platform.o */
+void
+phone_reset (DeviceResetType resetType)
+{
+    return;
+}
+
+
+/*
+ * Methods below should be moved to plat as they are exported as an external API.
+ * For now keeping all miscellaneous methods here.
+ */
+extern void config_get_value (int id, void *buffer, int length);
+
+/*logger.c */
+
+/*
+ * Clear an entry or multiple entries in the
+ * log table.  The passed in log message is used
+ * for partial matching, in order to figure out
+ * what logs need to be cleared.
+ *
+ * The TNP Status Message screen does not have any
+ * facility to "clear" log messages.
+ */
+void
+log_clear (int msg)
+{
+}
+
+
+/**
+ *
+ * Indicates if the (preferred) network interface has changed (e.g., dock/undock)
+ *
+ * @param  none
+ *
+ *
+ * @return true if the (preferred) network interface has changed since last query
+ * @return false if the (preferred) network interface has not changed
+ */
+boolean plat_is_network_interface_changed (void)
+{
+    return(FALSE);
+}
+
+
+/**
+ * give the platform IPV6 IP address.
+ *
+ * @param[in/out] ip_addr - pointer to the cpr_ip_addr_t. The
+ *                          result IP address will be populated in this
+ *                          structure.
+ *
+ * @return None.
+ */
+void
+platform_get_ipv6_address (cpr_ip_addr_t *ip_addr)
+{
+    //config_get_value(CFGID_IP_ADDR_MODE, &ip_mode, sizeof(ip_mode));
+    //Todo IPv6: Hack to get the IPV6 address
+    //if (ip_mode == CPR_IP_MODE_IPV6 || ip_mode == CPR_IP_MODE_DUAL) {
+    //}
+    ip_addr->type = CPR_IP_ADDR_IPV6;
+    ip_addr->u.ip6.addr.base8[15] = 0x65;
+    ip_addr->u.ip6.addr.base8[14] = 0xfb;
+    ip_addr->u.ip6.addr.base8[13] = 0xb1;
+    ip_addr->u.ip6.addr.base8[12] = 0xfe;
+    ip_addr->u.ip6.addr.base8[11] = 0xff;
+    ip_addr->u.ip6.addr.base8[10] = 0x11;
+    ip_addr->u.ip6.addr.base8[9] = 0x11;
+    ip_addr->u.ip6.addr.base8[8] = 0x02;
+    ip_addr->u.ip6.addr.base8[7] = 0x01;
+    ip_addr->u.ip6.addr.base8[6] = 0x00;
+    ip_addr->u.ip6.addr.base8[5] = 0x18;
+    ip_addr->u.ip6.addr.base8[4] = 0x0c;
+    ip_addr->u.ip6.addr.base8[3] = 0xb8;
+    ip_addr->u.ip6.addr.base8[2] = 0x0d;
+    ip_addr->u.ip6.addr.base8[1] = 0x01;
+    ip_addr->u.ip6.addr.base8[0] = 0x20;
+
+    return;
+}
+
+/**
+ * give the mac address string
+ *
+ * @param addr - mac address string (OUTPUT)
+ *
+ * @return none
+ */
+void
+platform_get_wired_mac_address (unsigned char *addr)
+{
+    config_get_value(CFGID_MY_MAC_ADDR, addr, 6);
+    TNP_DEBUG(DEB_F_PREFIX"Wired MacAddr:from Get Val: %04x:%04x:%04x",
+            DEB_F_PREFIX_ARGS(PLAT_API, "platform_get_wired_mac_address"),
+              addr[0] * 256 + addr[1], addr[2] * 256 + addr[3],
+              addr[4] * 256 + addr[5]);
+}
+
+/**
+ * Get active mac address if required
+ *
+ * @param addr - mac address string (OUTPUT)
+ *
+ * @return none
+ */
+void
+platform_get_active_mac_address (unsigned char *addr)
+{
+    config_get_value(CFGID_MY_ACTIVE_MAC_ADDR, addr, 6);
+    TNP_DEBUG(DEB_F_PREFIX"ActiveMacAddr:from Get Val: %04x:%04x:%04x",
+            DEB_F_PREFIX_ARGS(PLAT_API, "platform_get_mac_address"),
+              addr[0] * 256 + addr[1], addr[2] * 256 + addr[3],
+              addr[4] * 256 + addr[5]);
+}
+
+/**
+ * give the platform IPV4 IP address.
+ *
+ * @param[in/out] ip_addr - pointer to the cpr_ip_addr_t. The
+ *                          result IP address will be populated in this
+ *                          structure.
+ *
+ * @return  None.
+ */
+void
+platform_get_ipv4_address (cpr_ip_addr_t *ip_addr)
+{
+    config_get_value(CFGID_MY_IP_ADDR, ip_addr, sizeof(cpr_ip_addr_t));
+    ip_addr->type = CPR_IP_ADDR_IPV4;
+
+    return;
+}
+
+uint32_t
+IPNameCk (char *name, char *addr_error)
+{
+    char *namePtr = name;
+    char string[4] = { 0, 0, 0, 0 };
+    int x = 0;
+    int i = 0;
+    uint32_t temp, ip_addr = 0;
+    char ip_addr_out[MAX_IPADDR_STR_LEN];
+    unsigned long strtoul_result;
+    char *strtoul_end;
+
+    /* Check if valid IPv6 address */
+    if (cpr_inet_pton(AF_INET6, name, ip_addr_out)) {
+        *addr_error = FALSE;
+        return TRUE;
+    }
+    *addr_error = TRUE;
+    while (*namePtr != 0) {
+        if ((*namePtr >= 0x30) && (*namePtr <= 0x39)) {
+            if (x > 2)
+                return (0);
+            string[x++] = *namePtr++;
+        } else {
+            if (*namePtr == 0x2e) {
+                if (i > 3)
+                    return (0);
+                namePtr++;
+                x = 0;
+
+                errno = 0;
+                strtoul_result = strtoul(string, &strtoul_end, 10);
+
+                if (errno || string == strtoul_end || strtoul_result > 255) {
+                    return 0;
+                }
+
+                temp = (uint32_t) strtoul_result;
+
+                ip_addr |= temp << (24 - (i * 8));
+                string[0] = 0;
+                string[1] = 0;
+                string[2] = 0;
+                i++;
+            } else
+                return (0);     // not an IP address
+        }
+    }
+
+    if (i == 3) {
+        errno = 0;
+        strtoul_result = strtoul(string, &strtoul_end, 10);
+
+        if (errno || string == strtoul_end || strtoul_result > 255) {
+            return 0;
+        }
+
+        temp = (uint32_t) strtoul_result;
+
+        ip_addr |= temp;
+        *addr_error = FALSE;
+        return (ntohl(ip_addr));
+    } else {
+        return 0;
+    }
+}
+
+/**
+ * @brief Given a msg buffer, returns a pointer to the buffer's header
+ *
+ * The cprGetSysHeader function retrieves the system header buffer for the
+ * passed in message buffer.
+ *
+ * @param[in] buffer  pointer to the buffer whose sysHdr to return
+ *
+ * @return        Abstract pointer to the msg buffer's system header
+ *                or #NULL if failure
+ */
+void *
+cprGetSysHeader (void *buffer)
+{
+    phn_syshdr_t *syshdr;
+
+    /*
+     * Stinks that an external structure is necessary,
+     * but this is a side-effect of porting from IRX.
+     */
+    syshdr = cpr_calloc(1, sizeof(phn_syshdr_t));
+    if (syshdr) {
+        syshdr->Data = buffer;
+    }
+    return (void *)syshdr;
+}
+
+/**
+ * @brief Called when the application is done with this system header
+ *
+ * The cprReleaseSysHeader function returns the system header buffer to the
+ * system.
+ * @param[in] syshdr  pointer to the sysHdr to be released
+ *
+ * @return        none
+ */
+void
+cprReleaseSysHeader (void *syshdr)
+{
+    if (syshdr == NULL) {
+        CPR_ERROR("cprReleaseSysHeader: Sys header pointer is NULL\n");
+        return;
+    }
+
+    cpr_free(syshdr);
+}
+
+/**
+ * @brief An internal function to update the system header
+ *
+ * A CPR-only function. Given a sysHeader and data, this function fills
+ * in the data.  The purpose for this function is to help prevent CPR
+ * code from having to know about the phn_syshdr_t layout.
+ *
+ * @param[in] buffer    pointer to a syshdr from a successful call to
+ *                  cprGetSysHeader
+ * @param[in] cmd       command to place in the syshdr buffer
+ * @param[in] len       length to place in the syshdr buffer
+ * @param[in] timerMsg  msg being sent to the calling thread
+ *
+ * @return          none
+ *
+ * @pre (buffer != NULL)
+ */
+void
+fillInSysHeader (void *buffer, uint16_t cmd, uint16_t len, void *timerMsg)
+{
+    phn_syshdr_t *syshdr;
+
+    syshdr = (phn_syshdr_t *) buffer;
+    syshdr->Cmd = cmd;
+    syshdr->Len = len;
+    syshdr->Usr.UsrPtr = timerMsg;
+    return;
+}
+
diff --git a/libs/sipcc/core/common/plat.c b/libs/sipcc/core/common/plat.c
new file mode 100644 (file)
index 0000000..bbbcf61
--- /dev/null
@@ -0,0 +1,89 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string.h>
+#include "cpr.h"
+#include "phone_debug.h"
+#include "CCProvider.h"
+#include "ccsip_pmh.h"
+#include "sessionTypes.h"
+#include "ccapp_task.h"
+
+// Why don't we modify strlib_malloc to handle NULL?
+#define STRLIB_CREATE(str)  (str)?strlib_malloc((str), strlen((str))):strlib_empty()
+
+void
+platform_apply_config (char * configVersionStamp,
+                       char * dialplanVersionStamp,
+                       char * fcpVersionStamp,
+                       char * cucmResult,
+                       char * loadId,
+                       char * inactiveLoadId,
+                       char * loadServer,
+                       char * logServer,
+                       boolean ppid);
+/**
+ * This function calls the JNI function to sends the information received
+ * in apply-config NOTIFY message to Java side.
+ *
+ * @param   configVersionStamp - version stamp for config file
+ * @param   dialplanVersionStamp - version stamp for the dialplan file
+ * @param   fcpVersionStamp - version stamp for the softkey file (?)
+ * @param   cucmResult  - CUCM result after applying config by CUCM
+ * @param   loadId  - loadId to upgrade as requested by CUCM
+ * @param   inactiveLoadId  - inactive loadId for inactive partition as requested by CUCM
+ * @param   loadServer  - load server form where to pick loadId
+ * @param   logServer  - log server for logging output of peer to peer upgrade.
+ * @param   ppid  - specify whether peer to peer upgrade is enabled/disabled.
+ *
+ * @return  none
+ */
+void
+platform_apply_config (char * configVersionStamp,
+                       char * dialplanVersionStamp,
+                       char * fcpVersionStamp,
+                       char * cucmResult,
+                       char * loadId,
+                       char * inactiveLoadId,
+                       char * loadServer,
+                       char * logServer,
+                       boolean ppid)
+{
+    static const char fname[] = "platform_apply_config";
+    session_mgmt_t msg;
+
+    fcpVersionStamp = (fcpVersionStamp != NULL) ? fcpVersionStamp : "";
+
+    /// Print the arguments
+    CCAPP_DEBUG(DEB_F_PREFIX"   configVersionStamp=%s \ndialplanVersionStamp=%s"
+           "\nfcpVersionStamp=%s \ncucmResult=%s "
+           "\nloadId=%s \ninactiveLoadId=%s \nloadServer=%s \nlogServer=%s "
+           "\nppid=%s\n", DEB_F_PREFIX_ARGS(PLAT_API, fname),
+           (configVersionStamp != NULL) ? configVersionStamp : "",
+           (dialplanVersionStamp != NULL) ? dialplanVersionStamp:"",
+           fcpVersionStamp,
+           cucmResult != NULL ? cucmResult: "",
+           (loadId != NULL) ? loadId : "",
+           (inactiveLoadId != NULL) ? inactiveLoadId : "",
+           (loadServer != NULL) ? loadServer : "",
+           (logServer != NULL) ? logServer : "",
+           ppid == TRUE? "True": "False");
+
+
+    // following data is freed in function freeSessionMgmtData()
+    msg.func_id = SESSION_MGMT_APPLY_CONFIG;
+    msg.data.config.config_version_stamp = STRLIB_CREATE(configVersionStamp);
+    msg.data.config.dialplan_version_stamp = STRLIB_CREATE(dialplanVersionStamp);
+    msg.data.config.fcp_version_stamp = STRLIB_CREATE(fcpVersionStamp);
+    msg.data.config.cucm_result = STRLIB_CREATE(cucmResult);
+    msg.data.config.load_id = STRLIB_CREATE(loadId);
+    msg.data.config.inactive_load_id = STRLIB_CREATE(inactiveLoadId);
+    msg.data.config.load_server = STRLIB_CREATE(loadServer);
+    msg.data.config.log_server = STRLIB_CREATE(logServer);
+    msg.data.config.ppid = ppid;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_MGMT, &msg, sizeof(session_mgmt_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_DEBUG(DEB_F_PREFIX"failed to send platform_apply_config msg\n", DEB_F_PREFIX_ARGS(PLAT_API, fname));
+    }
+}
diff --git a/libs/sipcc/core/common/platform_api.c b/libs/sipcc/core/common/platform_api.c
new file mode 100755 (executable)
index 0000000..233ce10
--- /dev/null
@@ -0,0 +1,396 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <time.h>
+#include <string.h>
+#include "cpr.h"
+#include "cpr_string.h"
+#include "phone_debug.h"
+#include "prot_configmgr.h"
+#include "phone.h"
+#include "CCProvider.h"
+#include "cpr_stdlib.h"
+#include "ccsip_pmh.h"
+#include "platform_api.h"
+#include <fcntl.h>
+#include "ccapp_task.h"
+
+/*--------------------------------------------------------------------------
+ * Local definitions
+ *--------------------------------------------------------------------------
+ */
+#define PLT_F_PREFIX "PLT : %s : " // requires 1 arg: fname
+// Why don't we modify strlib_malloc to handle NULL?
+#define STRLIB_CREATE(str)  (str)?strlib_malloc((str), strlen((str))):strlib_empty()
+
+/*--------------------------------------------------------------------------
+ * Global data
+ *--------------------------------------------------------------------------
+ */
+
+/*--------------------------------------------------------------------------
+ * External function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+void platform_sync_cfg_vers(char *cfg_ver, char *dp_ver, char *softkey_ver);
+void platform_reg_fallback_ind(int fallback_to);
+void platform_reg_failover_ind(int failover_to);
+
+
+/*--------------------------------------------------------------------------
+ * Local scope function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+
+/**
+ * give the platform IP address mode. This is a temporary function
+ * until config_get_value(CFGID_IP_ADDR_MODE, &ip_mode, sizeof(ip_mode))
+ * is fully implemented (P2).
+ *
+ * @param none
+ *
+ * @return CPR_IP_MODE_IPV4,
+ *         CPR_IP_MODE_IPV6,
+ *         CPR_IP_MODE_DUAL
+ */
+cpr_ip_mode_e
+platform_get_ip_address_mode (void)
+{
+    cpr_ip_mode_e ip_mode;
+
+    //config_get_value(CFGID_IP_ADDR_MODE, &ip_mode, sizeof(ip_mode));
+
+    /* fake mode to what ever mode under test here */
+    ip_mode = CPR_IP_MODE_IPV4;
+
+    return (ip_mode);
+}
+
+
+/**
+ * Send a reset or restart request to the adapter
+ *
+ * @param    action - reset or restart
+ * @return   none
+ *
+ */
+void
+platform_reset_req (DeviceResetType action)
+{
+    static const char fname[] = "platform_reset_req";
+    feature_update_t msg;
+
+    DEF_DEBUG(DEB_F_PREFIX"***********%s, requested***********\n",
+            DEB_F_PREFIX_ARGS(PLAT_API, fname),
+            (action==1)? "RESET":"RESTART");
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_SERVICE_CONTROL_REQ;
+    msg.update.ccFeatUpd.data.reset_type = action;
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_DEBUG(DEB_F_PREFIX"failed to send platform_reset_req(%d) msg \n", DEB_F_PREFIX_ARGS(PLAT_API, fname), action);
+    }
+}
+
+/**
+ * Ask the platform to sync with versions got from call control
+ * or outside entity.  As part of the sync, platform downloads
+ * the files if required.
+ *
+ * @param   cfg_ver     - version stamp for config file
+ * @param   dp_ver      - version stamp for the dialplan file
+ * @param   softkey_ver - version stamp for the softkey file.
+ *
+ * @return  none
+ */
+void
+platform_sync_cfg_vers (char *cfg_ver, char *dp_ver, char *softkey_ver)
+{
+    static const char fname[] = "platform_sync_cfg_vers";
+    char empty_string[] = "";
+    feature_update_t msg;
+
+    if (cfg_ver == NULL) {
+        cfg_ver = empty_string;
+    }
+    if (dp_ver == NULL) {
+        dp_ver = empty_string;
+    }
+    if (softkey_ver == NULL) {
+        softkey_ver = empty_string;
+    }
+
+    CCAPP_DEBUG(DEB_F_PREFIX"cfg_ver=%s dp_ver=%s sk_ver=%s\n", DEB_F_PREFIX_ARGS(PLAT_API, fname),
+        cfg_ver, dp_ver, softkey_ver);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_SYNC_CONFIG_VERSION;
+    msg.update.ccFeatUpd.data.cfg_ver_data.cfg_ver = strlib_malloc(cfg_ver, strlen(cfg_ver));
+    msg.update.ccFeatUpd.data.cfg_ver_data.dp_ver = strlib_malloc(dp_ver, strlen(dp_ver));
+    msg.update.ccFeatUpd.data.cfg_ver_data.softkey_ver = strlib_malloc(softkey_ver, strlen(softkey_ver));
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_DEBUG(DEB_F_PREFIX"failed to send platform_sync_cfg_vers msg \n",
+                DEB_F_PREFIX_ARGS(PLAT_API, fname));
+    }
+}
+
+
+/**
+ *
+ * Tell platform to adjust the time to the gmt_time received from outside
+ * For sip the "outside" is from the "Date" header coming from a CCM or
+ * any other Server
+ *
+ * @param       gmt_time   - GMT time in seconds
+ *
+ * @return      none
+ */
+void
+platform_set_time (long gmt_time)
+{
+    static const char fname[] = "platform_set_time";
+    session_mgmt_t msg;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"setting time to=%ld", DEB_F_PREFIX_ARGS(PLAT_API, fname), gmt_time);
+
+    msg.func_id = SESSION_MGMT_SET_TIME;
+    msg.data.time.gmt_time = gmt_time;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_MGMT, &msg, sizeof(session_mgmt_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_DEBUG(DEB_F_PREFIX"failed to send platform_set_time msg\n", DEB_F_PREFIX_ARGS(PLAT_API, fname));
+    }
+}
+
+
+/**
+ *
+ * Indicate to the platform that reg manager wants to failover to a Call
+ * control indicated in failover_to.  A corresponding ccRegFailoverRsp()
+ * will be issued by the platform once the failover indication is processed.
+ *
+ * @param   failover_to - type of call control,
+ *                        e.g. cip_sipcc_CcMgmtConst_CC_TYPE_CCM
+ * @return  none
+ */
+void
+platform_reg_failover_ind (int failover_to)
+{
+    static const char fname[] = "platform_reg_failover_ind";
+    feature_update_t msg;
+
+    DEF_DEBUG(DEB_F_PREFIX"***********Failover to %s=%d ***********\n",
+            DEB_F_PREFIX_ARGS(PLAT_API, fname),
+            failover_to == CC_TYPE_CCM ? "CC_TYPE_CCM" :
+            "Other", failover_to);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = CCAPP_FAILOVER_IND;
+    msg.update.ccFeatUpd.data.line_info.info = failover_to;
+
+    if ( ccappTaskPostMsg(CCAPP_FAILOVER_IND, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(PLT_F_PREFIX"failed to send platform_reg_failover_ind(%d) msg \n", fname, failover_to);
+    }
+
+}
+
+
+/**
+ * Indicate to the platform that reg manager intends to fallback to
+ * primary CCM.  Currently the fallback_to is always to CC_TYPE_CCM.
+ *
+ * @param   fallback_to - type of call control,
+ *                        e.g. cip_sipcc_CcMgmtConst_CC_TYPE_CCM
+ *
+ * @return  none
+ */
+void
+platform_reg_fallback_ind (int fallback_to)
+{
+    static const char fname[] = "platform_reg_fallback_ind";
+    feature_update_t msg;
+
+    DEF_DEBUG(DEB_F_PREFIX"***********Fallback to %d CUCM.***********\n",
+            DEB_F_PREFIX_ARGS(PLAT_API, fname),
+                                fallback_to);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = CCAPP_FALLBACK_IND;
+    msg.update.ccFeatUpd.data.line_info.info = fallback_to;
+
+    if ( ccappTaskPostMsg(CCAPP_FALLBACK_IND, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(PLT_F_PREFIX"failed to send platform_reg_fallback_ind(%d) msg \n", fname, fallback_to);
+    }
+}
+
+/**
+ *
+ * Indicate to the platform that current fallback activity
+ * is complete i.e. either success or encountered an error
+ * and that the platform should put the User interaction back
+ * in service.
+ *
+ * @param  none
+ *
+ * @return none
+ */
+void
+platform_reg_fallback_cfm (void)
+{
+    static const char fname[] = "platform_reg_fallback_cfm";
+
+    DEF_DEBUG(DEB_F_PREFIX"***********Fallback completed.***********\n",
+            DEB_F_PREFIX_ARGS(PLAT_API, fname));
+}
+
+/**
+ *
+ * Indicate to the platform that current failover activity
+ * is complete i.e. either success or encountered an error
+ * and that the platform should put the User interaction back
+ * in service.
+ *
+ * @param  none
+ *
+ * @return none
+ */
+void
+platform_reg_failover_cfm (void)
+{
+    static const char fname[] = "platform_reg_failover_cfm";
+
+    DEF_DEBUG(DEB_F_PREFIX"***********Failover completed.***********\n",
+            DEB_F_PREFIX_ARGS(PLAT_API, fname));
+}
+
+
+/**
+ *
+ * Notify the platform that call control has shut down.
+ *
+ * @param  none
+ *
+ * @return none
+ */
+void
+shutdownCCAck (void)
+{
+    static const char fname[] = "shutdownCCAck";
+    feature_update_t msg;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(PLAT_API, fname));
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = CCAPP_SHUTDOWN_ACK;
+
+    if ( ccappTaskPostMsg(CCAPP_SHUTDOWN_ACK, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(PLT_F_PREFIX"failed to send shutdownCCAck(%d) msg \n", fname);
+    }
+}
+
+/**
+ * Notify the platform about the change in call control mode.
+ *
+ * @param  mode - the call control mode.
+ *                                       cip_sipcc_CcMgmtConst_CC_TYPE_CCM
+ *                                    or cip_sipcc_CcMgmtConst_CC_TYPE_OTHER
+ *
+ * @return none
+ */
+void
+platform_cc_mode_notify (int mode)
+{
+    static const char fname[] = "platform_cc_mode_notify";
+    feature_update_t msg;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"mode =%d\n", DEB_F_PREFIX_ARGS(PLAT_API, fname), mode);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = CCAPP_MODE_NOTIFY;
+    msg.update.ccFeatUpd.data.line_info.info = mode;
+
+    if ( ccappTaskPostMsg(CCAPP_MODE_NOTIFY, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(PLT_F_PREFIX"failed to send platform_cc_mode_notify(%d) msg \n", fname, mode);
+    }
+}
+
+
+int
+platform_get_phrase_text (int ndx, char *outstr, uint32_t len)
+{
+    static const char fname[] = "platform_get_phrase_text";
+    session_mgmt_t msg;
+
+    CCAPP_DEBUG(DEB_F_PREFIX "index=%d\n", DEB_F_PREFIX_ARGS(PLAT_API, fname), ndx);
+
+    msg.func_id = SESSION_MGMT_GET_PHRASE_TEXT;
+    msg.data.phrase_text.ndx = ndx;
+    msg.data.phrase_text.outstr = outstr;
+    msg.data.phrase_text.len = len;
+
+    ccappSyncSessionMgmt(&msg);
+
+    return msg.data.phrase_text.ret_val;
+}
+
+
+/*called from configapp.c.  This routine will set the kpml
+  value on the java side and trigger it down to c-side.
+  Also, it will reinitialize the dialplan. */
+void
+update_kpmlconfig(int kpmlVal)
+{
+    static const char fname[] = "update_kpmlconfig";
+    session_mgmt_t msg;
+
+    CCAPP_DEBUG(DEB_F_PREFIX "kpml=%d\n", DEB_F_PREFIX_ARGS(PLAT_API, fname), kpmlVal);
+
+    msg.func_id = SESSION_MGMT_UPDATE_KPMLCONFIG;
+    msg.data.kpmlconfig.kpml_val = kpmlVal;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_MGMT, &msg, sizeof(session_mgmt_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_DEBUG(DEB_F_PREFIX"failed to send update_kpmlconfig msg\n", DEB_F_PREFIX_ARGS(PLAT_API, fname));
+    }
+}
+
+
+boolean
+check_speaker_headset_mode()
+{
+    static const char fname[] = "check_speaker_headset_mode";
+
+    CCAPP_DEBUG(DEB_F_PREFIX "checking SPEAKER and HEADSET active or not\n", DEB_F_PREFIX_ARGS(PLAT_API, fname));
+
+    return platGetSpeakerHeadsetMode();
+}
+
+
+/**
+ *
+ * Notify the platform to logout and reset.
+ *
+ * @param  none
+ *
+ * @return none
+ */
+
+void
+platform_logout_reset_req(void){
+    static const char fname[] = "platform_logout_reset_req";
+    feature_update_t msg;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(PLAT_API, fname));
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = CCAPP_LOGOUT_RESET;
+
+    if ( ccappTaskPostMsg(CCAPP_FALLBACK_IND, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(PLT_F_PREFIX"failed to send Logout_Reset(%d) msg \n", fname);
+    }
+    return;
+}
+
diff --git a/libs/sipcc/core/common/prot_cfgmgr_private.h b/libs/sipcc/core/common/prot_cfgmgr_private.h
new file mode 100755 (executable)
index 0000000..ddf609e
--- /dev/null
@@ -0,0 +1,431 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PROT_CFGMGR_PRIVATE_H_
+#define _PROT_CFGMGR_PRIVATE_H_
+
+#include "cpr_types.h"
+#include "ccapi.h"
+#include "ccsip_protocol.h"
+//TEMPORARY REMOVAL #include "sntp.h"
+#include "configmgr.h"
+#include "dtmf.h"
+#include "phone_platform_constants.h"
+
+#ifdef SAPP_SAPP_GSM
+#define DEFAULT_PROTOCOL         "sip"
+#define SCCP_WELL_KNOWN_PORT_STR "2000"
+#endif
+
+#define SIP_PLATFORM_CONFIG_DATE_TEMPLATE_24HOUR  "M/D/Y"
+#define SIP_PLATFORM_CONFIG_DATE_TEMPLATE_12HOUR  "M/D/YA"
+
+#define DYNAMIC_DTMF_PAYLOAD_MIN 96
+#define DYNAMIC_DTMF_PAYLOAD_MAX 127
+
+// Includes 3 CCMS
+#define MAX_CCMS 4
+
+#define MAX_CODEC_ENTRIES 10
+// updated MAX_LOAD_FILE_NAME to be in sync with XmlDefaultConfigParmObject
+#define MAX_LOAD_FILE_NAME 65
+
+/*********************************************************
+ *
+ *  Config Block Definition
+ *    This structure holds all of the parsed configuration
+ *    information obtained from the TFTP config file
+ *
+ *    To add new entries to the config table, please see
+ *    the instructions in configmgr.h and prot_configmgr.h
+ *
+ *  note: IP addresses are internally stored in the
+ *        Telecaster "Byte Reversed" order.
+ *        Eg. 0xf8332ca1 = 161.44.51.248
+ *
+ ********************************************************/
+typedef struct
+{
+    int         feature;
+    int         index;
+    int         maxnumcalls;
+    int         busy_trigger;
+    char        name[MAX_LINE_NAME_SIZE];
+    char        authname[AUTH_NAME_SIZE];
+    char        password[MAX_LINE_PASSWORD_SIZE];
+    char        displayname[MAX_LINE_NAME_SIZE]; // Actually we allow upto 32 UTF-8 chars which typically needs 32*3 octets.
+    char        contact[MAX_LINE_CONTACT_SIZE];
+    int         autoanswer;
+    char        autoanswer_mode[MAX_LINE_AUTO_ANS_MODE_SIZE];
+    int         call_waiting;
+    int         msg_waiting_lamp;
+    int         msg_waiting_amwi;
+    int         ring_setting_idle;
+    int         ring_setting_active;
+    char        proxy_address[MAX_IPADDR_STR_LEN];
+    int         proxy_port;
+    char        cfwdall[MAX_URL_LENGTH];
+    char        speeddial_number[MAX_LINE_NAME_SIZE];
+    char        retrieval_prefix[MAX_LINE_NAME_SIZE];
+    char        messages_number[MAX_LINE_NAME_SIZE];
+    int         fwd_caller_name_display;
+    int         fwd_caller_number_display;
+    int         fwd_redirected_number_display;
+    int         fwd_dialed_number_display;
+    int         feature_option_mask;
+} line_cfg_t;
+
+typedef struct
+{
+    char        address[MAX_IPADDR_STR_LEN];
+    char        ipv6address[MAX_IPADDR_STR_LEN];
+    int         sip_port;
+    int         sec_level;
+    int         is_valid;
+} ccm_cfg_t;
+
+typedef struct
+{
+    cpr_ip_addr_t   my_ip_addr;
+    uint8_t     my_mac_addr[6];
+
+    line_cfg_t  line[MAX_CONFIG_LINES];
+    ccm_cfg_t   ccm[MAX_CCMS];
+    int         proxy_register;
+    int         sip_retx;              /* SIP retransmission count */
+    int         sip_invite_retx;       /* SIP INVITE request retransmission count */
+    int         timer_t1;              /* SIP T1 timer value */
+    int         timer_t2;              /* SIP T2 timer value */
+    int         timer_invite_expires;  /* SIP Expires timer value */
+    int         timer_register_expires;
+    /*
+     * preferred codec is kept as key_table_entry structure. The
+     * name field of the key is a primary indication whether the
+     * parameter is configured or not. This is because the
+     * zero value is a designated value for G711 and the -1 is
+     * for no codec. The -1 is not natural value of uninitialized
+     * variable therefore keep the codec as name and value pair.
+     * The missing of the name indiates there the parameter is not
+     * configured.
+     */
+    key_table_entry_t preferred_codec;
+    int         dtmf_db_level;
+    DtmfOutOfBandTransport_t dtmf_outofband;
+    int         dtmf_avt_payload;
+    int         callerid_blocking;
+    int         dnd_call_alert;
+    int         dnd_reminder_timer;
+    int         blf_alert_tone_idle;
+    int         blf_alert_tone_busy;
+    int         auto_pickup_enabled;
+    int         call_hold_ringback;
+    int         stutter_msg_waiting;
+    int         call_stats;
+    int         auto_answer;
+    int         anonymous_call_block;
+    int         nat_enable;
+    char        nat_address[MAX_IPADDR_STR_LEN];
+    int         voip_control_port;
+    unsigned int media_port_start;
+    unsigned int media_port_end;
+    char        sync[MAX_SYNC_LEN];
+    char        proxy_backup[MAX_IPADDR_STR_LEN];
+    char        proxy_emergency[MAX_IPADDR_STR_LEN];
+    int         proxy_backup_port;
+    int         proxy_emergency_port;
+    int         nat_received_processing;
+
+    char        proxy_outbound[MAX_IPADDR_STR_LEN];
+    int         proxy_outbound_port;
+    char        reg_user_info[MAX_REG_USER_INFO_LEN];
+    int         cnf_join_enable;
+    int         remote_party_id;
+    int         semi_xfer;
+    char        cfwd_uri[MAX_URL_LENGTH];
+    int         local_cfwd_enable;
+    int         timer_register_delta;
+    int         rfc_2543_hold;
+    int         sip_max_forwards;
+    int         conn_monitor_duration;
+    char        call_pickup_uri[MAX_URL_LENGTH];
+    char        call_pickup_list_uri[MAX_URL_LENGTH];
+    char        call_pickup_group_uri[MAX_URL_LENGTH];
+    char        meet_me_service_uri[MAX_URL_LENGTH];
+    char        call_forward_uri[MAX_URL_LENGTH];
+    char        abbreviated_dial_uri[MAX_URL_LENGTH];
+    int         call_log_blf_enabled;
+    int         remote_cc_enabled;
+    int         timer_keepalive_expires;
+    int         timer_subscribe_expires;
+    int         timer_subscribe_delta;
+    int         transport_layer_prot;
+    int         kpml;
+    int         enable_vad;
+    int         autoanswer_idle_alt;
+    int         autoanswer_timer;
+    int         autoanswer_override;
+    int         offhook_to_first_digit;
+    int         call_waiting_period;
+    int         ring_setting_busy_pol;
+    int         dscp_for_call_control;
+    int         speaker_enabled;
+    int         xfr_onhook_enabled;
+    int         retain_forward_information;
+    int         rollover;
+    int         join_across_lines;
+    int         emcc_mode;
+    int         visiting_em_port;
+    char        visiting_em_ip[MAX_IPADDR_STR_LEN];
+    int         ip_addr_mode;
+    char        load_file[MAX_LOAD_FILE_NAME];
+    int         inter_digit_timer;
+    int         dscp_audio;
+    int         dscp_video;
+    char        deviceName[MAX_REG_USER_INFO_LEN];
+    uint8_t     my_active_mac_addr[6];
+    char        userAgent[MAX_REG_USER_INFO_LEN];
+    char        modelNumber[MAX_REG_USER_INFO_LEN];
+    int         srst_is_secure;
+    char        join_dxfer_policy[MAX_JOIN_DXFER_POLICY_SIZE];
+    char        external_number_mask[MAX_EXTERNAL_NUMBER_MASK_SIZE];
+    char        media_ip_addr[MAX_IPADDR_STR_LEN];
+    int         p2psip;
+    int         sdpmode;
+    char        version[4];
+    int         rtcpmux;
+    int         rtpsavpf;
+    int         maxavbitrate;
+    int         maxcodedaudiobw;
+    int         usedtx;
+    int         stereo;
+    int         useinbandfec;
+    int         cbr;
+    int         maxptime;
+    int         sctp_port;
+    int         num_data_streams;
+} prot_cfg_t;
+
+static prot_cfg_t prot_cfg_block;
+
+
+/*********************************************************
+ *  return (1);
+ *  Config Table Keytable Structures
+ *
+ *  Some config table entries can only contain special  return (1);
+ *  values.  Those entries must specify a key table
+ *  that contains valid values for the entry.
+ *
+ *********************************************************/
+/*
+ * codec_table table is used for parsing configured preferred
+ * codec, J-side caches string and it is converted to the enumerated
+ * value during reset/restart. The "none" value is currently used
+ * by CUCM' TFTP when there is no preferred codec configured and
+ * therefore the "none" is included in the table.
+ */
+static const key_table_entry_t codec_table[] = {
+    {"g711ulaw",         RTP_PCMU},
+    {"g711alaw",         RTP_PCMA},
+    {"g729a",            RTP_G729},
+    {"L16",              RTP_L16},
+#ifdef CISCOWB_SUPPORTED
+    {"CiscoWb",          RTP_CISCOWB},
+#endif
+    {"g722",             RTP_G722},
+    {"iLBC",             RTP_ILBC},
+    {"iSAC",             RTP_ISAC},
+    {"opus",             RTP_OPUS},
+    {"none",             RTP_NONE},
+    {0,                  RTP_NONE}
+};
+
+static const key_table_entry_t dtmf_outofband_table[] = {
+    {"none",             DTMF_OUTOFBAND_NONE},
+    {"avt",              DTMF_OUTOFBAND_AVT},
+    {"avt_always",       DTMF_OUTOFBAND_AVT_ALWAYS},
+    {0,                  0}
+};
+
+static const key_table_entry_t user_info_table[] = {
+    {"none",             0,},
+    {"phone",            1,},
+    {"ip",               2,},
+    {0,                  0,}
+};
+
+/*********************************************************
+ *!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ *  SIP Protocol Config table Variable Name and Type Definitions
+ *
+ *    This table defines all the possible variables that can
+ *    be set in the TFTP config file (if used).
+ *    var:        Is the name as it is defined in the ascii
+ *                configuration file.
+ *    addr:       Is the address of where the parsed variable
+ *                is stored into memory
+ *    len:        Is the length of the item in kazoo memory.
+ *                (Important for things like strings)
+ *    parser:     Is a function pointer to a routine that is
+ *                used to parse (convert) this particular
+ *                variable from ascii form to internal form.
+ *    print:      Is a function pointer to a routine that is
+ *                used to print (convert) this particular
+ *                variable from internal form to ascii form.
+ *    keytable:   Defines the table of values that this entry
+ *                can have.
+ *
+ *!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * Before changing any these, please read the following:
+ *
+ * This table MUST be kept in sync with the configuration
+ * ID enums located in the prot_configmgr.h
+ * file.  There is a one-to-one correspondence between those
+ * enums and this table.
+ *
+ * To add or remove config properties, please refer to the
+ * file prot_configmgr.h.
+ *
+ *!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *********************************************************/
+      /*********************************************************
+       *
+       *  Platform-Specific Configuration Section
+       *  (Common across all IP Phone protocols.)
+       *
+       ********************************************************/
+
+/*     name                                  {addr & len}                    parser  print   */
+/*     ------------------------              -----------------               ------  ------  */
+var_t prot_cfg_table[CFGID_PROTOCOL_MAX+1] = {
+ /* 0 */{"startMediaPort",CFGVAR(media_port_start),       PA_INT, PR_INT, 0},
+        {"endMediaPort", CFGVAR(media_port_end),         PA_INT, PR_INT, 0},
+        {"callerIdBlocking", CFGVAR(callerid_blocking),      PA_INT, PR_INT, 0},
+        {"anonymousCallBlock", CFGVAR(anonymous_call_block),   PA_INT, PR_INT, 0},
+        {"dndCallAlert", CFGVAR(dnd_call_alert),         PA_INT, PR_INT, 0},
+        {"dndReminderTimer", CFGVAR(dnd_reminder_timer),     PA_INT, PR_INT, 0},
+        {"preferredCode", CFGVAR(preferred_codec),        PA_KEYE, PR_KEYE, codec_table},
+        {"dtmfOutofBand",CFGVAR(dtmf_outofband),         PA_KEY, PR_KEY, dtmf_outofband_table},
+        {"dtmfAvtPayload", CFGVAR(dtmf_avt_payload),       PA_INT, PR_INT, 0},
+        {"dtmfDbLevel", CFGVAR(dtmf_db_level),          PA_INT, PR_INT, 0},
+        {"sipRetx", CFGVAR(sip_retx),               PA_INT, PR_INT, 0},
+ /*11*/ {"sipInviteRetx", CFGVAR(sip_invite_retx),        PA_INT, PR_INT, 0},
+        {"timerT1", CFGVAR(timer_t1),               PA_INT, PR_INT, 0},
+        {"timerT2", CFGVAR(timer_t2),               PA_INT, PR_INT, 0},
+        {"timerInviteExpires", CFGVAR(timer_invite_expires),   PA_INT, PR_INT, 0},
+        {"timerRegisterExpires", CFGVAR(timer_register_expires), PA_INT, PR_INT, 0},
+        {"registerWithProxy", CFGVAR(proxy_register),         PA_INT, PR_INT, 0},
+        {"backupProxy", CFGVAR(proxy_backup),           PA_STR, PR_STR, 0},
+        {"backupProxyPort", CFGVAR(proxy_backup_port),      PA_INT, PR_INT, 0},
+        {"emergencyProxy", CFGVAR(proxy_emergency),        PA_STR, PR_STR, 0},
+        {"emergencyProxyPort", CFGVAR(proxy_emergency_port),   PA_INT, PR_INT, 0},
+        {"outboundProxy", CFGVAR(proxy_outbound),         PA_STR, PR_STR, 0},
+        {"outboundProxyPort", CFGVAR(proxy_outbound_port),    PA_INT, PR_INT, 0},
+        {"natReceivedProcessing", CFGVAR(nat_received_processing),PA_INT, PR_INT, 0},
+        {"userInfo", CFGVAR(reg_user_info),          PA_KEY, PR_KEY, user_info_table},
+        {"cnfJoinEnable", CFGVAR(cnf_join_enable),        PA_INT, PR_INT, 0},
+        {"remotePartyID", CFGVAR(remote_party_id),        PA_INT, PR_INT, 0},
+        {"semiAttendedTransfer", CFGVAR(semi_xfer),              PA_INT, PR_INT, 0},
+        {"callHoldRingback", CFGVAR(call_hold_ringback),     PA_INT, PR_INT, 0},
+        {"stutterMsgWaiting", CFGVAR(stutter_msg_waiting),    PA_INT, PR_INT, 0},
+        {"callForwardUri", CFGVAR(cfwd_uri),               PA_STR, PR_STR, 0},
+ /*31*/ {"callStats", CFGVAR(call_stats),             PA_INT, PR_INT, 0},
+        {"autoAnswer", CFGVAR(auto_answer),            PA_INT, PR_INT, 0},
+        {"localCfwdEnable", CFGVAR(local_cfwd_enable),      PA_INT, PR_INT, 0},
+        {"timerRegisterDelta", CFGVAR(timer_register_delta),   PA_INT, PR_INT, 0},
+        {"MaxRedirects", CFGVAR(sip_max_forwards),       PA_INT, PR_INT, 0},
+        {"rfc2543Hold", CFGVAR(rfc_2543_hold),          PA_INT, PR_INT, 0},
+        {"ccm1_address", CFGVAR(ccm[0].address),         PA_STR, PR_STR, 0},
+        {"ccm2_address", CFGVAR(ccm[1].address),         PA_STR, PR_STR, 0},
+        {"ccm3_address", CFGVAR(ccm[2].address),         PA_STR, PR_STR, 0},
+        {"ccm1_ipv6address", CFGVAR(ccm[0].ipv6address),     PA_STR, PR_STR, 0},
+        {"ccm2_ipv6address", CFGVAR(ccm[1].ipv6address),     PA_STR, PR_STR, 0},
+        {"ccm3_ipv6address", CFGVAR(ccm[2].ipv6address),     PA_STR, PR_STR, 0},
+        {"ccm1_sipPort", CFGVAR(ccm[0].sip_port),        PA_INT, PR_INT, 0},
+        {"ccm2_sipPort", CFGVAR(ccm[1].sip_port),        PA_INT, PR_INT, 0},
+        {"ccm3_sipPort", CFGVAR(ccm[2].sip_port),        PA_INT, PR_INT, 0},
+        {"ccm1_securityLevel", CFGVAR(ccm[0].sec_level),       PA_INT, PR_INT, 0},
+        {"ccm2_securityLevel", CFGVAR(ccm[1].sec_level),       PA_INT, PR_INT, 0},
+        {"ccm3_securityLevel", CFGVAR(ccm[2].sec_level),       PA_INT, PR_INT, 0},
+        {"ccm1_isValid", CFGVAR(ccm[0].is_valid),        PA_INT, PR_INT, 0},
+ /*50*/ {"ccm2_isValid", CFGVAR(ccm[1].is_valid),        PA_INT, PR_INT, 0},
+        {"ccm3_isValid", CFGVAR(ccm[2].is_valid),        PA_INT, PR_INT, 0},
+        {"ccmTftp_ipAddr", CFGVAR(ccm[3].address),         PA_STR, PR_STR, 0},
+        {"ccmTftp_port", CFGVAR(ccm[3].sip_port),        PA_INT, PR_INT, 0},
+        {"ccmTftp_isValid", CFGVAR(ccm[3].is_valid),        PA_INT, PR_INT, 0},
+        {"ccmTftp_securityLevel", CFGVAR(ccm[3].sec_level),       PA_INT, PR_INT, 0},
+        {"ccmSrstIpAddr", CFGVAR(ccm[4].address),         PA_STR, PR_STR, 0},
+        {"ccmSrst_sipPort", CFGVAR(ccm[4].sip_port),        PA_INT, PR_INT, 0},
+        {"ccmSrst_isValid", CFGVAR(ccm[4].is_valid),        PA_INT, PR_INT, 0},
+        {"ccmSrst_securityLevel", CFGVAR(ccm[4].sec_level),       PA_INT, PR_INT, 0},
+        {"connectionMonitorDuration", CFGVAR(conn_monitor_duration),  PA_INT, PR_INT, 0},
+        {"callPickupURI", CFGVAR(call_pickup_uri),        PA_STR, PR_STR, 0},
+        {"callPickupListURI", CFGVAR(call_pickup_list_uri),   PA_STR, PR_STR, 0},
+        {"callPickupGroupURI", CFGVAR(call_pickup_group_uri),  PA_STR, PR_STR, 0},
+        {"meetMeServiceURI", CFGVAR(meet_me_service_uri),    PA_STR, PR_STR, 0},
+        {"callForwardURI", CFGVAR(call_forward_uri),       PA_STR, PR_STR, 0},
+        {"abbreviatedDialURI", CFGVAR(abbreviated_dial_uri),   PA_STR, PR_STR, 0},
+        {"callLogBlfEnabled", CFGVAR(call_log_blf_enabled),   PA_INT, PR_INT, 0},
+        {"remoteCcEnabled", CFGVAR(remote_cc_enabled),      PA_INT, PR_INT, 0},
+        {"retainForwardInformation", CFGVAR(retain_forward_information),  PA_INT, PR_INT, 0},
+ /*70*/ {"timerKeepaliveExpires", CFGVAR(timer_keepalive_expires),PA_INT, PR_INT, 0},
+        {"timerSubscribeExpires", CFGVAR(timer_subscribe_expires),PA_INT, PR_INT, 0},
+        {"timerSubscribeDelta", CFGVAR(timer_subscribe_delta),  PA_INT, PR_INT, 0},
+        {"transportLayerProtocol", CFGVAR(transport_layer_prot),   PA_INT, PR_INT, 0},
+        {"kpml", CFGVAR(kpml),                   PA_INT, PR_INT, 0},
+        {"natEnable", CFGVAR(nat_enable),             PA_INT, PR_INT, 0},
+        {"natAddress", CFGVAR(nat_address),            PA_STR, PR_STR, 0},
+        {"voipControlPort", CFGVAR(voip_control_port),      PA_INT, PR_INT, 0},
+        {"myIpAddr", CFGVAR(my_ip_addr),             PA_IP,  PR_IP, 0},
+        {"myMacAddr", CFGVAR(my_mac_addr),            PA_STR, PR_MAC, 0},
+        {"enableVad", CFGVAR(enable_vad),             PA_INT, PR_INT, 0},
+        {"autoAnswerAltBehavior", CFGVAR(autoanswer_idle_alt),    PA_INT, PR_INT, 0},
+        {"autoAnswerTimer", CFGVAR(autoanswer_timer),       PA_INT, PR_INT, 0},
+        {"autoAnswerOverride", CFGVAR(autoanswer_override),    PA_INT, PR_INT, 0},
+        {"offhookToFirstDigitTimer", CFGVAR(offhook_to_first_digit), PA_INT, PR_INT, 0},
+        {"silentPeriodBetweenCallWaitingBursts", CFGVAR(call_waiting_period),    PA_INT, PR_INT, 0},
+        {"ringSettingBusyStationPolicy", CFGVAR(ring_setting_busy_pol),  PA_INT, PR_INT, 0},
+        {"DscpForCm2Dvce", CFGVAR(dscp_for_call_control),  PA_INT, PR_INT, 0},
+        {"speakerEnabled", CFGVAR(speaker_enabled),        PA_INT, PR_INT, 0},
+        {"transferOnhookEnable", CFGVAR(xfr_onhook_enabled),    PA_INT, PR_INT, 0},
+ /*90*/ {"rollover", CFGVAR(rollover),               PA_INT, PR_INT, 0},
+        {"loadFileName", CFGVAR(load_file), PA_STR, PR_STR, 0},
+        {"blfAlertToneIdle", CFGVAR(blf_alert_tone_idle),PA_INT, PR_INT, 0},
+        {"blfAlertToneBusy", CFGVAR(blf_alert_tone_busy),PA_INT, PR_INT, 0},
+        {"autoPickupEnable", CFGVAR(auto_pickup_enabled),PA_INT, PR_INT, 0},
+        {"joinAcrossLines", CFGVAR(join_across_lines),      PA_INT, PR_INT, 0},
+ /*96*/ {"myActiveMacAddr", CFGVAR(my_active_mac_addr),            PA_STR, PR_MAC, 0},
+ /*97*/ {"DscpAudio", CFGVAR(dscp_audio), PA_INT, PR_INT, 0},
+        {"deviceName",                    CFGVAR(deviceName),         PA_STR, PR_STR, 0},
+        {"userAgent",                     CFGVAR(userAgent),          PA_STR, PR_STR, 0},
+        {"modelNumber",                   CFGVAR(modelNumber),        PA_STR, PR_STR, 0},
+        {"DscpVideo", CFGVAR(dscp_video), PA_INT, PR_INT, 0},
+        {"IPAddrMode", CFGVAR(ip_addr_mode),      PA_INT, PR_INT, 0},
+        {"interDigitTimer", CFGVAR(inter_digit_timer),      PA_INT, PR_INT, 0},
+        {"emccMode",            CFGVAR(emcc_mode),          PA_INT, PR_INT, 0},
+        {"visitingEMPort",      CFGVAR(visiting_em_port),  PA_INT, PR_INT, 0},
+        {"visitingEMIpAddress", CFGVAR(visiting_em_ip),    PA_STR, PR_STR, 0},
+        {"isSRSTSecure", CFGVAR(srst_is_secure), PA_INT, PR_INT, 0},
+/*108*/ {"joinDxferPolicy", CFGVAR(join_dxfer_policy), PA_STR, PR_STR, 0},
+        {"externalNumberMask", CFGVAR(external_number_mask), PA_STR, PR_STR, 0},
+        {"mediaIpAddr", CFGVAR(media_ip_addr),    PA_STR, PR_STR, 0},
+        {"p2psip", CFGVAR(p2psip),       PA_INT, PR_INT, 0},
+        {"version", CFGVAR(version),    PA_STR, PR_STR, 0},
+        {"sdpmode", CFGVAR(sdpmode),       PA_INT, PR_INT, 0},
+        {"rtcpmux", CFGVAR(rtcpmux),       PA_INT, PR_INT, 0},
+        {"rtpsavpf", CFGVAR(rtpsavpf),       PA_INT, PR_INT, 0},
+        {"maxavbitrate", CFGVAR(maxavbitrate),       PA_INT, PR_INT, 0},
+        {"maxcodedaudiobw", CFGVAR(maxcodedaudiobw),       PA_INT, PR_INT, 0},
+        {"usedtx", CFGVAR(usedtx),       PA_INT, PR_INT, 0},
+        {"stereo", CFGVAR(stereo),       PA_INT, PR_INT, 0},
+        {"useinbandfec", CFGVAR(useinbandfec),       PA_INT, PR_INT, 0},
+        {"cbr", CFGVAR(cbr),       PA_INT, PR_INT, 0},
+        {"maxptime", CFGVAR(maxptime),       PA_INT, PR_INT, 0},
+        {"sctp_port", CFGVAR(sctp_port),       PA_INT, PR_INT, 0},
+        {"num_data_streams", CFGVAR(num_data_streams),       PA_INT, PR_INT, 0},
+        {0,                              0,      0,      0, 0, 0}
+  };
+
+#endif /* _PROT_CFGMGR_PRIVATE_H_ */
diff --git a/libs/sipcc/core/common/prot_configmgr.c b/libs/sipcc/core/common/prot_configmgr.c
new file mode 100755 (executable)
index 0000000..93da2d5
--- /dev/null
@@ -0,0 +1,890 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "cpr_in.h"
+#include "util_string.h"
+#include "task.h"
+#include "upgrade.h"
+#include "ccsip_task.h"
+#include "config.h"
+#include "ccsip_core.h"
+#include "prot_configmgr.h"
+#include "prot_cfgmgr_private.h"
+#include "sip_common_transport.h"
+#include "phone_debug.h"
+#include "regmgrapi.h"
+#include "rtp_defs.h"
+#include "vcm.h"
+#include "plat_api.h"
+
+#define MAX_TOS_VALUE            5
+#define MIN_VOIP_PORT_RANGE   1024
+#define MAX_VOIP_PORT_RANGE  65535
+#define MAX_AUTO_ANSWER_7960    63
+#define MIN_KEEPALIVE_EXPIRES 120
+#define MAX_KEEPALIVE_EXPIRES 7200
+
+extern void platform_get_ipv4_address(cpr_ip_addr_t *ip_addr);
+extern void platform_get_ipv6_address(cpr_ip_addr_t *ip_addr);
+extern boolean Is794x;
+
+static cpr_ip_addr_t redirected_nat_ipaddr = {0,{0}};
+static void config_set_current_codec_table(int codec_mask,
+                                           rtp_ptype *codec_table);
+
+/*********************************************************
+ *
+ *  Network Configuration Settings
+ *  Look at making these generic and moving them to the
+ *  network library...
+ *
+ *********************************************************/
+
+static void initCfgTblEntry(int index, const char * name, void *addr, int length,
+                            parse_func_t parse, print_func_t print,
+                            const key_table_entry_t *key)
+{
+  var_t *table;
+  table = &prot_cfg_table[index];
+
+  table->name = name;
+  table->addr = addr;
+  table->length = length;
+  table->parse_func = parse;
+  table->print_func = print;
+  table->key_table = key;
+
+}
+
+/* Function: protCfgTblInit()
+ *
+ *  Description: Initializes line specific params in prot_cfg_table
+ *
+ *  Parameters:
+ *
+ *  Returns:
+ */
+void protCfgTblInit()
+{
+int i;
+  memset(&prot_cfg_block, 0, sizeof(prot_cfg_block));
+  for (i=0; i< MAX_CONFIG_LINES; i++) {
+    initCfgTblEntry(CFGID_LINE_INDEX+i, "Index", CFGVAR(line[i].index), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_FEATURE+i, "Feat", CFGVAR(line[i].feature), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_MAXNUMCALLS+i, "MNC", CFGVAR(line[i].maxnumcalls), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_BUSY_TRIGGER+i, "BT", CFGVAR(line[i].busy_trigger), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_PROXY_ADDRESS+i, "ProxyAddr", CFGVAR(line[i].proxy_address), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_PROXY_PORT+i, "ProxyPort", CFGVAR(line[i].proxy_port), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_CALL_WAITING+i, "CWait", CFGVAR(line[i].call_waiting), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_AUTOANSWER_ENABLED+i, "AAns", CFGVAR(line[i].autoanswer), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_AUTOANSWER_MODE+i, "AAnsMode", CFGVAR(line[i].autoanswer_mode), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_MSG_WAITING_LAMP+i, "MWILamp", CFGVAR(line[i].msg_waiting_lamp), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_MESSAGE_WAITING_AMWI+i, "AMWI", CFGVAR(line[i].msg_waiting_amwi), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_RING_SETTING_IDLE+i, "RingIdle", CFGVAR(line[i].ring_setting_idle), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_RING_SETTING_ACTIVE+i, "RingActive", CFGVAR(line[i].ring_setting_active), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_NAME+i, "Name", CFGVAR(line[i].name), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_AUTHNAME+i, "AuthName", CFGVAR(line[i].authname), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_PASSWORD+i, "Passwd", CFGVAR(line[i].password), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_DISPLAYNAME+i, "DisplayName", CFGVAR(line[i].displayname), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_CONTACT+i, "Contact", CFGVAR(line[i].contact), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_CFWDALL+i, "CfwdAll", CFGVAR(line[i].cfwdall), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_SPEEDDIAL_NUMBER+i, "speedDialNumber", CFGVAR(line[i].speeddial_number), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_RETRIEVAL_PREFIX+i, "retrievalPrefix", CFGVAR(line[i].retrieval_prefix), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_MESSAGES_NUMBER+i, "messagesNumber", CFGVAR(line[i].messages_number), PA_STR, PR_STR, 0);
+    initCfgTblEntry(CFGID_LINE_FWD_CALLER_NAME_DIPLAY+i, "callerName", CFGVAR(line[i].fwd_caller_name_display), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_FWD_CALLER_NUMBER_DIPLAY+i, "callerName", CFGVAR(line[i].fwd_caller_number_display), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_FWD_REDIRECTED_NUMBER_DIPLAY+i, "redirectedNumber", CFGVAR(line[i].fwd_redirected_number_display), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_FWD_DIALED_NUMBER_DIPLAY+i, "dialedNumber", CFGVAR(line[i].fwd_dialed_number_display), PA_INT, PR_INT, 0);
+    initCfgTblEntry(CFGID_LINE_FEATURE_OPTION_MASK+i, "featureOptionMask", CFGVAR(line[i].feature_option_mask), PA_INT, PR_INT, 0);
+  }
+
+  initCfgTblEntry(CFGID_PROTOCOL_MAX, 0, 0, 0, 0, 0, 0);
+}
+
+/*
+ * sip_config_get_net_device_ipaddr()
+ *
+ * Get the device IP address.
+ * Note: the IP Address is returned in the non-Telecaster
+ *       SIP format, which is not byte reversed.
+ *       Eg. 0xac2c33f8 = 161.44.51.248
+ */
+void
+sip_config_get_net_device_ipaddr (cpr_ip_addr_t *ip_addr)
+{
+    cpr_ip_addr_t ip_addr1 = {0,{0}};
+
+    platform_get_ipv4_address(&ip_addr1);
+    util_ntohl(ip_addr, &ip_addr1);
+}
+
+/*
+ * sip_config_get_net_device_ipaddr()
+ *
+ * Get the device IP address.
+ * Note: the IP Address is returned in the non-Telecaster
+ *       SIP format, which is not byte reversed.
+ *
+ */
+void
+sip_config_get_net_ipv6_device_ipaddr (cpr_ip_addr_t *ip_addr)
+{
+    cpr_ip_addr_t ip_addr1 = {0,{0}};
+
+    platform_get_ipv6_address(&ip_addr1);
+    util_ntohl(ip_addr, &ip_addr1);
+}
+
+
+/*
+ * sip_config_get_nat_ipaddr()
+ *
+ * Get the nat IP address.
+ * Note: the IP Address is returned in the non-Telecaster
+ *       SIP format, which is not byte reversed.
+ *       Eg. 0xac2c33f8 = 161.44.51.248
+ */
+void
+sip_config_get_nat_ipaddr (cpr_ip_addr_t *ip_addr)
+{
+    cpr_ip_addr_t IPAddress;
+    char address[MAX_IPADDR_STR_LEN];
+    int dnsErrorCode = 1;
+
+    if (redirected_nat_ipaddr.type == CPR_IP_ADDR_INVALID) {
+        config_get_string(CFGID_NAT_ADDRESS, address, sizeof(address));
+        if ((cpr_strcasecmp(address, UNPROVISIONED) != 0) && (address[0] != 0)) {
+            dnsErrorCode = dnsGetHostByName(address, &IPAddress, 100, 1);
+        }
+
+        if (dnsErrorCode == 0) {
+            util_ntohl(ip_addr, &IPAddress);
+            return ;
+        } else {
+            /*
+             * If the NAT address is not provisioned or
+             * unavailable, return the local address instead.
+             */
+            sip_config_get_net_device_ipaddr(ip_addr);
+            return;
+        }
+    } else {
+        *ip_addr = redirected_nat_ipaddr;
+        return ;
+    }
+
+}
+
+/*
+ * sip_config_set_nat_ipaddr()
+ *
+ * Set the device NAT IP address.
+ * Note: the IP Address is returned in the non-Telecaster
+ *       SIP format, which is not byte reversed.
+ *       Eg. 0xac2c33f8 = 161.44.51.248
+ */
+void
+sip_config_set_nat_ipaddr (cpr_ip_addr_t *ip_address)
+{
+    redirected_nat_ipaddr = *ip_address;
+}
+
+/*********************************************************
+ *
+ *  SIP Configuration Settings
+ *  These should probably be turned into generic config
+ *  table "gets/sets" or should be moved to a SIP platform
+ *  file.  Maybe ccsip_platform_ui.c since that's where we
+ *  have the other SIP Platform code.  In the long run, we
+ *  should rename ccsip_platform_ui.c to ccsip_platform.c
+ *
+ *********************************************************/
+
+/*
+ * sip_config_get_line_from_button()
+ * Some cases CCM sends down line number instead of button number
+ * that has to be mapped to correct button number. This function is
+ * called to get actual line number for a given button number
+ *
+ * Returns: actual line number from given button number
+ *
+ */
+line_t
+sip_config_get_line_from_button (line_t button)
+{
+    line_t     max_lines_allowed;
+    uint32_t   line = 0;
+    line_t     button_no = 0;
+
+    if (Is794x) {
+        max_lines_allowed = MAX_REG_LINES_794X;
+    } else {
+        max_lines_allowed = MAX_REG_LINES;
+    }
+
+    if ((button < 1) || (button > max_lines_allowed)) {
+        return (button);
+    }
+
+    config_get_line_value(CFGID_LINE_INDEX, &line,
+                          sizeof(line), button);
+
+    /* Look for the line number through the configuration
+     * <line button="4" lineIndex="3>. If the inddex value is not
+     * found then use old way of searching for button. The dolby
+     * release of CCM adds index to configuration but older
+     * ccm does not support that.
+     */
+
+    if (line > 0) {
+
+        return((line_t)line);
+    }
+
+    /* Try old way of calculating the line number
+     */
+
+    line = 0;
+    button_no = 0;
+
+    for (button_no = 1; button_no <= button; button_no++) {
+
+        if (sip_config_check_line(button_no) == FALSE) {
+            continue;
+        }
+
+        line++;
+    }
+
+    return ((line_t)line);
+}
+
+
+/*
+ * sip_config_get_button_from_line()
+ *
+ * Some cases CCM sends down line number instead of button number
+ * that has to be mapped to correct button number.
+ *
+ * Parameters: line - the line instance
+ *
+ * Returns: line - actual button number
+ *
+ */
+line_t
+sip_config_get_button_from_line (line_t line)
+{
+    line_t     max_lines_allowed;
+    line_t     button = 0;
+    uint32_t   line_no = 0;
+
+    if (Is794x) {
+        max_lines_allowed = MAX_REG_LINES_794X;
+    } else {
+        max_lines_allowed = MAX_REG_LINES;
+    }
+
+    if ((line < 1) || (line > max_lines_allowed)) {
+        return (line);
+    }
+
+    /* Look for the button number through the configuration
+     * <line button="4" lineIndex="3>. If the inddex value is not
+     * found then use old way of searching for button. The dolby
+     * release of CCM adds index to configuration but older
+     * ccm does not support that.
+     */
+
+    for (button = 1; button <= max_lines_allowed; button++) {
+
+        config_get_line_value(CFGID_LINE_INDEX, &line_no, sizeof(line_no), button);
+
+        if ((line_t)line_no == line) {
+            return(button);
+        }
+    }
+
+    button = 0;
+    line_no = 0;
+
+    /* Nothing has found so far, try old way of calculating the
+     * button number
+     */
+    do {
+
+        if (sip_config_check_line(button) == FALSE) {
+            button++;
+            continue;
+        }
+
+        button++;
+        line_no++;
+
+    } while (((line_t)line_no < line) &&
+            button <= max_lines_allowed);
+
+
+    /* Above loop not able to find the correct button number
+     * so return value 0
+     */
+    if (button > max_lines_allowed) {
+        return(0);
+    }
+
+    return (button - 1);
+}
+
+
+/*
+ * sip_config_check_line()
+ *
+ * Check to see if the indicated line is configured as a DN line
+ *
+ * Parameters: line - the line instance
+ *
+ * Returns: TRUE if the indicated line is Valid
+ *          FALSE if the indicated line is Invalid
+ *
+ */
+boolean
+sip_config_check_line (line_t line)
+{
+    const char fname[] = "sip_config_check_line";
+    char     temp[MAX_LINE_NAME_SIZE];
+    uint32_t line_feature;
+    line_t   max_lines_allowed;
+
+    if (Is794x) {
+        max_lines_allowed = MAX_REG_LINES_794X;
+    } else {
+        max_lines_allowed = MAX_REG_LINES;
+    }
+
+    if ((line < 1) || (line > max_lines_allowed)) {
+        if (line != 0) {
+            PLAT_ERROR(PLAT_COMMON_F_PREFIX"Invalid Line: %d\n", fname, line);
+        }
+        return FALSE;
+    }
+
+    config_get_line_string(CFGID_LINE_NAME, temp, line, sizeof(temp));
+    if (temp[0] == '\0') {
+        return FALSE;
+    }
+
+    config_get_line_value(CFGID_LINE_FEATURE, &line_feature,
+                          sizeof(line_feature), line);
+
+    if (line_feature != cfgLineFeatureDN) {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+/*
+ * sip_config_local_line_get()
+ *
+ * Get the Line setting.
+ * Note: The UI has serious problems if there are gaps in the
+ * line names.  Therefore, lines must be sequential.  In
+ * other words, if lines 1, 2, and 4 have names, this routine
+ * will return that two lines are active.  Once Line three is
+ * found to be unprovisioned, line 4 will be ignored.
+ */
+line_t
+sip_config_local_line_get (void)
+{
+    if (Is794x) {
+        return (MAX_REG_LINES_794X);
+    }
+    return (MAX_REG_LINES);
+}
+/*
+ * sip_config_get_keepalive_expires()
+ *
+ * Returns the keepalive expires configured.
+ * The minimum allowed value is returned if
+ * configured value is less than the minimum
+ * allowed value.If the configured value is
+ * greater than the maximum allowed then the
+ * maximum allowed value is returned.
+ *
+ */
+int
+sip_config_get_keepalive_expires()
+{
+    int keepalive_interval = 0;
+
+    config_get_value(CFGID_TIMER_KEEPALIVE_EXPIRES, &keepalive_interval,
+                         sizeof(keepalive_interval));
+
+    if (keepalive_interval < MIN_KEEPALIVE_EXPIRES) {
+        keepalive_interval = MIN_KEEPALIVE_EXPIRES;
+        TNP_DEBUG(DEB_F_PREFIX"Keepalive interval less than minimum acceptable.Resetting it to %d\n",
+            DEB_F_PREFIX_ARGS(SIP_KA, "sip_config_get_keepalive_expires"),
+            keepalive_interval);
+    } else if (keepalive_interval > MAX_KEEPALIVE_EXPIRES) {
+        keepalive_interval = MAX_KEEPALIVE_EXPIRES;
+        TNP_DEBUG(DEB_F_PREFIX"Keepalive interval more than maximum acceptable.Resetting it to %d\n",
+            DEB_F_PREFIX_ARGS(SIP_KA, "sip_config_get_keepalive_expires"),
+            keepalive_interval);
+    }
+
+    return keepalive_interval;
+}
+/*
+ * sip_config_get_display_name()
+ *
+ * Get the display name
+ */
+void
+sip_config_get_display_name (line_t line, char *buffer, int buffer_len)
+{
+
+    config_get_line_string(CFGID_LINE_DISPLAYNAME, buffer, line, buffer_len);
+
+    if ((strcmp(buffer, UNPROVISIONED) == 0) || (buffer[0] == '\0')) {
+        config_get_line_string(CFGID_LINE_NAME, buffer, line, buffer_len);
+    }
+}
+
+/**
+ * Returns the configured value of preferred codec. The codec may
+ * or may not be available by the platform.
+ *
+ * @param[in] none.
+ *
+ * @return rtp_ptype of the codec.
+ */
+rtp_ptype
+sip_config_preferred_codec (void)
+{
+    key_table_entry_t cfg_preferred_codec;
+
+    config_get_value(CFGID_PREFERRED_CODEC, &cfg_preferred_codec,
+                     sizeof(cfg_preferred_codec));
+    if ((cfg_preferred_codec.name != NULL) &&
+        (cfg_preferred_codec.name[0] != '\0')) {
+        /* The configuration has preferred codec configured */
+        return (cfg_preferred_codec.value);
+    }
+    /* No preferred codec configured */
+    return (RTP_NONE);
+}
+
+/**
+ * sip_config_local_supported_codecs_get()
+ * Get the locally supported codec list. The returned list
+ * of codecs will be in the ordered of preference. If there is
+ * preferred condec configured and it is available, the
+ * preferred codec will be put on the first entry of the
+ * returned list.
+ *
+ * @param[in,out] aSupportedCodecs - pointer to arrary fo the
+ *                                   rtp_ptype to store the result of
+ *                                   currenlty available codecs.
+ * @param[in] supportedCodecsLen   - indicates the number of entry
+ *                                   of the aSupportedCodecs.
+ *
+ * @return number of current codecs available.
+ *
+ * @pre  (aSupportedCodecs != NULL)
+ * @pre  (supportedCodecsLen != 0)
+ */
+uint16_t
+sip_config_local_supported_codecs_get (rtp_ptype aSupportedCodecs[],
+                                       uint16_t supportedCodecsLen)
+{
+    rtp_ptype current_codec_table[MAX_CODEC_ENTRIES+1];
+    rtp_ptype *codec;
+    rtp_ptype pref_codec;
+    uint16_t count = 0;
+    int codec_mask;
+    boolean preferred_codec_available = FALSE;
+
+    codec_mask = vcmGetAudioCodecList(VCM_DSP_FULLDUPLEX);
+
+    if (!codec_mask) {
+        codec_mask = VCM_CODEC_RESOURCE_G711 | VCM_CODEC_RESOURCE_OPUS;
+    }
+
+    /*
+     * convert the current available codec into the enumerated
+     * preferred list.
+     */
+    current_codec_table[0] = RTP_NONE;
+    current_codec_table[MAX_CODEC_ENTRIES] = RTP_NONE;
+    config_set_current_codec_table(codec_mask, &current_codec_table[0]);
+
+    /*
+     * Get the configured preferred codec. If one is configured,
+     * check it to see if currently it can be supported by the
+     * platform. If it is configured and is availble to support,
+     * put the preferred codec in the first one of the list.
+     */
+    pref_codec = sip_config_preferred_codec();
+    if (pref_codec != RTP_NONE) {
+        /*
+         * There is a configured preferred codec, check to see if
+         * the codec is currently avaible or not.
+         */
+        codec = &current_codec_table[0];
+        while (*codec != RTP_NONE) {
+            if (pref_codec == *codec) {
+                preferred_codec_available = TRUE;
+                break;
+            }
+            codec++;
+        }
+    }
+
+    if (preferred_codec_available) {
+        /*
+         * The preferred codec is configured and the platform
+         * currently can support the preferred codec, put it in
+         * the first entry.
+         */
+        aSupportedCodecs[count] = pref_codec;
+        count++;
+    } else {
+        /*
+         * Must init or comparison will be made to uninitialized memory.
+         * Do not increment count here since we are not adding RTP_NONE
+         * as a supported codec. We are only initializing memory to a
+         * known value.
+         */
+        aSupportedCodecs[count] = RTP_NONE;
+    }
+
+    codec = &current_codec_table[0];
+    while (*codec != RTP_NONE) {
+        if (count < supportedCodecsLen) {
+            if (*codec != aSupportedCodecs[0]) {
+                aSupportedCodecs[count] = *codec;
+                count++;
+            }
+        }
+        codec++;
+    }
+    return count;
+}
+
+/*
+ * sip_config_local_supported_codecs_get()
+ *
+ * Get the locally supported codec list.
+ */
+uint16_t
+sip_config_video_supported_codecs_get (rtp_ptype aSupportedCodecs[],
+                          uint16_t supportedCodecsLen, boolean isOffer)
+{
+    uint16_t count = 0;
+    int codec_mask;
+    cc_uint32_t major_ver, minor_ver;
+
+    if ( isOffer ) {
+        codec_mask = vcmGetVideoCodecList(VCM_DSP_FULLDUPLEX);
+    } else {
+        /* we are trying to match the answer then we
+           already have the rx stream open */
+        //codec_mask = vcmGetVideoCodecList(DSP_ENCODEONLY);
+        codec_mask = vcmGetVideoCodecList(VCM_DSP_IGNORE);
+    }
+    if ( codec_mask & VCM_CODEC_RESOURCE_VP8) {
+      aSupportedCodecs[count] = RTP_VP8;
+      count++;
+    }
+    if ( codec_mask & VCM_CODEC_RESOURCE_H264) {
+      /*
+       * include payload type for packetization mode 1 only if ucm sis version
+       * is equal to or greater than 5.1.0 (AngelFire).
+       */
+      platGetSISProtocolVer(&major_ver, &minor_ver, NULL, NULL);
+      if ((major_ver > SIS_PROTOCOL_MAJOR_VERSION_ANGELFIRE) ||
+          (major_ver == SIS_PROTOCOL_MAJOR_VERSION_ANGELFIRE &&
+           minor_ver >= SIS_PROTOCOL_MINOR_VERSION_ANGELFIRE)) {
+          if (vcmGetVideoMaxSupportedPacketizationMode() == 1) {
+            aSupportedCodecs[count] = RTP_H264_P1;
+            count++;
+          }
+      }
+      aSupportedCodecs[count] = RTP_H264_P0;
+      count++;
+    }
+    if ( codec_mask & VCM_CODEC_RESOURCE_H263) {
+      aSupportedCodecs[count] = RTP_H263;
+      count++;
+    }
+
+    return count;
+}
+
+/**
+ * The function fills in the given codec array based on the
+ * platform bit mask of codecs.  Note, that the enumerated list
+ * produced is also in the preferred order.
+ *
+ * @param[in] codec_mask - platform bit mask corresponding to the
+ *                         codecs.
+ * @param[in/out] codecs - pointer to array of for storing the
+ *                         output of the enumerated codec based on
+ *                         bit set in the codec_mask.
+ *
+ * @return  None.
+ *
+ * @pre     (codec_table != NULL)
+ * @pre     storge of codec_table must be last enough to holds
+ *          supported codec in the bit mask.
+ */
+static void
+config_set_current_codec_table (int codec_mask, rtp_ptype *codecs)
+{
+    int idx = 0;
+
+    if (codec_mask & VCM_CODEC_RESOURCE_OPUS) {
+        codecs[idx] = RTP_OPUS;
+        idx++;
+    }
+
+    if (codec_mask & VCM_CODEC_RESOURCE_G711) {
+        codecs[idx] = RTP_PCMU;
+        idx++;
+        codecs[idx] = RTP_PCMA;
+        idx++;
+    }
+
+    if (codec_mask & VCM_CODEC_RESOURCE_G729A) {
+        codecs[idx] = RTP_G729;
+        idx++;
+    }
+
+    if (codec_mask & VCM_CODEC_RESOURCE_LINEAR) {
+        codecs[idx] = RTP_L16;
+        idx++;
+    }
+
+    if (codec_mask & VCM_CODEC_RESOURCE_G722) {
+        codecs[idx] = RTP_G722;
+        idx++;
+    }
+
+    if (codec_mask & VCM_CODEC_RESOURCE_iLBC) {
+        codecs[idx] = RTP_ILBC;
+        idx++;
+    }
+
+    if (codec_mask & VCM_CODEC_RESOURCE_iSAC) {
+        codecs[idx] = RTP_ISAC;
+        idx++;
+    }
+
+    codecs[idx] = RTP_NONE;
+
+    return;
+}
+
+/*
+ * sip_config_local_dtmf_dblevels_get()
+ *
+ * Get the DTMF DB levels
+ */
+uint32_t
+sip_config_local_dtmf_dblevels_get (void)
+{
+    int value;
+
+    config_get_value(CFGID_DTMF_DB_LEVEL, &value, sizeof(value));
+    switch (value) {
+    case 0:
+        return 0;     // Mute
+    case 1:
+        return 2900;  // 6 dB down
+    case 2:
+        return 4096;  // 3 dB down
+    case 3:
+        return 5786;  // Nominal amplitude
+                      // (-8.83 dBm0 to network, -11.83 dBm0 local)
+    case 4:
+        return 8173;  // 3 dB up
+    case 5:
+        return 11544; // 6 dB up
+    default:
+        return 5786;  // Nominal amplitude
+    }
+}
+
+/*
+ *  sip_config_get_line_by_called_number
+ *
+ *  Return the line by the given called_number
+ */
+line_t sip_config_get_line_by_called_number (line_t start_line, const char *called_number)
+{
+    int             i;
+    line_t          max_lines;
+    line_t          line = 0;
+    char            line_name[MAX_LINE_NAME_SIZE];
+    char            contact[MAX_LINE_CONTACT_SIZE];
+    char           *name;
+
+    max_lines = sip_config_local_line_get();
+
+    /*
+     * Check the called number for the E.164 "+"
+     * and ignore it if present.
+     */
+    if (called_number[0] == '+') {
+        called_number++;
+    }
+
+    for (i = start_line; i <= max_lines; i++) {
+        if (sip_config_check_line((line_t)i)) {
+            config_get_line_string(CFGID_LINE_NAME, line_name, i,
+                                   sizeof(line_name));
+            /*
+             * Check the configured line name for the E.164 "+"
+             * and ignore it if present.
+             */
+            name = &line_name[0];
+            if (line_name[0] == '+') {
+                name++;
+            }
+
+            if (cpr_strcasecmp(called_number, name) == 0) {
+                line = (line_t)i;
+                break;
+            }
+        }
+    }
+
+    // If line not found - check with contact list
+    if (line == 0) {
+        for (i = start_line; i <= max_lines; i++) {
+            if (sip_config_check_line((line_t)i)) {
+                config_get_line_string(CFGID_LINE_CONTACT, contact, i,
+                                       sizeof(contact));
+                if (cpr_strcasecmp(called_number, contact) == 0) {
+                    line = (line_t)i;
+                    break;
+                }
+            }
+        }
+    }
+
+    return (line);
+}
+
+/*********************************************************
+ *
+ *  SIP Config API
+ *  The routines below with the "prot" prefix are called
+ *  by the config system.  The calls that start with "sip"
+ *  are helper functions for the SIP implementation of the
+ *  "prot" API.
+ *
+ *********************************************************/
+
+/*
+ * sip_minimum_config_check()
+ *
+ * Return indication if the SIP minimum configuration
+ * requirements have been met.
+ * Returns 0 if minimum config is met
+ * Returns non-zero if minimum config has not been met
+ *   (eg. missing at least 1 required parameter)
+ */
+int
+sip_minimum_config_check (void)
+{
+    char str_val[MAX_IPADDR_STR_LEN];
+    char line_name[MAX_LINE_NAME_SIZE];
+    int value;
+
+    /*
+     * Make sure that line 1 is configured
+     */
+    config_get_line_string(CFGID_LINE_NAME, line_name, 1, sizeof(line_name));
+    if ((strcmp(line_name, UNPROVISIONED) == 0) || (line_name[0] == '\0')) {
+        return -1;
+    }
+
+    config_get_line_string(CFGID_PROXY_ADDRESS, str_val, 1, MAX_IPADDR_STR_LEN);
+    if ((strcmp(str_val, UNPROVISIONED) == 0) || (str_val[0] == '\0')) {
+        return -1;
+    }
+
+    config_get_line_value(CFGID_PROXY_PORT, &value, sizeof(value), 1);
+    if (value == 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * prot_config_change_notify()
+ * Let the SIP stack know that a config change has occurred.
+ *
+ */
+int
+prot_config_change_notify (int notify_type)
+{
+    if (SIPTaskProcessConfigChangeNotify(notify_type) < 0) {
+//CPR TODO: need reference for
+        CCSIP_DEBUG_ERROR(PLAT_COMMON_F_PREFIX"SIPTaskProcessConfigChangeNotify() "
+                          "returned error.\n", "prot_config_change_notify");
+    }
+
+    return (TRUE);
+}
+
+/*
+ * prot_config_check_line_name()
+ * Makes sure that there are no spaces in the SIP Line Names
+ *
+ * Returns: TRUE if the Name is Valid
+ *          FALSE if the Name is Invalid
+ *
+ */
+boolean
+prot_config_check_line_name (char *line_name)
+{
+    while ((*line_name != ' ') && (*line_name != NUL)) {
+        line_name++;
+    }
+
+    if (*line_name == ' ') {
+        return (FALSE);
+    }
+    return (TRUE);
+}
+
+/*
+ * prot_sanity_check_config_settings()
+ *
+ , Louis* Checks the sanity of the protocol config block values
+ * and sets them to defaults if they are incorrect.
+ */
+int
+prot_sanity_check_config_settings (void)
+{
+    int retval = 0;
+    return retval;
+}
+
+
+/*
+ * prot_shutdown()
+ *
+ * Shut down the protocol stack.
+ */
+
+void
+prot_shutdown (void)
+{
+    sip_shutdown();
+}
diff --git a/libs/sipcc/core/common/prot_configmgr.h b/libs/sipcc/core/common/prot_configmgr.h
new file mode 100755 (executable)
index 0000000..204c3b6
--- /dev/null
@@ -0,0 +1,298 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PROT_CONFIGMGR_H_
+#define _PROT_CONFIGMGR_H_
+
+#include "cpr_types.h"
+#include "phone_types.h"
+#include "rtp_defs.h"
+#include "ccsip_platform.h"
+#include "configmgr.h"
+#include "cfgfile_utils.h"
+#include "phone_platform_constants.h"
+#include "cc_config.h"
+#include "cc_constants.h"
+#include "ccsdp.h"
+
+#define UNPROVISIONED "UNPROVISIONED"
+
+/*********************************************************
+ *
+ *  The following parameters set Config system settings
+ *  for SIP
+ *
+ *********************************************************/
+#define HWTYPE "SIP"
+#define MAX_LINE_NAME_SIZE  128
+#define AUTH_NAME_SIZE 129
+#define MAX_LINE_PASSWORD_SIZE  32
+#define MAX_LINE_DISPLAY_SIZE  32
+#define MAX_LINE_CONTACT_SIZE  128
+#define MAX_LINE_AUTO_ANS_MODE_SIZE  32
+#define MAX_REG_USER_INFO_LEN 32
+#define MAX_JOIN_DXFER_POLICY_SIZE 40
+#define MAX_EXTERNAL_NUMBER_MASK_SIZE 40
+
+/*********************************************************
+ *!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ *  TNP SIP Phone Configuration IDs
+ *
+ *!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * The following macro definitions are defined in cc_config.h.
+ * Change should be made in the cc_config.h and add reference here.
+ *
+ *         <------ Original notes ------>
+ * Before changing this code, please read the following:
+ *
+ * The Configuration system for the TNP phones is simply a cache
+ * that exists for the GSM/SIP DLL to use.  The property values are
+ * sent from Java across the JNI to the cache.  This prevents
+ * the SIP and GSM code from having to suffer through a JNI call
+ * every time they wish to retrieve a configuration parameter.
+ *
+ * These ID's need to match the definitions in JplatConfigConstants.java
+ *
+ * To add a new value to the table,
+ * In general, you will have to:
+ *
+ * 1) Create an index for the new CFG param below
+ * 2) Update prot_cfg_table either in prot_cfgmgr_private.h
+ *    or in prot_configmgr.c (for line specific params)
+ * 3) Update JPlatConfigConstants.h with the new ID
+ * 4) Create a property on JAVA side and update it from XML config
+ * 5) Update show_cfg_cmd if adding a new line param
+ *
+ *!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *********************************************************/
+
+/* Keep non line specific params here */
+
+#define CFGID_MEDIA_PORT_RANGE_START       CFGID_MEDIA_PORT_RANGE_START_INT
+#define CFGID_MEDIA_PORT_RANGE_END         CFGID_MEDIA_PORT_RANGE_END_INT
+#define CFGID_CALLERID_BLOCKING            CFGID_CALLERID_BLOCKING_BOOL
+#define CFGID_ANONYMOUS_CALL_BLOCK         CFGID_ANONYMOUS_CALL_BLOCK_BOOL
+#define CFGID_DND_CALL_ALERT               CFGID_DND_CALL_ALERT_BYTE
+#define CFGID_DND_REMINDER_TIMER           CFGID_DND_REMINDER_TIMER_INT
+#define CFGID_PREFERRED_CODEC              CFGID_PREFERRED_CODEC_STRING
+#define CFGID_DTMF_OUTOFBAND               CFGID_DTMF_OUTOFBAND_STRING
+#define CFGID_DTMF_AVT_PAYLOAD             CFGID_DTMF_AVT_PAYLOAD_INT
+#define CFGID_DTMF_DB_LEVEL                CFGID_DTMF_DB_LEVEL_INT
+
+#define CFGID_SIP_RETX                     CFGID_SIP_RETX_INT
+#define CFGID_SIP_INVITE_RETX              CFGID_SIP_INVITE_RETX_INT
+#define CFGID_TIMER_T1                     CFGID_TIMER_T1_INT
+#define CFGID_TIMER_T2                     CFGID_TIMER_T2_INT
+#define CFGID_TIMER_INVITE_EXPIRES         CFGID_TIMER_INVITE_EXPIRES_INT
+#define CFGID_TIMER_REGISTER_EXPIRES       CFGID_TIMER_REGISTER_EXPIRES_INT
+
+#define CFGID_PROXY_REGISTER               CFGID_PROXY_REGISTER_BOOL
+#define CFGID_PROXY_BACKUP                 CFGID_PROXY_BACKUP_STRING
+#define CFGID_PROXY_BACKUP_PORT            CFGID_PROXY_BACKUP_PORT_INT
+#define CFGID_PROXY_EMERGENCY              CFGID_PROXY_EMERGENCY_STRING
+#define CFGID_PROXY_EMERGENCY_PORT         CFGID_PROXY_EMERGENCY_PORT_INT
+#define CFGID_OUTBOUND_PROXY               CFGID_OUTBOUND_PROXY_STRING
+#define CFGID_OUTBOUND_PROXY_PORT          CFGID_OUTBOUND_PROXY_PORT_INT
+
+#define CFGID_NAT_RECEIVED_PROCESSING      CFGID_NAT_RECEIVED_PROCESSING_BOOL
+#define CFGID_REG_USER_INFO                CFGID_REG_USER_INFO_STRING
+#define CFGID_CNF_JOIN_ENABLE              CFGID_CNF_JOIN_ENABLE_BOOL
+#define CFGID_REMOTE_PARTY_ID              CFGID_REMOTE_PARTY_ID_BOOL
+#define CFGID_SEMI_XFER                    CFGID_SEMI_XFER_BOOL
+#define CFGID_CALL_HOLD_RINGBACK           CFGID_CALL_HOLD_RINGBACK_BOOL
+#define CFGID_STUTTER_MSG_WAITING          CFGID_STUTTER_MSG_WAITING_BOOL
+/**
+ * The CFGID_CFWD_URL was consolidated for RT and CIUS and should be for TNP as well.
+ */
+#define CFGID_CFWD_URL                     CFGID_CFWD_URL_STRING
+
+#define CFGID_CALL_STATS                   CFGID_CALL_STATS_BOOL
+#define CFGID_LOCAL_CFWD_ENABLE            CFGID_LOCAL_CFWD_ENABLE_BOOL
+#define CFGID_TIMER_REGISTER_DELTA         CFGID_TIMER_REGISTER_DELTA_INT
+#define CFGID_SIP_MAX_FORWARDS             CFGID_SIP_MAX_FORWARDS_INT
+#define CFGID_2543_HOLD                    CFGID_2543_HOLD_BOOL
+
+#define CFGID_CCM1_ADDRESS                 CFGID_CCM1_ADDRESS_STRING
+#define CFGID_CCM2_ADDRESS                 CFGID_CCM2_ADDRESS_STRING
+#define CFGID_CCM3_ADDRESS                 CFGID_CCM3_ADDRESS_STRING
+
+// Note:  IPv6 Not currently supported on Cius
+#define CFGID_CCM1_IPV6_ADDRESS            CFGID_CCM1_IPV6_ADDRESS_STRING
+#define CFGID_CCM2_IPV6_ADDRESS            CFGID_CCM2_IPV6_ADDRESS_STRING
+#define CFGID_CCM3_IPV6_ADDRESS            CFGID_CCM3_IPV6_ADDRESS_STRING
+
+#define CFGID_CCM1_SIP_PORT                CFGID_CCM1_SIP_PORT_INT
+#define CFGID_CCM2_SIP_PORT                CFGID_CCM2_SIP_PORT_INT
+#define CFGID_CCM3_SIP_PORT                CFGID_CCM3_SIP_PORT_INT
+
+#define CFGID_CCM1_SEC_LEVEL               CFGID_CCM1_SEC_LEVEL_INT
+#define CFGID_CCM2_SEC_LEVEL               CFGID_CCM2_SEC_LEVEL_INT
+#define CFGID_CCM3_SEC_LEVEL               CFGID_CCM3_SEC_LEVEL_INT
+
+#define CFGID_CCM1_IS_VALID                CFGID_CCM1_IS_VALID_BOOL
+#define CFGID_CCM2_IS_VALID                CFGID_CCM2_IS_VALID_BOOL
+#define CFGID_CCM3_IS_VALID                CFGID_CCM3_IS_VALID_BOOL
+
+#define CFGID_CCM_TFTP_IP_ADDR             CFGID_CCM_TFTP_IP_ADDR_STRING
+#define CFGID_CCM_TFTP_PORT                CFGID_CCM_TFTP_PORT_INT
+#define CFGID_CCM_TFTP_IS_VALID            CFGID_CCM_TFTP_IS_VALID_BOOL
+#define CFGID_CCM_TFTP_SEC_LEVEL           CFGID_CCM_TFTP_SEC_LEVEL_INT
+
+#define CFGID_CONN_MONITOR_DURATION        CFGID_CONN_MONITOR_DURATION_INT
+#define CFGID_CALL_PICKUP_URI              CFGID_CALL_PICKUP_URI_STRING
+#define CFGID_CALL_PICKUP_LIST_URI         CFGID_CALL_PICKUP_LIST_URI_STRING
+#define CFGID_CALL_PICKUP_GROUP_URI        CFGID_CALL_PICKUP_GROUP_URI_STRING
+#define CFGID_MEET_ME_SERVICE_URI          CFGID_MEET_ME_SERVICE_URI_STRING
+#define CFGID_CALL_FORWARD_URI             CFGID_CALL_FORWARD_URI_STRING
+#define CFGID_ABBREVIATED_DIAL_URI         CFGID_ABBREVIATED_DIAL_URI_STRING
+#define CFGID_CALL_LOG_BLF_ENABLED         CFGID_CALL_LOG_BLF_ENABLED_BOOL
+#define CFGID_REMOTE_CC_ENABLED            CFGID_REMOTE_CC_ENABLED_BOOL
+#define CFGID_RETAIN_FORWARD_INFORMATION   CFGID_RETAIN_FORWARD_INFORMATION_BOOL
+
+#define CFGID_TIMER_KEEPALIVE_EXPIRES      CFGID_TIMER_KEEPALIVE_EXPIRES_INT
+#define CFGID_TIMER_SUBSCRIBE_EXPIRES      CFGID_TIMER_SUBSCRIBE_EXPIRES_INT
+#define CFGID_TIMER_SUBSCRIBE_DELTA        CFGID_TIMER_SUBSCRIBE_DELTA_INT
+#define CFGID_TRANSPORT_LAYER_PROT         CFGID_TRANSPORT_LAYER_PROT_INT
+#define CFGID_KPML_ENABLED                 CFGID_KPML_ENABLED_INT
+
+#define CFGID_NAT_ENABLE                   CFGID_NAT_ENABLE_BOOL
+#define CFGID_NAT_ADDRESS                  CFGID_NAT_ADDRESS_STRING
+#define CFGID_VOIP_CONTROL_PORT            CFGID_VOIP_CONTROL_PORT_INT
+#define CFGID_MY_IP_ADDR                   CFGID_MY_IP_ADDR_STRING
+#define CFGID_MY_MAC_ADDR                  CFGID_MY_MAC_ADDR_STRING
+#define CFGID_ENABLE_VAD                   CFGID_ENABLE_VAD_BOOL
+
+#define CFGID_AUTOANSWER_IDLE_ALTERNATE    CFGID_AUTOANSWER_IDLE_ALTERNATE_BOOL
+#define CFGID_AUTOANSWER_TIMER             CFGID_AUTOANSWER_TIMER_INT
+#define CFGID_AUTOANSWER_OVERRIDE          CFGID_AUTOANSWER_OVERRIDE_BOOL
+
+#define CFGID_OFFHOOK_TO_FIRST_DIGIT_TIMER CFGID_OFFHOOK_TO_FIRST_DIGIT_TIMER_INT
+#define CFGID_CALL_WAITING_SILENT_PERIOD   CFGID_CALL_WAITING_SILENT_PERIOD_INT
+#define CFGID_RING_SETTING_BUSY_POLICY     CFGID_RING_SETTING_BUSY_POLICY_INT
+#define CFGID_DSCP_FOR_CALL_CONTROL        CFGID_DSCP_FOR_CALL_CONTROL_INT
+#define CFGID_SPEAKER_ENABLED              CFGID_SPEAKER_ENABLED_BOOL
+#define CFGID_XFR_ONHOOK_ENABLED           CFGID_XFR_ONHOOK_ENABLED_BOOL
+#define CFGID_ROLLOVER                     CFGID_ROLLOVER_INT
+#define CFGID_LOAD_FILE                    CFGID_LOAD_FILE_STRING
+
+#define CFGID_BLF_ALERT_TONE_IDLE          CFGID_BLF_ALERT_TONE_IDLE_INT
+#define CFGID_BLF_ALERT_TONE_BUSY          CFGID_BLF_ALERT_TONE_BUSY_INT
+#define CFGID_AUTO_PICKUP_ENABLED          CFGID_AUTO_PICKUP_ENABLED_BOOL
+
+#define CFGID_JOIN_ACROSS_LINES            CFGID_JOIN_ACROSS_LINES_INT
+
+#define CFGID_MY_ACTIVE_MAC_ADDR           CFGID_MY_ACTIVE_MAC_ADDR_STRING
+#define CFGID_DSCP_AUDIO                   CFGID_DSCP_AUDIO_INT
+#define CFGID_DEVICE_NAME                  CFGID_DEVICE_NAME_STRING
+#define CFGID_USER_AGENT                   CFGID_USER_AGENT_STRING
+#define CFGID_MODEL_NUMBER                 CFGID_MODEL_NUMBER_STRING
+#define CFGID_DSCP_VIDEO                   CFGID_DSCP_VIDEO_INT
+
+#define CFGID_IP_ADDR_MODE                 CFGID_IP_ADDR_MODE_INT
+#define CFGID_INTER_DIGIT_TIMER            CFGID_INTER_DIGIT_TIMER_INT
+
+// Note - EMCC not currently supported on CIUS
+#define CFGID_EMCC_MODE                    CFGID_EMCC_MODE_BOOL
+#define CFGID_VISITING_EM_PORT             CFGID_VISITING_EM_PORT_INT
+#define CFGID_VISITING_EM_IP               CFGID_VISITING_EM_IP_STRING
+
+#define CFGID_CCM_EXTERNAL_NUMBER_MASK     CFGID_CCM_EXTERNAL_NUMBER_MASK_STRING
+#define CFGID_MEDIA_IP_ADDR                CFGID_MEDIA_IP_ADDR_STRING
+
+/* All non Line specific params should be added above */
+/* All Line specific params should be added below */
+
+#define CFGID_LINE_FEATURE                 CFGID_LINE_FEATURE_INT
+#define CFGID_LINE_INDEX                   CFGID_LINE_INDEX_INT
+#define CFGID_LINE_MAXNUMCALLS             CFGID_LINE_MAXNUMCALLS_INT
+#define CFGID_LINE_NAME                    CFGID_LINE_NAME_STRING
+#define CFGID_LINE_AUTHNAME                CFGID_LINE_AUTHNAME_STRING
+#define CFGID_LINE_PASSWORD                CFGID_LINE_PASSWORD_STRING
+#define CFGID_LINE_DISPLAYNAME             CFGID_LINE_DISPLAYNAME_STRING
+#define CFGID_LINE_CONTACT                 CFGID_LINE_CONTACT_STRING
+#define CFGID_PROXY_ADDRESS                CFGID_PROXY_ADDRESS_STRING
+#define CFGID_PROXY_PORT                   CFGID_PROXY_PORT_INT
+#define CFGID_LINE_AUTOANSWER_ENABLED      CFGID_LINE_AUTOANSWER_ENABLED_BYTE
+#define CFGID_LINE_AUTOANSWER_MODE         CFGID_LINE_AUTOANSWER_MODE_STRING
+#define CFGID_LINE_CALL_WAITING            CFGID_LINE_CALL_WAITING_BYTE
+#define CFGID_LINE_MSG_WAITING_LAMP        CFGID_LINE_MSG_WAITING_LAMP_BYTE
+#define CFGID_LINE_MESSAGE_WAITING_AMWI    CFGID_LINE_MESSAGE_WAITING_AMWI_BYTE
+#define CFGID_LINE_RING_SETTING_IDLE       CFGID_LINE_RING_SETTING_IDLE_BYTE
+#define CFGID_LINE_RING_SETTING_ACTIVE     CFGID_LINE_RING_SETTING_ACTIVE_BYTE
+#define CFGID_LINE_BUSY_TRIGGER            CFGID_LINE_BUSY_TRIGGER_INT
+#define CFGID_LINE_CFWDALL                 CFGID_LINE_CFWDALL_STRING
+
+#define CFGID_LINE_SPEEDDIAL_NUMBER                  CFGID_LINE_SPEEDDIAL_NUMBER_STRING
+#define CFGID_LINE_RETRIEVAL_PREFIX                  CFGID_LINE_RETRIEVAL_PREFIX_STRING
+#define CFGID_LINE_MESSAGES_NUMBER                   CFGID_LINE_MESSAGES_NUMBER_STRING
+#define CFGID_LINE_FWD_CALLER_NAME_DIPLAY            CFGID_LINE_FWD_CALLER_NAME_DIPLAY_BOOL
+#define CFGID_LINE_FWD_CALLER_NUMBER_DIPLAY          CFGID_LINE_FWD_CALLER_NUMBER_DIPLAY_BOOL
+#define CFGID_LINE_FWD_REDIRECTED_NUMBER_DIPLAY      CFGID_LINE_FWD_REDIRECTED_NUMBER_DIPLAY_BOOL
+#define CFGID_LINE_FWD_DIALED_NUMBER_DIPLAY          CFGID_LINE_FWD_DIALED_NUMBER_DIPLAY_BOOL
+#define CFGID_LINE_FEATURE_OPTION_MASK               CFGID_LINE_FEATURE_OPTION_MASK_INT
+#define CFGID_P2PSIP                                 CFGID_P2PSIP_BOOL
+#define CFGID_VERSION                                CFGID_VERSION_STRING
+#define CFGID_SDPMODE                                CFGID_SDPMODE_BOOL
+#define CFGID_RTCPMUX                                CFGID_RTCPMUX_BOOL
+#define CFGID_RTPSAVPF                               CFGID_RTPSAVPF_BOOL
+#define CFGID_MAXAVBITRATE                           CFGID_MAXAVBITRATE_BOOL
+#define CFGID_MAXCODEDAUDIOBW                        CFGID_MAXCODEDAUDIOBW_BOOL
+#define CFGID_USEDTX                                 CFGID_USEDTX_BOOL
+#define CFGID_STEREO                                 CFGID_STEREO_BOOL
+#define CFGID_USEINBANDFEC                           CFGID_USEINBANDFEC_BOOL
+#define CFGID_CBR                                    CFGID_CBR_BOOL
+#define CFGID_MAXPTIME                               CFGID_MAXPTIME_BOOL
+#define CFGID_SCTP_PORT                              CFGID_SCTP_PORT_INT
+#define CFGID_NUM_DATA_STREAMS                       CFGID_NUM_DATA_STREAMS_INT
+
+/*********************************************************
+ *
+ *  Value Definitions
+ *
+ *********************************************************/
+// Line feature
+typedef enum {
+    cfgLineFeatureNone                = CC_LINE_FEATURE_NONE,
+    cfgLineFeatureRedial              = CC_LINE_FEATURE_REDIAL,
+    cfgLineFeatureSpeedDial           = CC_LINE_FEATURE_SPEEDDIAL,
+    cfgLineFeatureDN                  = CC_LINE_FEATURE_DN,
+    cfgLineFeatureService             = CC_LINE_FEATURE_SERVICE,
+    cfgLineFeatureSpeedDialBLF        = CC_LINE_FEATURE_SPEEDDIALBLF,
+    cfgLineFeatureMaliciousCallID     = CC_LINE_FEATURE_MALICIOUSCALLID,
+    cfgLineFeatureAllCalls            = CC_LINE_FEATURE_ALLCALLS,
+    cfgLineFeatureAnswerOldest        = CC_LINE_FEATURE_ANSWEROLDEST,
+    cfgLineFeatureServices            = CC_LINE_FEATURE_SERVICES,
+    cfgLineFeatureBLF                 = CC_LINE_FEATURE_BLF
+} cfgLineFeatureType_e;
+
+/*********************************************************
+ *
+ *  Function Prototypes
+ *
+ *********************************************************/
+void protocol_cfg_init(void);
+void sip_config_get_net_device_ipaddr(cpr_ip_addr_t *ip_addr);
+void sip_config_get_net_ipv6_device_ipaddr(cpr_ip_addr_t *ip_addr);
+void sip_config_get_nat_ipaddr(cpr_ip_addr_t *ip_addr);
+void sip_config_set_nat_ipaddr(cpr_ip_addr_t *ip_address);
+uint16_t sip_config_local_supported_codecs_get(rtp_ptype aSupportedCodecs[],
+                                               uint16_t supportedCodecsLen);
+uint16_t sip_config_video_supported_codecs_get(rtp_ptype aSupportedCodecs[],
+                              uint16_t supportedCodecsLen, boolean isOffer);
+
+boolean prot_config_check_line_name(char *line_name);
+//const key_table_entry_t * sip_config_local_codec_entry_find(const rtp_ptype codec);
+line_t sip_config_get_button_from_line(line_t line);
+line_t sip_config_get_line_from_button(line_t button);
+boolean sip_config_check_line(line_t line);
+line_t sip_config_local_line_get(void);
+void sip_config_get_display_name(line_t line, char *buffer, int buffer_len);
+line_t sip_config_get_line_by_called_number(line_t start_line, const char *called_number);
+int sip_minimum_config_check(void);
+void config_set_codec_table(int codec_mask);
+int sip_config_get_keepalive_expires();
+rtp_ptype sip_config_preferred_codec(void);
+
+#endif /* PROT_CONFIGMGR_H_ */
diff --git a/libs/sipcc/core/common/resource_manager.c b/libs/sipcc/core/common/resource_manager.c
new file mode 100644 (file)
index 0000000..6e969a1
--- /dev/null
@@ -0,0 +1,300 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "resource_manager.h"
+#include "phone_debug.h"
+
+#define RM_NUM_ELEMENTS_PER_MAP 32
+#define rm_get_table_index(a) (a / RM_NUM_ELEMENTS_PER_MAP)
+#define rm_get_map_offset(a)  (a % RM_NUM_ELEMENTS_PER_MAP)
+
+/*
+ * rm_clear_all_elements
+ *
+ * Description:
+ *    This function clears all members of the specified resource manager
+ *
+ * Parameters:
+ *    rm_p - pointer to the resource manager to be cleared
+ *
+ * Returns:
+ *    None
+ */
+void
+rm_clear_all_elements (resource_manager_t *rm_p)
+{
+    static const char fname[] = "rm_clear_all_elements";
+    uint16_t i;
+
+    if (!rm_p) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"null resource manager received.\n", fname);
+        return;
+    }
+
+    for (i = 0; i < rm_p->max_index; i++) {
+        rm_p->table[i] = 0;
+    }
+}
+
+/*
+ * rm_clear_element
+ *
+ * Description:
+ *    This function clears a single element from the specified resource manager
+ *
+ * Parameters:
+ *    rm_p    - pointer to the resource manager to be cleared
+ *    element - element id of element to be cleared
+ *
+ * Returns:
+ *    None
+ */
+void
+rm_clear_element (resource_manager_t * rm_p, int16_t element)
+{
+    static const char fname[] = "rm_clear_elements";
+
+    if (!rm_p) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"null resource manager received.\n", fname);
+        return;
+    }
+
+    if (element < 0 || element >= rm_p->max_element) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"element value %d invalid. Max value is %d.\n",
+               fname, element, rm_p->max_element - 1);
+        return;
+    }
+
+    rm_p->table[rm_get_table_index(element)] &=
+        (~(1 << rm_get_map_offset(element)));
+}
+
+/*
+ * rm_set_element
+ *
+ * Description:
+ *    This function sets the bit representing the specified element
+ *    in the specified resource manager.
+ *
+ * Parameters:
+ *    rm_p    - pointer to the resource manager
+ *    element - element id of element to be set
+ *
+ * Returns:
+ *    None
+ */
+void
+rm_set_element (resource_manager_t *rm_p, int16_t element)
+{
+    static const char fname[] = "rm_set_element";
+
+    if (!rm_p) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"null resource manager received.\n", fname);
+        return;
+    }
+
+    if (element < 0 || element >= rm_p->max_element) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"element value %d invalid. Max value %d.\n",
+               fname, element, rm_p->max_element - 1);
+        return;
+    }
+
+    rm_p->table[rm_get_table_index(element)] |=
+        (1 << rm_get_map_offset(element));
+}
+
+/*
+ * rm_is_element_set
+ *
+ * Description:
+ *    This function checks if the specified element in the specified
+ *    resource manager is set.
+ *
+ * Parameters:
+ *    rm_p    - pointer to the resource manager.
+ *    element - element id of element to be checked.
+ *
+ * Returns:
+ *    TRUE if element is set, else FALSE
+ */
+boolean
+rm_is_element_set (resource_manager_t *rm_p, int16_t element)
+{
+    static const char fname[] = "rm_is_element_set";
+
+    if (!rm_p) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"null resource manager received.\n", fname);
+        return FALSE;
+    }
+
+    if (element < 0 || element >= rm_p->max_element) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"element value %d invalid. Max value %d.\n",
+               fname, element, rm_p->max_element - 1);
+        return FALSE;
+    }
+
+    if (rm_p->table[rm_get_table_index(element)] &
+        (1 << rm_get_map_offset(element))) {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*
+ * rm_get_free_element
+ *
+ * Description:
+ *    This function walks through the members of the resource manager and
+ *    attempts to locate a free element. If a free element is found, the
+ *    element's associated bit is set in the resource manager and the
+ *    element id is returned.
+ *
+ * Parameters:
+ *    rm_p - pointer to the resource manager.
+ *
+ * Returns:
+ *    If an element is available, a element id (from zero to max element)
+ *    If no element is available, -1 is returned.
+ */
+int16_t
+rm_get_free_element (resource_manager_t *rm_p)
+{
+    static const char fname[] = "rm_get_free_element";
+    int16_t element = -1;
+    uint16_t i, j;
+    uint32_t max_map = 0;
+
+    max_map = ~max_map;
+
+    if (!rm_p) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"null resource manager received.\n", fname);
+        return -1;
+    }
+
+    for (i = 0; i < rm_p->max_index && element == -1; i++) {
+        if (rm_p->table[i] != max_map) {
+            for (j = 0; j < RM_NUM_ELEMENTS_PER_MAP && element == -1; j++) {
+                if (!(rm_p->table[i] & (1 << j))) {
+                    element = i * RM_NUM_ELEMENTS_PER_MAP + j;
+                    if (element < rm_p->max_element) {
+                        rm_set_element(rm_p, element);
+                    }
+                }
+            }
+        }
+    }
+
+    if (element >= rm_p->max_element) {
+        element = -1;
+    }
+    return (element);
+}
+
+/*
+ * rm_show
+ *
+ * Description:
+ *    Utility function used to dump the contents of the resource manager.
+ *
+ * Parameters:
+ *    rm_p - pointer to the resource manager.
+ *
+ * Returns:
+ *    none
+ */
+void
+rm_show (resource_manager_t *rm_p)
+{
+    static const char fname[] = "rm_show";
+    int16_t element = 0;
+    uint16_t i, j;
+
+    if (!rm_p) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"null resource manager received.\n", fname);
+        return;
+    }
+
+    for (i = 0; i < rm_p->max_index; i++) {
+        for (j = 0; j < RM_NUM_ELEMENTS_PER_MAP; j++) {
+            if (rm_p->table[i] & (1 << j)) {
+                element = (i * RM_NUM_ELEMENTS_PER_MAP) + j;
+                TNP_DEBUG(DEB_F_PREFIX"rm map: %d\n", DEB_F_PREFIX_ARGS(RM, fname), element);
+            }
+        }
+    }
+}
+
+/*
+ * rm_create
+ *
+ * Description:
+ *    Allocates and initializes a new resource manager
+ *
+ * Parameters:
+ *    max_element - Maximum number of elements the resource manager
+ *                  is required to track
+ *
+ * Returns:
+ *    If successful, pointer to the newly allocated resource manager
+ *    If not successful, NULL
+ */
+resource_manager_t *
+rm_create (int16_t max_element)
+{
+    static const char fname[] = "rm_create";
+    resource_manager_t *rm_p;
+
+    if (max_element < 0) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"invalid max element %d received.\n", fname,
+               max_element);
+        return NULL;
+    }
+
+    rm_p = (resource_manager_t *) cpr_malloc(sizeof(resource_manager_t));
+    if (!rm_p) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"unable to allocate resource manager.\n", fname);
+        return NULL;
+    }
+
+    rm_p->max_element = max_element;
+    rm_p->max_index = max_element / RM_NUM_ELEMENTS_PER_MAP + 1;
+
+    rm_p->table = (uint32_t *)
+        cpr_malloc(rm_p->max_index * RM_NUM_ELEMENTS_PER_MAP);
+    if (!rm_p->table) {
+        free(rm_p);
+        return NULL;
+    }
+    rm_clear_all_elements(rm_p);
+    return rm_p;
+}
+
+/*
+ * rm_free
+ *
+ * Description:
+ *    This function frees the memory allocated for the specified resource manager.
+ *
+ * Parameters:
+ *    rm_p - pointer to the resource manager.
+ *
+ * Returns:
+ *    none
+ */
+void
+rm_destroy (resource_manager_t *rm_p)
+{
+    static const char fname[] = "rm_destroy";
+
+    if (!rm_p) {
+        PLAT_ERROR(PLAT_COMMON_F_PREFIX"null resource manager received.\n", fname);
+        return;
+    }
+
+    cpr_free(rm_p->table);
+    cpr_free(rm_p);
+}
diff --git a/libs/sipcc/core/common/resource_manager.h b/libs/sipcc/core/common/resource_manager.h
new file mode 100644 (file)
index 0000000..4d34be5
--- /dev/null
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RM_MGR_H__
+#define _RM_MGR_H_
+
+typedef struct resource_manager {
+    int16_t  max_element;
+    int16_t  max_index;
+    uint32_t *table;
+} resource_manager_t;
+
+void               rm_clear_all_elements(resource_manager_t *rm);
+void               rm_clear_element(resource_manager_t *rm, int16_t element);
+void               rm_set_element(resource_manager_t *rm, int16_t element);
+boolean            rm_is_element_set(resource_manager_t *rm, int16_t element);
+int16_t            rm_get_free_element(resource_manager_t *rm);
+void               rm_show(resource_manager_t *rm);
+resource_manager_t *rm_create(int16_t max_element);
+void               rm_destroy(resource_manager_t *rm);
+
+#endif
diff --git a/libs/sipcc/core/common/sip_socket_api.c b/libs/sipcc/core/common/sip_socket_api.c
new file mode 100755 (executable)
index 0000000..1595284
--- /dev/null
@@ -0,0 +1,100 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_socket.h"
+#include "errno.h"
+#include "plat_api.h"
+
+/**
+ * sipSocketSend
+ *
+ * @brief The sipSocketSend() function is a wrapper used by the sipstack to send
+ * data over a socket. This function decides to use the secure versus unsecure
+ * connection based on the "secure" flag.
+ *
+ * @note - The implementation of both secure/non-secure is the same in RT/TNP
+ * products. It is different for the other vendors and hence we need this
+ * flexibility.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags  - The options used for the send.
+ *
+ *
+ */
+ssize_t
+sipSocketSend (cpr_socket_t soc,
+         CONST void *buf,
+         size_t len,
+         int32_t flags,
+         boolean secure)
+{
+//   if (secure) {
+//      return platSecSocSend (soc, buf, len);
+//   } else {
+      return cprSend(soc, buf, len, flags);
+//   }
+}
+
+/**
+ * sipSocketRecv
+ *
+ * @brief The sipSocketRecv() function is a wrapper used by the sipstack to send
+ * data over a socket. This function decides to use the secure versus unsecure
+ * connection based on the "secure" flag.
+ *
+ * @note - The implementation of both secure/non-secure is the same in RT/TNP
+ * products. It is different for the other vendors and hence we need this
+ * flexibility.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags  - The options used for the recv.
+ */
+ssize_t
+sipSocketRecv (cpr_socket_t soc,
+         void * RESTRICT buf,
+         size_t len,
+         int32_t flags,
+         boolean secure)
+{
+//   if (secure) {
+//      return platSecSocRecv (soc, buf, len);
+//   } else {
+      return cprRecv(soc, buf, len, flags);
+//   }
+}
+
+/**
+ * sipSocketClose
+ *
+ * @brief The sipSocketClose() function is a wrapper used by the sipstack to
+ * close a socket. This function decides to use the secure versus unsecure
+ * connection based on the "secure" flag.
+ *
+ * @note - The implementation of both secure/non-secure is the same in RT/TNP
+ * products. It is different for the other vendors and hence we need this
+ * flexibility.
+ *
+ * @param[in] soc  - The socket that needs to be destroyed
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+sipSocketClose (cpr_socket_t soc,
+                boolean secure)
+{
+//   if (secure) {
+//      return platSecSocClose (soc);
+//   } else {
+      return cprCloseSocket(soc);
+//   }
+}
+
diff --git a/libs/sipcc/core/common/subscription_handler.c b/libs/sipcc/core/common/subscription_handler.c
new file mode 100755 (executable)
index 0000000..f587379
--- /dev/null
@@ -0,0 +1,342 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cc_types.h"
+#include "phone_platform_constants.h"
+#include "cc_constants.h"
+#include "phone_debug.h"
+#include "prot_configmgr.h"
+#include "cc_blf.h"
+#include "ccapi_snapshot.h"
+
+#define SPEEDDIAL_START_BUTTON_NUMBER 2
+
+static unsigned char transactionIds[MAX_REG_LINES];
+static boolean displayBLFState = TRUE;
+static cc_blf_state_t blfStates[MAX_REG_LINES];
+static boolean isBLFHandlerRunning = FALSE;
+static boolean isAvailable = FALSE;
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+
+static void ccBLFHandlerInitialized();
+
+/*
+ *  Function:    sub_hndlr_isAlertingBLFState
+ *
+ *  Description: returns if the BLF state is "alerting"
+ *
+ *  Parameters:
+ *    inst - line button number.
+ *
+ *  Returns:    TRUE/FALSE
+ */
+boolean sub_hndlr_isAlertingBLFState(int inst)
+{
+    static const char fname[] = "sub_hndlr_isAlertingBLFState";
+
+    if ((displayBLFState == TRUE) && (blfStates[inst - 1] == CC_SIP_BLF_ALERTING)) {
+        CCAPP_DEBUG(DEB_F_PREFIX"inst=%d, isAlerting=TRUE\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                    inst);
+
+        return TRUE;
+    }
+    CCAPP_DEBUG(DEB_F_PREFIX"inst=%d, isAlerting=FALSE\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                inst);
+    return FALSE;
+}
+
+/*
+ *  Function:    sub_hndlr_isInUseBLFState
+ *
+ *  Description: returns if the BLF state is "in use"
+ *
+ *  Parameters:
+ *    inst - line button number.
+ *
+ *  Returns:    TRUE/FALSE
+ */
+boolean sub_hndlr_isInUseBLFState(int inst)
+{
+    static const char fname[] = "sub_hndlr_isInUseBLFState";
+
+    if ((displayBLFState == TRUE) && (blfStates[inst - 1] == CC_SIP_BLF_INUSE)) {
+    CCAPP_DEBUG(DEB_F_PREFIX"inst=%d, isInUse=TRUE\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                inst);
+        return TRUE;
+    }
+    CCAPP_DEBUG(DEB_F_PREFIX"inst=%d, isInUse=FALSE\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                inst);
+    return FALSE;
+}
+
+/*
+ *  Function:    sub_hndlr_isAvailable
+ *
+ *  Description: returns if the subscription handler is available.
+ *
+ *  Parameters: none.
+ *
+ *  Returns:    TRUE/FALSE
+ */
+boolean sub_hndlr_isAvailable()
+{
+    static const char fname[] = "sub_hndlr_isAvailable";
+
+    CCAPP_DEBUG(DEB_F_PREFIX"isAvailable=%d\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                isAvailable);
+    return isAvailable;
+}
+
+static unsigned short get_new_trans_id()
+{
+    static unsigned short curr_trans_id = 0;
+
+    if (++curr_trans_id == 0) {
+        curr_trans_id = 1;
+    }
+
+    return curr_trans_id;
+}
+
+/*
+ *  Function:    sub_hndlr_start
+ *
+ *  Description: does blf subscriptions upon registration.
+ *
+ *  Parameters: none.
+ *
+ *  Returns:   void
+ */
+void sub_hndlr_start()
+{
+    static const char fname[] = "sub_hndlr_start";
+    int i;
+    cc_uint32_t lineFeature = 0;
+    cc_uint32_t featureOptionMask = 0;
+    char speedDialNumber[MAX_LINE_NAME_SIZE] = {0};
+    char primaryLine[MAX_LINE_NAME_SIZE] = {0};
+    int transId;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"entering\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+    /* let the system know that subscription handler is available. */
+    isAvailable = TRUE;
+
+    /* get primary DN */
+    config_get_line_string(CFGID_LINE_NAME, primaryLine, 1, sizeof(primaryLine));
+
+    /*
+     * for speeddial/BLF buttons, make presence subscriptions.
+     */
+    for (i = SPEEDDIAL_START_BUTTON_NUMBER; i <= MAX_REG_LINES; i++) {
+        // first line must always be a calling line.
+        config_get_line_value(CFGID_LINE_FEATURE, &lineFeature, sizeof(lineFeature), i);
+
+
+        CCAPP_DEBUG(DEB_F_PREFIX"inst=%d, lineFeature=%d\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                    i, lineFeature);
+        switch (lineFeature) {
+        case cfgLineFeatureSpeedDialBLF:
+            config_get_line_string(CFGID_LINE_SPEEDDIAL_NUMBER, speedDialNumber, i, sizeof(speedDialNumber));
+            if (speedDialNumber[0] == 0) {
+                break;
+            }
+            config_get_line_value(CFGID_LINE_FEATURE, &featureOptionMask, sizeof(featureOptionMask), i);
+
+            transId = get_new_trans_id();
+            transactionIds[i - 1] = transId;
+            CC_BLF_subscribe(transId,
+                             INT_MAX,
+                             primaryLine,
+                             speedDialNumber,
+                             i,
+                             featureOptionMask );
+            break;
+        default:
+            break;
+        }
+
+        //Initializes native BLF handler
+        ccBLFHandlerInitialized();
+    }
+}
+
+static void ccBLFHandlerInitialized()
+{
+    if (!isBLFHandlerRunning) {
+        CC_BLF_init();
+        isBLFHandlerRunning = TRUE;
+    }
+}
+
+/*
+ *  Function:    sub_hndlr_stop
+ *
+ *  Description: terminates blf subscriptions upon unregistration.
+ *
+ *  Parameters: none.
+ *
+ *  Returns:    void
+ */
+void sub_hndlr_stop()
+{
+    static const char fname[] = "sub_hndlr_stop";
+    int i;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"entering\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+    isAvailable = FALSE;
+    isBLFHandlerRunning = FALSE;
+
+    // should clean up blf susbcription list.
+    for (i = SPEEDDIAL_START_BUTTON_NUMBER; i <= MAX_REG_LINES; i++) {
+        //first, reset the transaction ids
+        transactionIds[i - 1] = 0;
+        //reset blf states.
+        blfStates[i - 1] = CC_SIP_BLF_UNKNOWN;
+    }
+    CC_BLF_unsubscribe_All();
+}
+
+
+/*
+ *  Function:    hideBLFButtonsDisplay
+ *
+ *  Description: hides BLF states
+ *
+ *  Parameters: none.
+ *
+ *  Returns:    void
+ */
+static void hideBLFButtonsDisplay()
+{
+    static const char fname[] = "hideBLFButtonsDisplay";
+    int i;
+    cc_uint32_t lineFeature = 0;
+
+    CCAPP_DEBUG(DEB_F_PREFIX"entering\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+    displayBLFState = FALSE;
+    for (i = SPEEDDIAL_START_BUTTON_NUMBER; i <= MAX_REG_LINES; i++) {
+        // first line must always be a calling line.
+        config_get_line_value(CFGID_LINE_FEATURE, &lineFeature, sizeof(lineFeature), i);
+
+        switch (lineFeature) {
+        case cfgLineFeatureSpeedDialBLF:
+            ccsnap_gen_blfFeatureEvent(CC_SIP_BLF_UNKNOWN, i);
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+/*
+ *  Function:    unhideBLFButtonsDisplay
+ *
+ *  Description: unhides BLF states.
+ *
+ *  Parameters: none.
+ *
+ *  Returns:    void
+ */
+static void unhideBLFButtonsDisplay()
+{
+    static const char fname[] = "unhideBLFButtonsDisplay";
+    int i;
+    cc_uint32_t lineFeature = 0;
+    char speedDialNumber[MAX_LINE_NAME_SIZE] = {0};
+
+    CCAPP_DEBUG(DEB_F_PREFIX"entering\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+
+    displayBLFState = TRUE;
+
+    for (i = SPEEDDIAL_START_BUTTON_NUMBER; i <= MAX_REG_LINES; i++) {
+        // first line must always be a calling line.
+        config_get_line_value(CFGID_LINE_FEATURE, &lineFeature, sizeof(lineFeature), i);
+        config_get_line_string(CFGID_LINE_SPEEDDIAL_NUMBER, speedDialNumber, i, sizeof(speedDialNumber));
+
+        switch (lineFeature) {
+        case cfgLineFeatureSpeedDialBLF:
+            ccsnap_gen_blfFeatureEvent(blfStates[i - 1], i);
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+/*
+ *  Function:    sub_hndlr_controlBLFButtons
+ *
+ *  Description: hides/unhides BLF states.
+ *
+ *  Parameters: none.
+ *
+ *  Returns:    void
+ */
+void sub_hndlr_controlBLFButtons(boolean state)
+{
+    static const char fname[] = "sub_hndlr_controlBLFButtons";
+
+    if (state == TRUE) {
+        CCAPP_DEBUG(DEB_F_PREFIX"going to hide\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+        hideBLFButtonsDisplay();
+    } else {
+        CCAPP_DEBUG(DEB_F_PREFIX"going to unhide\n",
+                    DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
+        unhideBLFButtonsDisplay();
+    }
+}
+
+/*
+ *  Function:    sub_hndlr_NotifyBLFStatus
+ *
+ *  Description: notifies the app of BLF state.
+ *
+ *  Parameters:
+ *      requestId - requestId of the subscription
+ *      status - BLF status
+ *      appId -  button number of the BLF feature key.
+ *
+ *  Returns:    void
+ */
+void sub_hndlr_NotifyBLFStatus(int requestId, cc_blf_state_t status, int appId)
+{
+    static const char fname[] = "sub_hndlr_NotifyBLFStatus";
+    cc_uint32_t lineFeature = 0;
+    char speedDialNumber[MAX_LINE_NAME_SIZE] = {0};
+
+
+    CCAPP_DEBUG(DEB_F_PREFIX"requestId=%d, status=%d, appId=%d\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname),
+                requestId, status, appId);
+    if (appId == 0) {
+        // call list BLF.
+    } else {
+        config_get_line_value(CFGID_LINE_FEATURE, &lineFeature, sizeof(lineFeature), appId);
+        config_get_line_string(CFGID_LINE_SPEEDDIAL_NUMBER, speedDialNumber, appId, sizeof(speedDialNumber));
+
+        blfStates[appId - 1] = status;
+        if (displayBLFState == FALSE) {
+            return; // ignore the notify
+        }
+        if (lineFeature == cfgLineFeatureSpeedDialBLF) {
+            ccsnap_gen_blfFeatureEvent(status, appId);
+        }
+    }
+}
+
diff --git a/libs/sipcc/core/common/subscription_handler.h b/libs/sipcc/core/common/subscription_handler.h
new file mode 100755 (executable)
index 0000000..3bcf731
--- /dev/null
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __SUB_HANDLER_H__
+#define __SUB_HANDLER_H__
+
+boolean sub_hndlr_isAlertingBLFState(int inst);
+
+boolean sub_hndlr_isInUseBLFState(int inst);
+
+boolean sub_hndlr_isAvailable();
+
+void sub_hndlr_start();
+
+void sub_hndlr_stop();
+
+void sub_hndlr_controlBLFButtons(boolean state);
+
+void sub_hndlr_NotifyBLFStatus(int requestId, int status, int appId);
+
+#endif
+
diff --git a/libs/sipcc/core/common/text_strings.c b/libs/sipcc/core/common/text_strings.c
new file mode 100755 (executable)
index 0000000..b8576c9
--- /dev/null
@@ -0,0 +1,361 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <cpr_string.h>
+#include "text_strings.h"
+
+
+#define dcl_str(x,p) char str_##x[] = p
+#define use_str(x) str_##x
+
+
+/*
+ * Debug strings. No localization needed. Keep it separate.
+ */
+
+dcl_str(DEBUG_START, "SIPCC-START:\0");
+dcl_str(DEBUG_SEPARATOR_BAR, "===============\n");
+dcl_str(DEBUG_CONSOLE_PASSWORD, "CONSOLE-PWD:cisco");
+dcl_str(DEBUG_CONSOLE_KEYWORD_CONSOLE_STALL, "CONSOLE-STALL");
+dcl_str(DEBUG_CONSOLE_KEYWORD_MEMORYMAP, "CONSOLE-MEMORYMAP");
+dcl_str(DEBUG_CONSOLE_KEYWORD_MALLOCTABLE, "CONSOLE-MALLOCTABLE");
+dcl_str(DEBUG_CONSOLE_KEYWORD_MEMORYDUMP, "CONSOLE-DUMP");
+dcl_str(DEBUG_CONSOLE_KEYWORD_DNS, "CONSOLE-DNS");
+dcl_str(DEBUG_CONSOLE_KEYWORD_DSPSTATE, "CONSOLE-DSPSTATE");
+dcl_str(DEBUG_CONSOLE_USAGE_MEMORYDUMP, "CONSOLE-MEMORYDUMP: addr bytes [cnt blk [1/0 char output]]\n");
+dcl_str(DEBUG_CONSOLE_BREAK, "CONSOLE-BREAK\r\n");
+
+dcl_str(DEBUG_FUNCTION_ENTRY, "SIPCC-FUNC_ENTRY: LINE %d/%d: %-35s: %s <- %s\n");
+dcl_str(DEBUG_FUNCTION_ENTRY2, "SIPCC-FUNC_ENTRY: LINE %d/%d: %-35s: %s <- %s(%d)\n");
+dcl_str(DEBUG_SIP_ENTRY, "SIPCC-ENTRY: LINE %d/%d: %-35s: %s\n");
+dcl_str(DEBUG_SIP_URL_ERROR, "SIPCC-%s: Error: URL is not SIP.\n");
+dcl_str(DEBUG_LINE_NUMBER_INVALID, "SIPCC-LINE_NUM: %s: Error: Line number (%d) is invalid\n");
+dcl_str(DEBUG_SIP_SPI_SEND_ERROR, "SIPCC-SPI_SEND_ERR: %s: Error: sipSPISendErrorResponse(%d) failed.\n");
+dcl_str(DEBUG_SIP_SDP_CREATE_BUF_ERROR, "SIPCC-SDP_BUF: %s: Error: sipsdp_src_dest_create() returned null\n");
+dcl_str(DEBUG_SIP_PARSE_SDP_ERROR, "SIPCC-SDP_PARSE: %s: Error: sdp_parse()\n");
+dcl_str(DEBUG_SIP_FEATURE_UNSUPPORTED, "SIPCC-FEATURE: LINE %d/%d: %-35s: This feature is unsupported in the current state.\n");
+dcl_str(DEBUG_SIP_DEST_SDP, "SIPCC-SDP_DEST: LINE %d/%d: %-35s: Process SDP: Dest=<%s>:<%d>\n");
+dcl_str(DEBUG_SIP_MSG_SENDING_REQUEST, "SIPCC-MSG_SEND_REQ: %s: Sending %s...\n");
+dcl_str(DEBUG_SIP_MSG_SENDING_RESPONSE, "SIPCC-MSG_SEND_RESP: %s: Sending response %d...\n");
+dcl_str(DEBUG_SIP_MSG_RECV, "SIPCC-MSG_RECV: %s: Received SIP message %s.\n");
+dcl_str(DEBUG_SIP_STATE_UNCHANGED, "SIPCC-SIP_STATE: LINE %d/%d: %-35s: State unchanged -> %s\n");
+dcl_str(DEBUG_SIP_FUNCTIONCALL_FAILED, "SIPCC-FUNC_CALL: LINE %d/%d: %-35s: Error: %s returned error.\n");
+dcl_str(DEBUG_SIP_BUILDFLAG_ERROR, "SIPCC-BUILD_FLAG: %s: Error: Build flag is not successful. Will not send message.\n");
+dcl_str(DEBUG_GENERAL_FUNCTIONCALL_FAILED, "SIPCC-FUNC_CALL: %s: Error: %s returned error.\n");
+dcl_str(DEBUG_GENERAL_SYSTEMCALL_FAILED, "SIPCC-SYS_CALL: %s: Error: %s failed: errno = %d\n");
+dcl_str(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT, "SIPCC-FUNC_CALL: %s: Error: invalid argument: %s\n");
+dcl_str(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_FROM, "SIPCC-FUNC_NAME: sippmh_parse_from_or_to(FROM)");
+dcl_str(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO, "SIPCC-FUNC_NAME: sippmh_parse_from_or_to(TO)");
+dcl_str(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE, "SIPCC-SM_REQ: sip_sm_request_check_and_store()");
+dcl_str(DEBUG_SNTP_LI_ERROR, "SIPCC-SNTP: Leap indicator == 3\n");
+dcl_str(DEBUG_SNTP_MODE_ERROR, "SIPCC-SNTP: Mode is not server (4/5) in response\n");
+dcl_str(DEBUG_SNTP_STRATUM_ERROR, "SIPCC-SNTP: Invalid stratum > 15\n");
+dcl_str(DEBUG_SNTP_TIMESTAMP_ERROR, "SIPCC-SNTP: Server did not echo our transmit timestamp\n");
+dcl_str(DEBUG_SNTP_TIMESTAMP1, "SIPCC-SNTP: %-15s: 0x%08x %08x, ");
+dcl_str(DEBUG_SNTP_TIMESTAMP2, "SIPCC-SNTP: (%+03d:%02d) %s");
+dcl_str(DEBUG_SNTP_TIME_UPDATE, "SIPCC-SNTP: Updating date and time to:\n%s %lu %02lu:%02lu:%02lu "
+ "%04lu, %s, week %lu and day %lu.  "
+ "Daylight saving is: %d\n\n");
+dcl_str(DEBUG_SNTP_TS_HEADER, "SIPCC-SNTP: Settings:\nMode        : %lu\nTimezone    "
+ ": %d\nServer Addr : %s\nTimeStruct  : TZ/Offset: %d/%lu,"
+ " AutoAdjust: %d\nMo  Day DoW WoM Time\n");
+dcl_str(DEBUG_SNTP_TS_PRINT, "SIPCC-SNTP: %3lu %3lu %3lu %3lu %4lu\n");
+dcl_str(DEBUG_SNTP_SOCKET_REOPEN, "SIPCC-SNTP: Re-opening listening port due to IP change\n");
+dcl_str(DEBUG_SNTP_DISABLED, "SIPCC-SNTP: Unicast w/server addr 0.0.0.0, SNTP disabled\n");
+dcl_str(DEBUG_SNTP_REQUEST, "SIPCC-SNTP: Sending NTP request packet [%s]\n");
+dcl_str(DEBUG_SNTP_RESPONSE, "SIPCC-SNTP: Receiving NTP response packet\n");
+dcl_str(DEBUG_SNTP_RETRANSMIT, "SIPCC-SNTP: Waiting %d msec to retransmit\n");
+dcl_str(DEBUG_SNTP_UNICAST_MODE, "SIPCC-SNTP: Unicast mode [%s]\n");
+dcl_str(DEBUG_SNTP_MULTICAST_MODE, "SIPCC-SNTP: Multicast/Directed broadcast mode [%s]\n");
+dcl_str(DEBUG_SNTP_ANYCAST_MODE, "SIPCC-SNTP: Anycast mode [%s]\n");
+dcl_str(DEBUG_SNTP_VALIDATION, "SIPCC-SNTP: Dropping unauthorized SNTP response: %s\n");
+dcl_str(DEBUG_SNTP_VALIDATION_PACKET, "SIPCC-SNTP: Semantic check failed for NTP packet\n");
+dcl_str(DEBUG_SNTP_WRONG_SERVER, "SIPCC-SNTP: Unauthorized server");
+dcl_str(DEBUG_SNTP_NO_REQUEST, "SIPCC-SNTP: No request sent");
+dcl_str(DEBUG_SNTP_ANYCAST_RESET, "SIPCC-SNTP: Reset to Unicast to server: %s\n");
+dcl_str(DEBUG_SOCKET_UDP_RTP, "SIPCC-SOC_TASK: UDP_RTP event received.\n");
+dcl_str(DEBUG_MAC_PRINT, "SIPCC-MAC_PRINT: %04x:%04x:%04x");
+dcl_str(DEBUG_IP_PRINT, "SIPCC-IP_PRINT: %u.%u.%u.%u");
+dcl_str(DEBUG_SYSBUF_UNAVAILABLE, "SIPCC-SYS_BUF: %s: Error: IRXLstGet() failed\n");
+dcl_str(DEBUG_MSG_BUFFER_TOO_BIG, "SIPCC-MSG_BUF: %s: Error: Args Check: message buffer length (%d) too big.\n");
+dcl_str(DEBUG_UNKNOWN_TIMER_BLOCK, "SIPCC-TIMER: %s: Error: Unknown timer block\n");
+dcl_str(DEBUG_CREDENTIALS_BAG_CORRUPTED, "SIPCC-CRED: %-35s: Error: credentials bags corrupted");
+dcl_str(DEBUG_INPUT_NULL, "SIPCC-INPUT: %s: Error: Input is null\n");
+dcl_str(DEBUG_INPUT_EMPTY, "SIPCC-INPUT: %s: Error: Input is empty\n");
+dcl_str(DEBUG_STRING_DUP_FAILED, "SIPCC-STR_DUP: %s: Unable to duplicate string.\n");
+dcl_str(DEBUG_PARSER_STRING_TOO_LARGE, "SIPCC-PARSE: Parse error: string too big (%d,%d)\n");
+dcl_str(DEBUG_PARSER_NULL_KEY_TABLE, "SIPCC-PARSE: Parse error: NULL key table passed into parser\n");
+dcl_str(DEBUG_PARSER_UNKNOWN_KEY, "SIPCC-PARSE: Parse error: Unknown key name: %s\n");
+dcl_str(DEBUG_PARSER_UNKNOWN_KEY_ENUM, "SIPCC-PARSE: Print error: Unknown key enum: %d\n");
+dcl_str(DEBUG_PARSER_INVALID_START_VAR, "SIPCC-PARSE: Parse error: Invalid start variable ch=0x%02x(%c)\n");
+dcl_str(DEBUG_PARSER_INVALID_VAR_CHAR, "SIPCC-PARSE: Parse error: Invalid variable ch=0x%02x(%c)\n");
+dcl_str(DEBUG_PARSER_MISSING_COLON, "SIPCC-PARSE: Parse error: Missing colon separator\n");
+dcl_str(DEBUG_PARSER_NO_VALUE, "SIPCC-PARSE: Parse error: no value for variable\n");
+dcl_str(DEBUG_PARSER_EARLY_EOL, "SIPCC-PARSE: Parse error: early EOL for value\n");
+dcl_str(DEBUG_PARSER_INVALID_VAR_NAME, "SIPCC-PARSE: Parse error: parse_var_name failed: %d\n");
+dcl_str(DEBUG_PARSER_INVALID_VAR_VALUE, "SIPCC-PARSE: Parse error: var: %s  parse_var_value failed: %d\n");
+dcl_str(DEBUG_PARSER_UNKNOWN_VAR, "SIPCC-PARSE: Parse error: var: %s not found in table\n");
+dcl_str(DEBUG_PARSER_NAME_VALUE, "SIPCC-PARSE: Name: [%s]  Value: [%s]\n");
+dcl_str(DEBUG_PARSER_UNKNOWN_NAME_VALUE, "SIPCC-PARSE: Parse error: Name: [%s] Value: [%s] rc:%d\n");
+dcl_str(DEBUG_PARSER_UNKNOWN_ERROR, "SIPCC-PARSE: Default error: Name: [%s] Value: [%s] rc:%d\n");
+dcl_str(DEBUG_PARSER_NUM_ERRORS, "SIPCC-PARSE: Parse error: %d Errors found\n");
+dcl_str(DEBUG_PARSER_SET_DEFAULT, "SIPCC-PARSE: Parser Info: Setting var: %s to default value: %s\n\n");
+dcl_str(DEBUG_SDP_ERROR_BODY_FIELD, "SIPCC-SDP: \n%s: Error in one of the SDP body fields \n");
+dcl_str(DEBUG_UDP_OPEN_FAIL, "SIPCC-UDP: %s: UdpOpen(R IP=%d, R Port=%d, L Port=%d) failed\n");
+dcl_str(DEBUG_UDP_PAYLOAD_TOO_LARGE, "SIPCC-UDP: %s: Error: payload size=<%d> > allowed size=<%d>\n");
+dcl_str(DEBUG_RTP_TRANSPORT, "SIPCC-RTP: %s: transport= %d\n");
+dcl_str(DEBUG_RTP_INVALID_VOIP_TYPE, "SIPCC-RTP: %s: Error: Unexpected voipCodec_t type: <%d>\n");
+dcl_str(DEBUG_RTP_INVALID_RTP_TYPE, "SIPCC-RTP: %s: Error: Unexpected rtp_ptype in SDP body: <%d>\n");
+dcl_str(DEBUG_MEMORY_ALLOC, "SIPCC-MEM: Malloc Addr:0x%lx, Size:%d\n");
+dcl_str(DEBUG_MEMORY_FREE, "SIPCC-MEM: Free Addr:0x%lx, Size:%d\n");
+dcl_str(DEBUG_MEMORY_MALLOC_ERROR, "SIPCC-MEM: 0x%lx:Malloc error for size %d\n");
+dcl_str(DEBUG_MEMORY_REALLOC_ERROR, "SIPCC-MEM: %s: Error: malloc_tagged() returned null.\n");
+dcl_str(DEBUG_MEMORY_OUT_OF_MEM, "SIPCC-MEM: %s: Error: malloc failed\n");
+dcl_str(DEBUG_MEMORY_ENTRY, "SIPCC-MEM: >> Used: %1d size: %6d addr:0x%08x\n");
+dcl_str(DEBUG_MEMORY_SUMMARY, "===== MEMORY MAP START =====\n"
+ "free blocks : %6d, free block space:%6d, largest free block: %6d\n"
+ "used blocks : %6d, used block space:%6d, largest used block: %6d\n"
+ "wasted block: %6d, str_lib space   :%6d\n"
+ "used space excluding str_lib space :%6d\n \n"
+ "=====  MEMORY MAP END  =====\n");
+dcl_str(DEBUG_MEMORY_ADDRESS_HEADER, "SIPCC-MEM: 0x%08x: ");
+dcl_str(DEBUG_MEMORY_DUMP, "SIPCC-MEM: DUMP: 0x%08x - 0x%08x\n");
+dcl_str(DEBUG_DNS_GETHOSTBYNAME, "SIPCC-DNS: gethostbyname('%s',%08x,%d,%d)\n");
+dcl_str(DEBUG_PMH_INCORRECT_SYNTAX, "SIPCC-PMH: INCORRECT SYNTAX");
+dcl_str(DEBUG_PMH_INVALID_FIELD_VALUE, "SIPCC-PMH: INVALID FIELD VALUE");
+dcl_str(DEBUG_PMH_INVALID_SCHEME, "SIPCC-PMH: INVALID SCHEME");
+dcl_str(DEBUG_PMH_UNKNOWN_SCHEME, "SIPCC-PMH: UNKNOWN SCHEME");
+dcl_str(DEBUG_PMH_NOT_ENOUGH_PARAMETERS, "SIPCC-PMH: NOT ENOUGH PARAMETERS");
+dcl_str(DEBUG_REG_DISABLED, "SIPCC-REG: LINE %d/%d: %-35s: registration disabled\n");
+dcl_str(DEBUG_REG_PROXY_EXPIRES, "SIPCC-REG: LINE %d/%d: %-35s: Using proxy expires value\n");
+dcl_str(DEBUG_REG_SIP_DATE, "SIPCC-REG: LINE %d/%d: %-35s: SIP-date= %s\n");
+dcl_str(DEBUG_REG_SIP_RESP_CODE, "SIPCC-REG: LINE %d/%d: %-35s: Error: SIP response code\n");
+dcl_str(DEBUG_REG_SIP_RESP_FAILURE, "SIPCC-REG: LINE %d/%d: %-35s: SIP failure %d resp\n");
+dcl_str(DEBUG_REG_INVALID_LINE, "SIPCC-REG: %-35s: Line %d: Invalid line\n");
+dcl_str(CC_NO_MSG_BUFFER, "SIPCC-MSG_BUF: %s : no msg buffer available\n");
+dcl_str(CC_SEND_FAILURE, "SIPCC-MSG_SEND: %s : unable to send msg\n");
+dcl_str(GSM_UNDEFINED, "SIPCC-GSM: UNDEFINED");
+dcl_str(GSM_DBG_PTR, "SIPCC-GSM_DBG_PTR: %s %-4d: %-35s: %s= %p\n");
+dcl_str(GSM_FUNC_ENTER, "SIPCC-GSM_FUNC_ENT: %s %-4d: %-35s\n");
+dcl_str(GSM_DBG1, "SIPCC-GSM: %s %-4d: %-35s: %s\n");
+dcl_str(FSM_DBG_SM_DEFAULT_EVENT, "SIPCC-FSM: default - ignoring.\n");
+dcl_str(FSM_DBG_SM_FTR_ENTRY, "SIPCC-FSM: feature= %s, src= %s\n");
+dcl_str(FSM_DBG_FAC_ERR, "SIPCC-FSM_FAC_ERR: %-4d: %-35s:\n    %s, rc= %s\n");
+dcl_str(FSM_DBG_FAC_FOUND, "SIPCC-FSM: %-4d: %-35s: facility found(%d)\n");
+dcl_str(FSM_DBG_IGNORE_FTR, "SIPCC-FSM: %s %-4d: %8d: ignoring feature= %s\n");
+dcl_str(FSM_DBG_IGNORE_SRC, "SIPCC-FSM: %s %-4d: %8d: ignoring src= %s\n");
+dcl_str(FSM_DBG_CHANGE_STATE, "SIPCC-FSM: %s %-4d: %8d: %s -> %s\n");
+dcl_str(FSM_DBG_SDP_BUILD_ERR, "SIPCC-FSM: Unable to build SDP. \n");
+dcl_str(FSMDEF_DBG_PTR, "SIPCC-FSM: DEF %-4d/%d: %-35s: dcb= %p\n");
+dcl_str(FSMDEF_DBG1, "SIPCC-FSM: DEF %-4d/%d: %-35s: %s\n");
+dcl_str(FSMDEF_DBG2, "SIPCC-FSM: DEF %-4d/%d: %-35s: %s %d\n");
+dcl_str(FSMDEF_DBG_SDP, "SIPCC-FSM: DEF %-4d/%d: %-35s: addr= %s, port= %d,\n    media_type(s)=");
+dcl_str(FSMDEF_DBG_CLR_SPOOF_APPLD, "SIPCC-FSM: DEF %-4d/%d: %-35s: clearing spoof_ringout_applied.\n");
+dcl_str(FSMDEF_DBG_CLR_SPOOF_RQSTD, "SIPCC-FSM: DEF %-4d/%d: %-35s: clearing spoof_ringout_requested.\n");
+dcl_str(FSMDEF_DBG_INVALID_DCB, "SIPCC-FSM: DEF 0   : %-35s: invalid dcb\n");
+dcl_str(FSMDEF_DBG_FTR_REQ_ACT, "SIPCC-FSM: DEF %-4d/%d: feature requested %s but %s is active.\n");
+dcl_str(FSMDEF_DBG_TMR_CREATE_FAILED, "SIPCC-FSM: DEF %-4d/%d: %-35s: %s:\n    cprCreateTimer failed.\n");
+dcl_str(FSMDEF_DBG_TMR_START_FAILED, "SIPCC-FSM: DEF %-4d/%d: %-35s: %s:\n    cprStartTimer failed, errno= %d.\n");
+dcl_str(FSMDEF_DBG_TMR_CANCEL_FAILED, "SIPCC-FSM: DEF %-4d/%d: %-35s: %s:\n    cprCancelTimer failed, errno= %d.\n");
+dcl_str(FSMXFR_DBG_XFR_INITIATED, "SIPCC-FSM: XFR %-4d/%d/%d: %8d: xfer initiated\n");
+dcl_str(FSMXFR_DBG_PTR, "SIPCC-FSM: XFR %-4d/%d/%d: %-35s: xcb= %p\n");
+dcl_str(FSMCNF_DBG_CNF_INITIATED, "SIPCC-FSM: CNF %-4d/%d/%d: %8d: conf initiated\n");
+dcl_str(FSMCNF_DBG_PTR, "SIPCC-FSM: CNF %-4d/%d/%d: %-35s: ncb= %p\n");
+dcl_str(FSMB2BCNF_DBG_CNF_INITIATED, "SIPCC-FSM: B2BCNF %-4d/%d/%d: %8d: b2bconf initiated\n");
+dcl_str(FSMB2BCNF_DBG_PTR, "SIPCC-FSM: B2BCNF %-4d/%d/%d: %-35s: ncb= %p\n");
+dcl_str(FSMSHR_DBG_BARGE_INITIATED, "SIPCC-FSM: SHR %-4d/%d/%d: %8d: Barge initiated\n");
+dcl_str(LSM_DBG_ENTRY, "SIPCC-LSM: %-4d/%d: %-35s\n");
+dcl_str(LSM_DBG_INT1, "SIPCC-LSM: %-4d/%d: %-35s: %s= %d\n");
+dcl_str(LSM_DBG_CC_ERROR, "SIPCC-LSM: %-4d/%d: %-35s: (%d:%p) failure\n");
+dcl_str(VCM_DEBUG_ENTRY, "SIPCC-VCM: %-4d: %-35s\n");
+dcl_str(SM_PROCESS_EVENT_ERROR, "SIPCC-SM: %s: Error: sip_sm_process_event() returned error processing %d\n");
+dcl_str(REG_SM_PROCESS_EVENT_ERROR, "SIPCC-SM: %s: Error: sip_reg_sm_process_event() returned error processing %d\n");
+dcl_str(DEBUG_END, "SIPCC-END: \0");
+
+/*
+ * Debug string table NOT subject to localization
+ */
+debug_string_table_entry debug_string_table [] = {
+    {0},                                            // DEBUG_START
+    {use_str(DEBUG_SEPARATOR_BAR)},                 // DEBUG_SEPARATOR_BAR
+    {use_str(DEBUG_CONSOLE_PASSWORD)},              // DEBUG_CONSOLE_PASSWORD
+    {use_str(DEBUG_CONSOLE_KEYWORD_CONSOLE_STALL)}, // DEBUG_CONSOLE_KEYWORD_CONSOLE_STALL
+    {use_str(DEBUG_CONSOLE_KEYWORD_MEMORYMAP)},     // DEBUG_CONSOLE_KEYWORD_MEMORYMAP
+    {use_str(DEBUG_CONSOLE_KEYWORD_MALLOCTABLE)},   // DEBUG_CONSOLE_KEYWORD_MALLOCTABLE
+    {use_str(DEBUG_CONSOLE_KEYWORD_MEMORYDUMP)},    // DEBUG_CONSOLE_KEYWORD_MEMORYDUMP
+    {use_str(DEBUG_CONSOLE_KEYWORD_DNS)},           // DEBUG_CONSOLE_KEYWORD_DNS
+    {use_str(DEBUG_CONSOLE_KEYWORD_DSPSTATE)},      // DEBUG_CONSOLE_KEYWORD_DSPSTATE
+    {use_str(DEBUG_CONSOLE_USAGE_MEMORYDUMP)},      // DEBUG_CONSOLE_USAGE_MEMORYDUMP
+    {use_str(DEBUG_CONSOLE_BREAK)},                 // DEBUG_CONSOLE_BREAK
+    {use_str(DEBUG_FUNCTION_ENTRY)},                // DEBUG_FUNCTION_ENTRY
+    {use_str(DEBUG_FUNCTION_ENTRY2)},               // DEBUG_FUNCTION_ENTRY2
+    {use_str(DEBUG_SIP_ENTRY)},                     // DEBUG_SIP_ENTRY
+    {use_str(DEBUG_SIP_URL_ERROR)},                 // DEBUG_SIP_URL_ERROR
+    {use_str(DEBUG_LINE_NUMBER_INVALID)},           // DEBUG_LINE_NUMBER_INVALID
+    {use_str(DEBUG_SIP_SPI_SEND_ERROR)},            // DEBUG_SIP_SPI_SEND_ERROR
+    {use_str(DEBUG_SIP_SDP_CREATE_BUF_ERROR)},      // DEBUG_SIP_SDP_CREATE_BUF_ERROR
+    {use_str(DEBUG_SIP_PARSE_SDP_ERROR)},           // DEBUG_SIP_PARSE_SDP_ERROR
+    {use_str(DEBUG_SIP_FEATURE_UNSUPPORTED)},       // DEBUG_SIP_FEATURE_UNSUPPORTED
+    {use_str(DEBUG_SIP_DEST_SDP)},                  // DEBUG_SIP_DEST_SDP
+    {use_str(DEBUG_SIP_MSG_SENDING_REQUEST)},       // DEBUG_SIP_MSG_SENDING_REQUEST
+    {use_str(DEBUG_SIP_MSG_SENDING_RESPONSE)},      // DEBUG_SIP_MSG_SENDING_RESPONSE
+    {use_str(DEBUG_SIP_MSG_RECV)},                  // DEBUG_SIP_MSG_RECV
+    {use_str(DEBUG_SIP_STATE_UNCHANGED)},           // DEBUG_SIP_STATE_UNCHANGED
+    {use_str(DEBUG_SIP_FUNCTIONCALL_FAILED)},       // DEBUG_SIP_FUNCTIONCALL_FAILED
+    {use_str(DEBUG_SIP_BUILDFLAG_ERROR)},           // DEBUG_SIP_BUILDFLAG_ERROR
+    {use_str(DEBUG_GENERAL_FUNCTIONCALL_FAILED)},   // DEBUG_GENERAL_FUNCTIONCALL_FAILED
+    {use_str(DEBUG_GENERAL_SYSTEMCALL_FAILED)},     // DEBUG_GENERAL_SYSTEMCALL_FAILED
+    {use_str(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT)}, // DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT
+    {use_str(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_FROM)},// DEBUG_FUNCTIONNAME_SIP_PARSE_FROM
+    {use_str(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO)},  // DEBUG_FUNCTIONNAME_SIP_PARSE_TO
+    {use_str(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE)}, // DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE
+    {use_str(DEBUG_SNTP_LI_ERROR)},                 // DEBUG_SNTP_LI_ERROR
+    {use_str(DEBUG_SNTP_MODE_ERROR)},               // DEBUG_SNTP_MODE_ERROR
+    {use_str(DEBUG_SNTP_STRATUM_ERROR)},            // DEBUG_SNTP_STRATUM_ERROR
+    {use_str(DEBUG_SNTP_TIMESTAMP_ERROR)},          // DEBUG_SNTP_TIMESTAMP_ERROR
+    {use_str(DEBUG_SNTP_TIMESTAMP1)},               // DEBUG_SNTP_TIMESTAMP1
+    {use_str(DEBUG_SNTP_TIMESTAMP2)},               // DEBUG_SNTP_TIMESTAMP2
+    {use_str(DEBUG_SNTP_TIME_UPDATE)},              // DEBUG_SNTP_TIME_UPDATE
+    {use_str(DEBUG_SNTP_TS_HEADER)},                // DEBUG_SNTP_TS_HEADER
+    {use_str(DEBUG_SNTP_TS_PRINT)},                 // DEBUG_SNTP_TS_PRINT
+    {use_str(DEBUG_SNTP_SOCKET_REOPEN)},            // DEBUG_SNTP_SOCKET_REOPEN
+    {use_str(DEBUG_SNTP_DISABLED)},                 // DEBUG_SNTP_DISABLED
+    {use_str(DEBUG_SNTP_REQUEST)},                  // DEBUG_SNTP_REQUEST
+    {use_str(DEBUG_SNTP_RESPONSE)},                 // DEBUG_SNTP_RESPONSE
+    {use_str(DEBUG_SNTP_RETRANSMIT)},               // DEBUG_SNTP_RETRANSMIT
+    {use_str(DEBUG_SNTP_UNICAST_MODE)},             // DEBUG_SNTP_UNICAST_MODE
+    {use_str(DEBUG_SNTP_MULTICAST_MODE)},           // DEBUG_SNTP_MULTICAST_MODE
+    {use_str(DEBUG_SNTP_ANYCAST_MODE)},             // DEBUG_SNTP_ANYCAST_MODE
+    {use_str(DEBUG_SNTP_VALIDATION)},               // DEBUG_SNTP_VALIDATION
+    {use_str(DEBUG_SNTP_VALIDATION_PACKET)},        // DEBUG_SNTP_VALIDATION_PACKET
+    {use_str(DEBUG_SNTP_WRONG_SERVER)},             // DEBUG_SNTP_WRONG_SERVER
+    {use_str(DEBUG_SNTP_NO_REQUEST)},               // DEBUG_SNTP_NO_REQUEST
+    {use_str(DEBUG_SNTP_ANYCAST_RESET)},            // DEBUG_SNTP_ANYCAST_RESET
+    {use_str(DEBUG_SOCKET_UDP_RTP)},                // DEBUG_SOCKET_UDP_RTP
+    {use_str(DEBUG_MAC_PRINT)},                     // DEBUG_MAC_PRINT
+    {use_str(DEBUG_IP_PRINT)},                      // DEBUG_IP_PRINT
+    {use_str(DEBUG_SYSBUF_UNAVAILABLE)},            // DEBUG_SYSBUF_UNAVAILABLE
+    {use_str(DEBUG_MSG_BUFFER_TOO_BIG)},            // DEBUG_MSG_BUFFER_TOO_BIG
+    {use_str(DEBUG_UNKNOWN_TIMER_BLOCK)},           // DEBUG_UNKNOWN_TIMER_BLOCK
+    {use_str(DEBUG_CREDENTIALS_BAG_CORRUPTED)},     // DEBUG_CREDENTIALS_BAG_CORRUPTED
+    {use_str(DEBUG_INPUT_NULL)},                    // DEBUG_INPUT_NULL
+    {use_str(DEBUG_INPUT_EMPTY)},                   // DEBUG_INPUT_EMPTY
+    {use_str(DEBUG_STRING_DUP_FAILED)},             // DEBUG_STRING_DUP_FAILED
+    {use_str(DEBUG_PARSER_STRING_TOO_LARGE)},       // DEBUG_PARSER_STRING_TOO_LARGE
+    {use_str(DEBUG_PARSER_NULL_KEY_TABLE)},         // DEBUG_PARSER_NULL_KEY_TABLE
+    {use_str(DEBUG_PARSER_UNKNOWN_KEY)},            // DEBUG_PARSER_UNKNOWN_KEY
+    {use_str(DEBUG_PARSER_UNKNOWN_KEY_ENUM)},       // DEBUG_PARSER_UNKNOWN_KEY_ENUM
+    {use_str(DEBUG_PARSER_INVALID_START_VAR)},      // DEBUG_PARSER_INVALID_START_VAR
+    {use_str(DEBUG_PARSER_INVALID_VAR_CHAR)},       // DEBUG_PARSER_INVALID_VAR_CHAR
+    {use_str(DEBUG_PARSER_MISSING_COLON)},          // DEBUG_PARSER_MISSING_COLON
+    {use_str(DEBUG_PARSER_NO_VALUE)},               // DEBUG_PARSER_NO_VALUE
+    {use_str(DEBUG_PARSER_EARLY_EOL)},              // DEBUG_PARSER_EARLY_EOL
+    {use_str(DEBUG_PARSER_INVALID_VAR_NAME)},       // DEBUG_PARSER_INVALID_VAR_NAME
+    {use_str(DEBUG_PARSER_INVALID_VAR_VALUE)},      // DEBUG_PARSER_INVALID_VAR_VALUE
+    {use_str(DEBUG_PARSER_UNKNOWN_VAR)},            // DEBUG_PARSER_UNKNOWN_VAR
+    {use_str(DEBUG_PARSER_NAME_VALUE)},             // DEBUG_PARSER_NAME_VALUE
+    {use_str(DEBUG_PARSER_UNKNOWN_NAME_VALUE)},     // DEBUG_PARSER_UNKNOWN_NAME_VALUE
+    {use_str(DEBUG_PARSER_UNKNOWN_ERROR)},          // DEBUG_PARSER_UNKNOWN_ERROR
+    {use_str(DEBUG_PARSER_NUM_ERRORS)},             // DEBUG_PARSER_NUM_ERRORS
+    {use_str(DEBUG_PARSER_SET_DEFAULT)},            // DEBUG_PARSER_SET_DEFAULT
+    {use_str(DEBUG_SDP_ERROR_BODY_FIELD)},          // DEBUG_SDP_ERROR_BODY_FIELD
+    {use_str(DEBUG_UDP_OPEN_FAIL)},                 // DEBUG_UDP_OPEN_FAIL
+    {use_str(DEBUG_UDP_PAYLOAD_TOO_LARGE)},         // DEBUG_UDP_PAYLOAD_TOO_LARGE
+    {use_str(DEBUG_RTP_TRANSPORT)},                 // DEBUG_RTP_TRANSPORT
+    {use_str(DEBUG_RTP_INVALID_VOIP_TYPE)},         // DEBUG_RTP_INVALID_VOIP_TYPE
+    {use_str(DEBUG_RTP_INVALID_RTP_TYPE)},          // DEBUG_RTP_INVALID_RTP_TYPE
+    {use_str(DEBUG_MEMORY_ALLOC)},                  // DEBUG_MEMORY_ALLOC
+    {use_str(DEBUG_MEMORY_FREE)},                   // DEBUG_MEMORY_FREE
+    {use_str(DEBUG_MEMORY_MALLOC_ERROR)},           // DEBUG_MEMORY_MALLOC_ERROR
+    {use_str(DEBUG_MEMORY_REALLOC_ERROR)},          // DEBUG_MEMORY_REALLOC_ERROR
+    {use_str(DEBUG_MEMORY_OUT_OF_MEM)},             // DEBUG_MEMORY_OUT_OF_MEM
+    {use_str(DEBUG_MEMORY_ENTRY)},                  // DEBUG_MEMORY_ENTRY
+    {use_str(DEBUG_MEMORY_SUMMARY)},                // DEBUG_MEMORY_SUMMARY
+    {use_str(DEBUG_MEMORY_ADDRESS_HEADER)},         // DEBUG_MEMORY_ADDRESS_HEADER
+    {use_str(DEBUG_MEMORY_DUMP)},                   // DEBUG_MEMORY_DUMP
+    {use_str(DEBUG_DNS_GETHOSTBYNAME)},             // DEBUG_DNS_GETHOSTBYNAME
+    {use_str(DEBUG_PMH_INCORRECT_SYNTAX)},          // DEBUG_PMH_INCORRECT_SYNTAX
+    {use_str(DEBUG_PMH_INVALID_FIELD_VALUE)},       // DEBUG_PMH_INVALID_FIELD_VALUE
+    {use_str(DEBUG_PMH_INVALID_SCHEME)},            // DEBUG_PMH_INVALID_SCHEME
+    {use_str(DEBUG_PMH_UNKNOWN_SCHEME)},            // DEBUG_PMH_UNKNOWN_SCHEME
+    {use_str(DEBUG_PMH_NOT_ENOUGH_PARAMETERS)},     // DEBUG_PMH_NOT_ENOUGH_PARAMETERS
+    {use_str(DEBUG_REG_DISABLED)},                  // DEBUG_REG_DISABLED
+    {use_str(DEBUG_REG_PROXY_EXPIRES)},             // DEBUG_REG_PROXY_EXPIRES
+    {use_str(DEBUG_REG_SIP_DATE)},                  // DEBUG_REG_SIP_DATE
+    {use_str(DEBUG_REG_SIP_RESP_CODE)},             // DEBUG_REG_SIP_RESP_CODE
+    {use_str(DEBUG_REG_SIP_RESP_FAILURE)},          // DEBUG_REG_SIP_RESP_FAILURE
+    {use_str(DEBUG_REG_INVALID_LINE)},              // DEBUG_REG_INVALID_LINE
+    {use_str(CC_NO_MSG_BUFFER)},                    // CC_NO_MSG_BUFFER^M
+    {use_str(CC_SEND_FAILURE)},                     // CC_SEND_FAILURE^M
+    {use_str(GSM_UNDEFINED)},                       // GSM_UNDEFINED
+    {use_str(GSM_DBG_PTR)},                         // GSM_DBG_PTR
+    {use_str(GSM_FUNC_ENTER)},                      // GSM_FUNC_ENTER
+    {use_str(GSM_DBG1)},                            // GSM_DBG1
+    {use_str(FSM_DBG_SM_DEFAULT_EVENT)},            // FSM_DBG_SM_DEFAULT_EVENT
+    {use_str(FSM_DBG_SM_FTR_ENTRY)},                // FSM_DBG_SM_FTR_ENTRY
+    {use_str(FSM_DBG_FAC_ERR)},                     // FSM_DBG_FAC_ERR
+    {use_str(FSM_DBG_FAC_FOUND)},                   // FSM_DBG_FAC_FOUND
+    {use_str(FSM_DBG_IGNORE_FTR)},                  // FSM_DBG_IGNORE_FTR
+    {use_str(FSM_DBG_IGNORE_SRC)},                  // FSM_DBG_IGNORE_SRC
+    {use_str(FSM_DBG_CHANGE_STATE)},                // FSM_DBG_CHANGE_STATE
+    {use_str(FSM_DBG_SDP_BUILD_ERR)},               // FSM_DBG_SDP_BUILD_ERR
+    {use_str(FSMDEF_DBG_PTR)},                      // FSMDEF_DBG_PTR
+    {use_str(FSMDEF_DBG1)},                         // FSMDEF_DBG1
+    {use_str(FSMDEF_DBG2)},                         // FSMDEF_DBG2
+    {use_str(FSMDEF_DBG_SDP)},                      // FSMDEF_DBG_SDP
+    {use_str(FSMDEF_DBG_CLR_SPOOF_APPLD)},          // FSMDEF_DBG_CLEAR_SPOOF
+    {use_str(FSMDEF_DBG_CLR_SPOOF_RQSTD)},          // FSMDEF_DBG_CLEAR_SPOOF
+    {use_str(FSMDEF_DBG_INVALID_DCB)},              // FSMDEF_DBG_INVALID_DCB
+    {use_str(FSMDEF_DBG_FTR_REQ_ACT)},              // FSMDEF_DBG_FTR_REQ_ACT
+    {use_str(FSMDEF_DBG_TMR_CREATE_FAILED)},        // FSMDEF_DBG_TMR_CREATE_FAILED
+    {use_str(FSMDEF_DBG_TMR_START_FAILED)},         // FSMDEF_DBG_TMR_START_FAILED
+    {use_str(FSMDEF_DBG_TMR_CANCEL_FAILED)},        // FSMDEF_DBG_TMR_CANCEL_FAILED
+    {use_str(FSMXFR_DBG_XFR_INITIATED)},            // FSMXFR_DBG_XFR_INITIATED
+    {use_str(FSMXFR_DBG_PTR)},                      // FSMXFR_DBG_PTR
+    {use_str(FSMCNF_DBG_CNF_INITIATED)},            // FSMCNF_DBG_CNF_INITIATED
+    {use_str(FSMCNF_DBG_PTR)},                      // FSMCNF_DBG_PTR
+    {use_str(FSMB2BCNF_DBG_CNF_INITIATED)},         // FSMB2BCNF_DBG_CNF_INITIATED
+    {use_str(FSMB2BCNF_DBG_PTR)},                   // FSMB2BCNF_DBG_PTR
+    {use_str(FSMSHR_DBG_BARGE_INITIATED)},          // FSMSHR_DBG_BARGE_INITIATED
+    {use_str(LSM_DBG_ENTRY)},                       // LSM_DBG_ENTRY
+    {use_str(LSM_DBG_INT1)},                        // LSM_DBG_INT1
+    {use_str(LSM_DBG_CC_ERROR)},                    // LSM_DBG_CC_ERROR
+    {use_str(VCM_DEBUG_ENTRY)},                     // VCM_DEBUG_ENTRY
+    {use_str(SM_PROCESS_EVENT_ERROR)},              // SM_PROCESS_EVENT_ERROR
+    {use_str(REG_SM_PROCESS_EVENT_ERROR)},          // REG_SM_PROCESS_EVENT_ERROR
+    {0},
+};
+
+
+
+//************************************************************************
+
+/*
+ * Phrase string table subject to localization
+ */
+tnp_phrase_index_str_table_entry tnp_phrase_index_str_table [] = {
+    {"\0"},                             // LOCALE_START
+    {"\x80\x13"},                       // IDLE_PROMPT sccp 119
+    {"Anonymous"},                      // ANONYMOUS - used for messaging; keep English
+    {"\x80\x1e"},                       // CALL_PROCEEDING_IN
+    {"\x80\x1e"},                       // CALL_PROCEEDING_OUT
+    {"\x80\x16"},                       // CALL_ALERTING sccp 122
+    {"\x80\x1b"},                       // CALL_ALERTING_SECONDARY sccp 127
+    {"\x80\x16"},                       // CALL_ALERTING_LOCAL
+    {"\x80\x18"},                       // CALL_CONNECTED sccp 124
+    {"\x80\x03"},                       // CALL_INITIATE_HOLD sccp 103 or phone 786?
+    {"\x80\x20"},                       // PROMPT_DIAL sccp 132
+    {"\x80\x19"},                       // LINE_BUSY sccp 125
+    {"\x80\x1b"},                       // CALL_WAITING
+    {"\x80\x52"},                       // TRANSFER_FAILED sccp 182
+    {"\x80\x26"},                       // Cannot complete the b2b conf
+    {"\x80\x34"},                       // UI_CONFERENCE
+    {"\x80\x38"},                       // UI_UNKNOWN
+    {"\x80\x1f"},                       // REMOTE_IN_USE
+    {"\x80\x55"},                       // NUM_NOT_CONFIGURED
+    {"\x80\x17"},                       // UI_FROM
+    {"\x80\x29"},                       // INVALID_CONF_PARTICIPANT
+    {"\x80\x36"},                       // UI_PRIVATE
+    {"\0"}                              // LOCALE_END
+};
diff --git a/libs/sipcc/core/common/text_strings.h b/libs/sipcc/core/common/text_strings.h
new file mode 100755 (executable)
index 0000000..0502a38
--- /dev/null
@@ -0,0 +1,302 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TEXT_STRINGS_H
+#define TEXT_STRINGS_H
+
+#include "cpr_types.h"
+#include "phone_types.h"
+#include "string_lib.h"
+
+
+#define DEF_NOTIFY_PRI    20
+#define HR_NOTIFY_PRI     1
+
+/*
+ * Define hard coded Anonymous string to be used in From header
+ * when call id blocking is enabled. This is also used to
+ * compare to when anonymous call block is enabled. We can
+ * can not use the localized version of Anonymous for these
+ * actions.
+ */
+#define SIP_HEADER_ANONYMOUS_STR        "Anonymous"
+
+/*
+ * Define special strings that are not localized to be used for
+ * comparing special display names whether the names are special
+ * and not to display the number associated with the name.
+ */
+#define CONFERENCE_STR                  "conference"
+#define CONFERENCE_STR_LEN              (sizeof(CONFERENCE_STR)-1)
+#define CONFERENCE_LOCALE_CODE                  964
+/*
+ * Constants for dictionary index
+ */
+
+// Hardcoding phrases that need to be dropped on floor for RT to 1
+
+#define STR_INDEX_GRP_CALL_PICKUP       47
+#define STR_INDEX_LST_CALL_PICKUP       49
+#define STR_INDEX_FEAT_UNAVAIL          148
+#define STR_INDEX_CNFR_FAIL_NOCODEC     1054
+#define STR_INDEX_REORDER               1055
+#define STR_INDEX_ANONYMOUS_SPACE       1056
+#define STR_INDEX_NO_FREE_LINES         1057
+#define STR_INDEX_REGISTRATION_REJECTED 1058
+#define STR_INDEX_REGISTERING           1
+#define STR_INDEX_WHISPER               1
+#define STR_INDEX_PROXY_UNAVAIL         1
+#define STR_INDEX_CALL_REDIRECTED       1
+#define STR_INDEX_TRANSFERRING          1
+#define STR_INDEX_SESSION_PROGRESS      1
+#define STR_INDEX_CALLING               1
+#define STR_INDEX_USE_LINE_OR_JOIN_TO_COMPLETE  1
+#define STR_INDEX_CONF_LOCAL_MIXED      1
+#define STR_INDEX_NO_CALL_FOR_PICKUP    195
+
+#define STR_INDEX_CALL_FORWARD          105
+#define STR_INDEX_CALL_PICKUP           117
+#define STR_INDEX_NO_LINE_FOR_PICKUP    198
+
+
+#define STR_INDEX_ERROR_PASS_LIMIT  149
+#define STR_INDEX_TRANS_COMPLETE    918
+#define STR_INDEX_END_CALL          877
+#define STR_INDEX_NO_BAND_WIDTH     1158
+
+#define STR_INDEX_RESP_TIMEOUT      1160
+
+#define CALL_BUBBLE_STR_MAX_LEN     32
+
+#define STATUS_LINE_MAX_LEN         128
+
+/*
+ * Escape codes definitions
+ */
+#define OLD_CUCM_DICTIONARY_ESCAPE_TAG                  '\x80'
+#define NEW_CUCM_DICTIONARY_ESCAPE_TAG                  '\x1E'
+#define CALL_CONTROL_PHRASE_OFFSET                      '\x64'  // offset 100
+#define MAX_LOCALE_PHRASE_LEN                            256
+#define MAX_LOCALE_STRING_LEN                            1024
+
+/*
+ * Escaped index string codes for Call Mgr dictionary phrases
+ */
+#define INDEX_STR_KEY_NOT_ACTIVE    (char *) "\x80\x2D"
+#define INDEX_STR_BARGE             (char *) "\x80\x43"
+#define INDEX_STR_PRIVATE           (char *) "\x80\x36"
+#define INDEX_STR_HOLD_REVERSION    (char *) "\x1E\x23"
+#define INDEX_STR_MONITORING        (char *) "\x1E\x27"
+#define INDEX_STR_COACHING          (char *) "\x1E\x46"
+
+
+/*
+ * Index value for phrase strings subject to localization
+ */
+enum PHRASE_STRINGS_ENUM {
+    LOCALE_START,
+    IDLE_PROMPT,
+    ANONYMOUS,                  /* fsmdef.o */
+    CALL_PROCEEDING_IN,
+    CALL_PROCEEDING_OUT,
+    CALL_ALERTING,
+    CALL_ALERTING_SECONDARY,
+    CALL_ALERTING_LOCAL,
+    CALL_CONNECTED,
+    CALL_INITIATE_HOLD,
+    PROMPT_DIAL,
+    LINE_BUSY,
+    CALL_WAITING,
+    TRANSFER_FAILED,
+    CONF_CANNOT_COMPLETE,
+    UI_CONFERENCE,
+    UI_UNKNOWN,
+    REMOTE_IN_USE,
+    NUM_NOT_CONFIGURED,
+    UI_FROM,
+    INVALID_CONF_PARTICIPANT,
+    UI_PRIVATE,
+    LOCALE_END
+};
+
+/*
+ * Index value for Debug strings NOT subject to localization
+ */
+enum DEBUG_STRINGS_ENUM {
+    DEBUG_START,
+    DEBUG_SEPARATOR_BAR,
+    DEBUG_CONSOLE_PASSWORD,
+    DEBUG_CONSOLE_KEYWORD_CONSOLE_STALL,
+    DEBUG_CONSOLE_KEYWORD_MEMORYMAP,
+    DEBUG_CONSOLE_KEYWORD_MALLOCTABLE,
+    DEBUG_CONSOLE_KEYWORD_MEMORYDUMP,
+    DEBUG_CONSOLE_KEYWORD_DNS,
+    DEBUG_CONSOLE_KEYWORD_DSPSTATE,
+    DEBUG_CONSOLE_USAGE_MEMORYDUMP,
+    DEBUG_CONSOLE_BREAK,
+
+    DEBUG_FUNCTION_ENTRY,
+    DEBUG_FUNCTION_ENTRY2,
+    DEBUG_SIP_ENTRY,
+    DEBUG_SIP_URL_ERROR,
+    DEBUG_LINE_NUMBER_INVALID,
+    DEBUG_SIP_SPI_SEND_ERROR,
+    DEBUG_SIP_SDP_CREATE_BUF_ERROR,
+    DEBUG_SIP_PARSE_SDP_ERROR,
+    DEBUG_SIP_FEATURE_UNSUPPORTED,
+    DEBUG_SIP_DEST_SDP,
+    DEBUG_SIP_MSG_SENDING_REQUEST,
+    DEBUG_SIP_MSG_SENDING_RESPONSE,
+    DEBUG_SIP_MSG_RECV,
+    DEBUG_SIP_STATE_UNCHANGED,
+    DEBUG_SIP_FUNCTIONCALL_FAILED,
+    DEBUG_SIP_BUILDFLAG_ERROR,
+    DEBUG_GENERAL_FUNCTIONCALL_FAILED,
+    DEBUG_GENERAL_SYSTEMCALL_FAILED,
+    DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT,
+    DEBUG_FUNCTIONNAME_SIPPMH_PARSE_FROM,
+    DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO,
+    DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE,
+
+    DEBUG_SNTP_LI_ERROR,
+    DEBUG_SNTP_MODE_ERROR,
+    DEBUG_SNTP_STRATUM_ERROR,
+    DEBUG_SNTP_TIMESTAMP_ERROR,
+    DEBUG_SNTP_TIMESTAMP1,
+    DEBUG_SNTP_TIMESTAMP2,
+    DEBUG_SNTP_TIME_UPDATE,
+    DEBUG_SNTP_TS_HEADER,
+    DEBUG_SNTP_TS_PRINT,
+    DEBUG_SNTP_SOCKET_REOPEN,
+    DEBUG_SNTP_DISABLED,
+    DEBUG_SNTP_REQUEST,
+    DEBUG_SNTP_RESPONSE,
+    DEBUG_SNTP_RETRANSMIT,
+    DEBUG_SNTP_UNICAST_MODE,
+    DEBUG_SNTP_MULTICAST_MODE,
+    DEBUG_SNTP_ANYCAST_MODE,
+    DEBUG_SNTP_VALIDATION,
+    DEBUG_SNTP_VALIDATION_PACKET,
+    DEBUG_SNTP_WRONG_SERVER,
+    DEBUG_SNTP_NO_REQUEST,
+    DEBUG_SNTP_ANYCAST_RESET,
+    DEBUG_SOCKET_UDP_RTP,
+    DEBUG_MAC_PRINT,
+    DEBUG_IP_PRINT,
+    DEBUG_SYSBUF_UNAVAILABLE,
+    DEBUG_MSG_BUFFER_TOO_BIG,
+    DEBUG_UNKNOWN_TIMER_BLOCK,
+    DEBUG_CREDENTIALS_BAG_CORRUPTED,
+    DEBUG_INPUT_EMPTY,
+    DEBUG_INPUT_NULL,
+    DEBUG_STRING_DUP_FAILED,
+    DEBUG_PARSER_STRING_TOO_LARGE,
+    DEBUG_PARSER_NULL_KEY_TABLE,
+    DEBUG_PARSER_UNKNOWN_KEY,
+    DEBUG_PARSER_UNKNOWN_KEY_ENUM,
+    DEBUG_PARSER_INVALID_START_VAR,
+    DEBUG_PARSER_INVALID_VAR_CHAR,
+    DEBUG_PARSER_MISSING_COLON,
+    DEBUG_PARSER_NO_VALUE,
+    DEBUG_PARSER_EARLY_EOL,
+    DEBUG_PARSER_INVALID_VAR_NAME,
+    DEBUG_PARSER_INVALID_VAR_VALUE,
+    DEBUG_PARSER_UNKNOWN_VAR,
+    DEBUG_PARSER_NAME_VALUE,
+    DEBUG_PARSER_UNKNOWN_NAME_VALUE,
+    DEBUG_PARSER_UNKNOWN_ERROR,
+    DEBUG_PARSER_NUM_ERRORS,
+    DEBUG_PARSER_SET_DEFAULT,
+    DEBUG_SDP_ERROR_BODY_FIELD,
+    DEBUG_UDP_OPEN_FAIL,
+    DEBUG_UDP_PAYLOAD_TOO_LARGE,
+    DEBUG_TCP_PAYLOAD_TOO_LARGE = DEBUG_UDP_PAYLOAD_TOO_LARGE,
+    DEBUG_RTP_TRANSPORT,
+    DEBUG_RTP_INVALID_VOIP_TYPE,
+    DEBUG_RTP_INVALID_RTP_TYPE,
+    DEBUG_MEMORY_ALLOC,
+    DEBUG_MEMORY_FREE,
+    DEBUG_MEMORY_MALLOC_ERROR,
+    DEBUG_MEMORY_REALLOC_ERROR,
+    DEBUG_MEMORY_OUT_OF_MEM,
+    DEBUG_MEMORY_ENTRY,
+    DEBUG_MEMORY_SUMMARY,
+    DEBUG_MEMORY_ADDRESS_HEADER,
+    DEBUG_MEMORY_DUMP,
+    DEBUG_DNS_GETHOSTBYNAME,
+    DEBUG_PMH_INCORRECT_SYNTAX,
+    DEBUG_PMH_INVALID_FIELD_VALUE,
+    DEBUG_PMH_INVALID_SCHEME,
+    DEBUG_PMH_UNKNOWN_SCHEME,
+    DEBUG_PMH_NOT_ENOUGH_PARAMETERS,
+    DEBUG_REG_DISABLED,
+    DEBUG_REG_PROXY_EXPIRES,
+    DEBUG_REG_SIP_DATE,
+    DEBUG_REG_SIP_RESP_CODE,
+    DEBUG_REG_SIP_RESP_FAILURE,
+    DEBUG_REG_INVALID_LINE,
+    CC_NO_MSG_BUFFER,
+    CC_SEND_FAILURE,
+    GSM_UNDEFINED,
+    GSM_DBG_PTR,
+    GSM_FUNC_ENTER,
+    GSM_DBG1,
+    FSM_DBG_SM_DEFAULT_EVENT,
+    FSM_DBG_SM_FTR_ENTRY,
+    FSM_DBG_FAC_ERR,
+    FSM_DBG_FAC_FOUND,
+    FSM_DBG_IGNORE_FTR,
+    FSM_DBG_IGNORE_SRC,
+    FSM_DBG_CHANGE_STATE,
+    FSM_DBG_SDP_BUILD_ERR,
+    FSMDEF_DBG_PTR,
+    FSMDEF_DBG1,
+    FSMDEF_DBG2,
+    FSMDEF_DBG_SDP,
+    FSMDEF_DBG_CLR_SPOOF_APPLD,
+    FSMDEF_DBG_CLR_SPOOF_RQSTD,
+    FSMDEF_DBG_INVALID_DCB,
+    FSMDEF_DBG_FTR_REQ_ACT,
+    FSMDEF_DBG_TMR_CREATE_FAILED,
+    FSMDEF_DBG_TMR_START_FAILED,
+    FSMDEF_DBG_TMR_CANCEL_FAILED,
+    FSMXFR_DBG_XFR_INITIATED,
+    FSMXFR_DBG_PTR,
+    FSMCNF_DBG_CNF_INITIATED,
+    FSMCNF_DBG_PTR,
+    FSMB2BCNF_DBG_CNF_INITIATED,
+    FSMB2BCNF_DBG_PTR,
+    FSMSHR_DBG_BARGE_INITIATED,
+    LSM_DBG_ENTRY,
+    LSM_DBG_INT1,
+    LSM_DBG_CC_ERROR,
+    VCM_DEBUG_ENTRY,
+    SM_PROCESS_EVENT_ERROR,
+    REG_SM_PROCESS_EVENT_ERROR,
+    DEBUG_END
+};
+
+
+typedef struct {
+    const char *text;
+} debug_string_table_entry;
+
+extern debug_string_table_entry debug_string_table[];
+
+#define get_debug_string(index)               ((char*)debug_string_table[(index)].text)
+
+
+
+typedef struct {
+    const char *index_str;
+} tnp_phrase_index_str_table_entry;
+
+
+extern tnp_phrase_index_str_table_entry tnp_phrase_index_str_table[];
+
+#define platform_get_phrase_index_str(index)  ((char*)tnp_phrase_index_str_table[(index)].index_str)
+#define get_info_string(index) "NotImplemented"
+
+
+#endif
diff --git a/libs/sipcc/core/common/ui.c b/libs/sipcc/core/common/ui.c
new file mode 100755 (executable)
index 0000000..750a435
--- /dev/null
@@ -0,0 +1,1666 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/** @file tnp_ui.c
+ * API provided by Platform to the Call Control for User Interface activities
+ */
+
+#include "cpr.h"
+#include "cpr_in.h"
+#include "phone.h"
+#include "time2.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "dialplan.h"
+#include "ccapi.h"
+#include "cfgfile_utils.h"
+#include "prot_configmgr.h"
+#include "dns_utils.h"
+#include "uiapi.h"
+#include "lsm.h"
+#include "fsm.h"
+#include "CCProvider.h"
+#include "ccSession.h"
+#include "platform_api.h"
+#include "vcm.h"
+#include "ccapp_task.h"
+
+/*
+ * Note: Do not include "msprovider.h" here unless the dependencies on
+ *       /vob/ip_phone/ip_g4/infra have been removed, as those dependencies
+ *       break CSF builds.
+ */
+
+/*--------------------------------------------------------------------------
+ * Local definitions
+ *--------------------------------------------------------------------------
+ */
+#define CCAPP_F_PREFIX "CCAPP : %s : " // requires 1 arg: __FUNCTION__
+// Why don't we modify strlib_malloc to handle NULL?
+#define STRLIB_CREATE(str)  (str)?strlib_malloc((str), strlen((str))):strlib_empty()
+// Define default display timeout and priority
+#define DEFAULT_DISPLAY_NOTIFY_TIMEOUT   0
+#define DEFAULT_DISPLAY_NOTIFY_PRIORITY  0
+
+
+/*--------------------------------------------------------------------------
+ * Global data
+ *--------------------------------------------------------------------------
+ */
+
+/*--------------------------------------------------------------------------
+ * External data references
+ * -------------------------------------------------------------------------
+ */
+
+
+/*--------------------------------------------------------------------------
+ * External function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+extern int Basic_Get_Line_By_Name(char *name);
+extern char *Basic_is_phone_forwarded(line_t line);
+extern void dp_int_dial_immediate(line_t line, callid_t call_id,
+                                  boolean collect_more, char *digit_str,
+                                  char *g_call_id,
+                                  monitor_mode_t monitor_mode);
+extern void dp_int_init_dialing_data(line_t line, callid_t call_id);
+extern callid_t dp_get_dialing_call_id(void);
+extern void dp_int_update_key_string(line_t line, callid_t call_id,
+                                     char *digits);
+extern void platform_set_time(int32_t gmt_time);
+extern session_id_t createSessionId(line_t line, callid_t call);
+// XXX need to either make msprovider.h platform-independent and include that file instead,
+//     or move ui_keypad_button out of this file (ccmedia.c, perhaps?)
+
+/*--------------------------------------------------------------------------
+ * Local scope function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+/**
+ *  wrapper to post Call State change to CCAPP
+ *
+ *  @param  event   - new state change event
+ *  @param  nLine   - line identifier for which call state change
+ *  @param  nCallID - call identifier
+ *
+ *
+ *  @return   none (Side effect: Call bubble changes accordingly)
+ *
+ */
+void
+ui_call_state (call_events event, line_t nLine, callid_t nCallID, cc_causes_t cause)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"event=%d \n", DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__),
+              event);
+
+    if (nCallID == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(nLine, nCallID);
+    msg.eventID = CALL_STATE;
+    msg.update.ccSessionUpd.data.state_data.state = event;
+    msg.update.ccSessionUpd.data.state_data.line_id = nLine;
+    msg.update.ccSessionUpd.data.state_data.cause = cause;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_STATE(%d) msg \n", __FUNCTION__, event);
+    }
+}
+
+/**
+ *  wrapper to post presentation for new call to CCAPP
+ *  Function: ui_new_call
+ *
+ *  @param nLine line identifier
+ *  @param nCallID - call identifier
+ *  @param call_attr - call attribute(normal, transfer consult, conf consult)
+ *  @param call_instance_id - call instance identifier
+ *
+ *  @return none
+ */
+void
+ui_new_call (call_events event, line_t nLine, callid_t nCallID,
+             int call_attr, uint16_t call_instance_id, boolean dialed_digits)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d attr=%d call_instance=%d, dialed_digits=%s\n",
+              DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_attr, call_instance_id, (dialed_digits)? "true" : "false");
+
+    if (nCallID == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID= createSessionId(nLine, nCallID);
+
+    msg.eventID = CALL_NEWCALL;
+    msg.update.ccSessionUpd.data.state_data.state = event;
+    msg.update.ccSessionUpd.data.state_data.attr = call_attr;
+    msg.update.ccSessionUpd.data.state_data.inst = call_instance_id;
+    msg.update.ccSessionUpd.data.state_data.line_id = nLine;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_STATE(%d) msg \n", __FUNCTION__, event);
+    }
+
+    return;
+}
+
+/**
+ * set or change the attribute on the call and reflect the changes
+ * to call plane (such as change in softkeys or other visual aspects).
+ * used to change the call attribute back to normal from consult
+ *
+ * Posts the update to CCAPP
+ *
+ * @param line_id - line identifier
+ * @param call_id - call identifier
+ * @param attr    - call attribute has to be one of call_attr_t
+ *
+ * @return none
+ */
+void
+ui_set_call_attr (line_t line_id, callid_t call_id, call_attr_t attr)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"attr=%d\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line_id, call_id, __FUNCTION__), attr);
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(line_id, call_id);
+    msg.eventID = CALL_ATTR;
+    msg.update.ccSessionUpd.data.state_data.attr = attr;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_ATTR(%d) msg \n", __FUNCTION__, attr);
+    }
+}
+
+/**
+ *  Wrapper for ui_update_callref
+ *
+ *  @param  line - line for the session
+ *  @param  call_id - callid for the session
+ *  @param  callref -  callref for the session
+ *
+ *  @return none
+ */
+void
+ui_update_callref (line_t line, callid_t call_id, unsigned int callref)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"callref = %d\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__), callref);
+
+    if ( callref == 0 ) return;
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = CALL_CALLREF;
+    msg.update.ccSessionUpd.data.callref = callref;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_REF() msg \n", __FUNCTION__);
+    }
+
+    return;
+}
+
+
+/**
+ *  Wrapper for ui_update_gcid - global_call_id
+ *
+ *  @param  line - line for the session
+ *  @param  call_id - callid for the session
+ *  @param  gcid -  Global CallId for the session
+ *
+ *  @return none
+ */
+void
+ui_update_gcid (line_t line, callid_t call_id, char *gcid)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"gcid = %s\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__), gcid);
+
+    if ( *gcid == '\0' ) return;
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = CALL_GCID;
+    sstrncpy(msg.update.ccSessionUpd.data.gcid, gcid, CC_MAX_GCID);
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_GCID() msg \n", __FUNCTION__);
+    }
+
+    return;
+}
+
+/**
+ *  Wrapper for ui_update_video_avail
+ *    indicates video stream is avail for this session to UI
+ *  @param  line - line for the session
+ *  @param  call_id - callid for the session
+ *
+ *  @return none
+ */
+void
+ui_update_video_avail (line_t line, callid_t call_id, int avail)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__));
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = VIDEO_AVAIL;
+    msg.update.ccSessionUpd.data.action = avail;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send VIDEO_AVAIL() msg \n", __FUNCTION__);
+    }
+
+    return;
+}
+
+void ui_update_media_interface_change(line_t line, callid_t call_id, group_call_event_t event) {
+    session_update_t msg;
+
+    if (event != MEDIA_INTERFACE_UPDATE_BEGIN &&
+        event != MEDIA_INTERFACE_UPDATE_SUCCESSFUL &&
+        event != MEDIA_INTERFACE_UPDATE_FAIL) {
+        // un-related event. ignore.
+        return;
+    }
+
+    if (event != MEDIA_INTERFACE_UPDATE_BEGIN) {
+        /* we are sending final result */
+        g_dock_undock_event = MEDIA_INTERFACE_UPDATE_NOT_REQUIRED;
+    }
+
+    DEF_DEBUG(DEB_L_C_F_PREFIX "event=%s", DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__),
+        event == MEDIA_INTERFACE_UPDATE_BEGIN ? "MEDIA_INTERFACE_UPDATE_BEGIN" :
+        event == MEDIA_INTERFACE_UPDATE_SUCCESSFUL ? "MEDIA_INTERFACE_UPDATE_SUCCESSFUL" :
+        event == MEDIA_INTERFACE_UPDATE_FAIL ? "MEDIA_INTERFACE_UPDATE_FAIL" : "unknown");
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = event;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS )     {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send media update () msg \n", __FUNCTION__);
+    }
+}
+
+void
+ui_call_stop_ringer (line_t line, callid_t call_id)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__));
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = RINGER_STATE;
+    msg.update.ccSessionUpd.data.ringer.start = FALSE;
+    msg.update.ccSessionUpd.data.ringer.mode = VCM_RING_OFF;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send RINGER_STATE() msg \n", __FUNCTION__);
+    }
+
+    return;
+}
+
+void
+ui_call_start_ringer (vcm_ring_mode_t ringMode, short once, line_t line, callid_t call_id)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__));
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = RINGER_STATE;
+    msg.update.ccSessionUpd.data.ringer.start = TRUE;
+    msg.update.ccSessionUpd.data.ringer.mode = ringMode;
+    msg.update.ccSessionUpd.data.ringer.once = (cc_boolean) once;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send RINGER_STATE() msg \n", __FUNCTION__);
+    }
+
+    return;
+}
+
+/**
+ *  Wrapper for ui_update_video_avail
+ *    indicates video stream is avail for this session to UI
+ *  @param  line - line for the session
+ *  @param  call_id - callid for the session
+ *
+ *  @return none
+ */
+void
+ui_update_video_offered (line_t line, callid_t call_id, int avail)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__));
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = VIDEO_OFFERED;
+    msg.update.ccSessionUpd.data.action = avail;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send VIDEO_OFFERED() msg \n", __FUNCTION__);
+    }
+
+    return;
+}
+
+
+/**
+ *  Wrapper for ui call info post to CCAPP
+ *  call bubble with appropriate party information
+ *
+ *  @param  pCallingPartyNameStr/NumberStr - Calling party information
+ *  @param  displayCallingNumber
+ *  @param  pCalledPartyNameStr/NumberStr - called party information
+ *  @param  displayCalledNumber
+ *  @param  pOrigCalledNameStr/NumberStr
+ *  @param  pLastRedirectingNameStr/NumberStr
+ *  @param  call_type
+ *  @param  line    - line identifier
+ *  @param  call_id - call identifier
+ *  @param  call_instance_id - call instance displayed in call bubble
+ *  @param  cc_security
+ *
+ *  @return none
+ */
+void
+ui_call_info (string_t pCallingPartyNameStr,
+              string_t pCallingPartyNumberStr,
+              string_t pAltCallingPartyNumberStr,
+              boolean displayCallingNumber,
+              string_t pCalledPartyNameStr,
+              string_t pCalledPartyNumberStr,
+              boolean displayCalledNumber,
+              string_t pOrigCalledNameStr,
+              string_t pOrigCalledNumberStr,
+              string_t pLastRedirectingNameStr,
+              string_t pLastRedirectingNumberStr,
+              calltype_t call_type,
+              line_t line,
+              callid_t call_id,
+              uint16_t call_instance_id,
+              cc_security_e call_security,
+             cc_policy_e call_policy)
+{
+    session_update_t msg;
+    const char *uiCalledName;
+    const char *uiCalledNumber;
+    const char *uiCallingName;
+    const char *uiCallingNumber;
+    int inbound;
+    char       lineName[MAX_LINE_NAME_SIZE];
+    char       lineNumber[MAX_LINE_NAME_SIZE];
+
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"call instance=%d callednum=%s calledname=%s clngnum=%s clngname = %s\n",
+        DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__), call_instance_id, pCalledPartyNumberStr,
+        pCalledPartyNameStr, pCallingPartyNumberStr, pCallingPartyNameStr);
+
+    TNP_DEBUG(DEB_F_PREFIX"calltype=%d displayClng=%d displayCld=%d\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), call_type,
+        displayCallingNumber, displayCalledNumber);
+
+
+    inbound  = (call_type == FSMDEF_CALL_TYPE_INCOMING) || (call_type == FSMDEF_CALL_TYPE_FORWARD);
+    uiCalledNumber = pCalledPartyNumberStr;
+    uiCalledName = pCalledPartyNameStr;
+    uiCallingNumber = pCallingPartyNumberStr;
+    uiCallingName = pCallingPartyNameStr;
+
+    config_get_line_string(CFGID_LINE_DISPLAYNAME, lineName, line, sizeof(lineName));
+    config_get_line_string(CFGID_LINE_NAME, lineNumber, line, sizeof(lineNumber));
+
+    if (inbound) {
+        uiCalledNumber = lineNumber;
+        uiCalledName = lineName;
+    } else {
+        uiCallingNumber = lineNumber;
+        uiCallingName = lineName;
+    }
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = CALL_INFORMATION;
+    msg.update.ccSessionUpd.data.call_info.clgName = uiCallingName?strlib_malloc(uiCallingName, strlen(uiCallingName)):strlib_empty();
+
+    if ( uiCallingNumber== NULL || (inbound && !displayCallingNumber)) {
+        msg.update.ccSessionUpd.data.call_info.clgNumber = strlib_empty();
+    } else {
+        msg.update.ccSessionUpd.data.call_info.clgNumber = strlib_malloc(uiCallingNumber, strlen(uiCallingNumber));
+    }
+
+    if ( pAltCallingPartyNumberStr == NULL ||  (inbound && !displayCallingNumber)) {
+        msg.update.ccSessionUpd.data.call_info.altClgNumber = strlib_empty();
+    } else {
+        msg.update.ccSessionUpd.data.call_info.altClgNumber = strlib_malloc(pAltCallingPartyNumberStr,strlen(pAltCallingPartyNumberStr));
+    }
+
+    msg.update.ccSessionUpd.data.call_info.cldName = uiCalledName?strlib_malloc(uiCalledName,strlen(uiCalledName)):strlib_empty();
+
+    if (uiCalledNumber == NULL || (!inbound && !displayCalledNumber)) {
+        msg.update.ccSessionUpd.data.call_info.cldNumber = strlib_empty();
+    } else {
+        msg.update.ccSessionUpd.data.call_info.cldNumber = strlib_malloc(uiCalledNumber,strlen(uiCalledNumber));
+    }
+    msg.update.ccSessionUpd.data.call_info.origCalledName = pOrigCalledNameStr?strlib_malloc(pOrigCalledNameStr, strlen(pOrigCalledNameStr)):strlib_empty();
+    msg.update.ccSessionUpd.data.call_info.origCalledNumber = pOrigCalledNumberStr?strlib_malloc(pOrigCalledNumberStr,strlen(pOrigCalledNumberStr)):strlib_empty();
+    msg.update.ccSessionUpd.data.call_info.lastRedirectingName = pLastRedirectingNameStr?strlib_malloc(pLastRedirectingNameStr,strlen(pLastRedirectingNameStr)):strlib_empty();
+    msg.update.ccSessionUpd.data.call_info.lastRedirectingNumber = pLastRedirectingNumberStr?strlib_malloc(pLastRedirectingNumberStr, strlen(pLastRedirectingNumberStr)):strlib_empty();
+    msg.update.ccSessionUpd.data.call_info.call_type = call_type;
+    msg.update.ccSessionUpd.data.call_info.instance_id = call_instance_id;
+    msg.update.ccSessionUpd.data.call_info.security = call_security;
+    msg.update.ccSessionUpd.data.call_info.policy = call_policy;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_INFO() msg \n", __FUNCTION__);
+    }
+
+    return;
+}
+
+/**
+ *  Wrapper for ui cc capability post to CCAPP
+ *
+ *  @param line - line identifier
+ *  @param callID - call identifier
+ *  @param capability - cc capability
+ *
+ *  @return none
+ */
+void
+ui_cc_capability (line_t line, callid_t call_id, string_t recv_info_list)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"recv_info_list:%s\n",
+        DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__),
+        recv_info_list);
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = CALL_RECV_INFO_LIST;
+    msg.update.ccSessionUpd.data.recv_info_list = strlib_copy(recv_info_list);
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_RECV_INFO_LIST msg \n", __FUNCTION__);
+    }
+}
+
+/**
+ *  Wrapper for ui info received post to CCAPP
+ *
+ *  @param line - line identifier
+ *  @param callID - call identifier
+ *  @param info_package - the Info-Package header of the Info Package
+ *  @param content_type - the Content-Type header of the Info Package
+ *  @param message_body - the message body of the Info Package
+ *
+ *  @return none
+ */
+void
+ui_info_received (line_t line, callid_t call_id, const char *info_package,
+                  const char *content_type, const char *message_body)
+{
+    session_rcvd_info_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"info_package:%s content_type:%s message_body:%s\n",
+        DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__),
+        info_package, content_type, message_body);
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.packageID = INFO_PKG_ID_GENERIC_RAW;
+    msg.info.generic_raw.info_package = info_package?strlib_malloc(info_package, strlen(info_package)):strlib_empty();
+    msg.info.generic_raw.content_type = content_type?strlib_malloc(content_type, strlen(content_type)):strlib_empty();
+    msg.info.generic_raw.message_body = message_body?strlib_malloc(message_body, strlen(message_body)):strlib_empty();
+
+    if ( ccappTaskPostMsg(CCAPP_RCVD_INFO, &msg, sizeof(session_rcvd_info_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_INFO_RECEIVED msg \n", __FUNCTION__);
+    }
+}
+
+
+/**
+ *  An internal wrapper for ui call status post to CCAPP
+ *
+ *  @param pString - status string
+ *  @param line - line identifier
+ *  @param callID - call identifier
+ *  @param timeout - timeout for the status line
+ *
+ *  @return none
+ */
+static void
+ui_set_call_status_display (string_t status, line_t line, callid_t callID, int timeout, char priority)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"the stat string =%s, timeout= %d, priority=%d\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line, callID, __FUNCTION__),
+              status,
+              timeout,
+              priority);
+
+    if (callID == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(line, callID);
+    msg.eventID = CALL_STATUS;
+    msg.update.ccSessionUpd.data.status.timeout = timeout;
+    msg.update.ccSessionUpd.data.status.priority = priority;
+    if ( status ) {
+      msg.update.ccSessionUpd.data.status.status = strlib_malloc(status, strlen(status));
+    } else {
+      msg.update.ccSessionUpd.data.status.status = strlib_empty();
+    }
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_STATUS(%s) msg \n", __FUNCTION__, status);
+    }
+}
+
+
+/**
+ *  Wrapper for ui call status post to CCAPP
+ *
+ *  @param pString - status string
+ *  @param line - line identifier
+ *  @param callID - call identifier
+ *  @param timeout - timeout for the status line
+ *
+ *  @return none
+ */
+void
+ui_set_call_status (string_t status, line_t line, callid_t callID)
+{
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"the stat string =%s\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line, callID, __FUNCTION__),
+              status);
+
+    if (callID == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    ui_set_call_status_display(status, line, callID, DEFAULT_DISPLAY_NOTIFY_TIMEOUT, DEFAULT_DISPLAY_NOTIFY_PRIORITY);
+}
+
+
+/**
+ *  Wrapper for sending notification CCAPP
+ *
+ *  @param  promptString  - notification string
+ *  @param  timeout       - timeout of this notify
+ *  @param  notifyProgress- the type of notification
+ *                          TRUE - Progress, FALSE- Normal
+ *  @param  priority      - priority of this notification
+ *                         Pri 1..5 Low .. High
+ *
+ *  @return none
+ */
+void
+ui_set_notification (line_t line, callid_t call_id, char *promptString, int timeout,
+                     boolean notifyProgress, char priority)
+{
+    feature_update_t msg;
+
+    TNP_DEBUG(DEB_F_PREFIX"line=%d callid=%d str=%s tout=%d notifyProgress=%d pri=%d\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__),
+              line, call_id, promptString, timeout, notifyProgress, priority);
+
+    if (line > 0 && call_id > 0) {
+        ui_set_call_status_display(promptString, line, call_id, timeout, priority);
+        return;
+    }
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_NOTIFICATION;
+    msg.update.ccFeatUpd.data.notification.timeout = timeout;
+    msg.update.ccFeatUpd.data.notification.notifyProgress = notifyProgress;
+    msg.update.ccFeatUpd.data.notification.priority = priority;
+    if ( promptString != NULL ) {
+       msg.update.ccFeatUpd.data.notification.prompt = strlib_malloc(promptString, strlen(promptString));
+    } else {
+        msg.update.ccFeatUpd.data.notification.prompt = strlib_empty();
+    }
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send DEVICE_NOTIFICATION(%s) msg \n", __FUNCTION__, promptString);
+    }
+}
+
+/**
+ *  CCAPPPost to clear the previous notify of given priority
+ *
+ *  @param priority - notification of the given pri
+ *
+ *  @return   none
+ */
+/* TBD: the API should have priority param */
+void
+ui_clear_notification ()
+{
+    TNP_DEBUG(DEB_F_PREFIX"called..\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__));
+
+    // A promptString of NULL shall act as a clear
+    ui_set_notification(CC_NO_LINE, CC_NO_CALL_ID, NULL, 0, FALSE, 1);
+}
+
+/**
+ *
+ *  CCAPP Post to set the MWI lamp indication of the device
+ *
+ *  @param  1 - on or else off
+ *
+ *  @return none
+ */
+void
+ui_change_mwi_lamp (int status)
+{
+    feature_update_t msg;
+
+
+    TNP_DEBUG(DEB_F_PREFIX"status=%d \n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), status);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_FEATURE_MWILAMP;
+    msg.update.ccFeatUpd.data.state_data.state = status;
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send DEVICE_FEATURE_MWILAMP(%d) msg \n", __FUNCTION__, status);
+    }
+}
+
+/**
+ *
+ *  CCAPP post to set the MWI lamp indication for given line
+ *
+ *  @param  line - line identifier
+ *  @param  on -   TRUE -> on, otherwise off
+ *
+ *  @return none
+ */
+void
+ui_set_mwi (line_t line, boolean status, int type, int newCount, int oldCount, int hpNewCount, int hpOldCount)
+{
+    feature_update_t msg;
+
+    TNP_DEBUG(DEB_F_PREFIX"line=%d count=%d \n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), line, status);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_FEATURE_MWI;
+    msg.update.ccFeatUpd.data.mwi_status.line = line;
+    msg.update.ccFeatUpd.data.mwi_status.status = status;
+    msg.update.ccFeatUpd.data.mwi_status.type = type;
+    msg.update.ccFeatUpd.data.mwi_status.newCount = newCount;
+    msg.update.ccFeatUpd.data.mwi_status.oldCount = oldCount;
+    msg.update.ccFeatUpd.data.mwi_status.hpNewCount = hpNewCount;
+    msg.update.ccFeatUpd.data.mwi_status.hpOldCount = hpOldCount;
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send DEVICE_FEATURE_MWI(%d,%d) msg \n", __FUNCTION__, line, status);
+    }
+}
+
+/*
+ * Function: ui_mnc_reached
+ *
+ * @param   line - line number
+ * @param   boolean - maximum number of calls reached on this line
+ *
+ * Description:
+ *    post mnc_reached status to CCAPP
+ *
+ * @return none
+ *
+ */
+void ui_mnc_reached (line_t line, boolean mnc_reached)
+{
+    feature_update_t msg;
+
+    DEF_DEBUG(DEB_F_PREFIX"line %d: Max number of calls reached =%d \n",
+            DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__),
+            line, mnc_reached);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_FEATURE_MNC_REACHED;
+    msg.update.ccFeatUpd.data.line_info.line = line;
+    msg.update.ccFeatUpd.data.line_info.info = mnc_reached;
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send DEVICE_FEATURE_MNC_REACHED(%d,%d) msg \n", __FUNCTION__,
+                       line, mnc_reached);
+    }
+
+}
+
+/**
+ *
+ *  Check if MWI is active on a given line
+ *
+ *  @param  line - line identifier
+ *
+ *  @return true if active; false otherwise.
+ */
+boolean
+ui_line_has_mwi_active (line_t line)
+{
+    session_mgmt_t msg;
+
+    TNP_DEBUG(DEB_F_PREFIX"line=%d\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), line);
+
+    msg.func_id = SESSION_MGMT_LINE_HAS_MWI_ACTIVE;
+    msg.data.line_mwi_active.line = line;
+
+    ccappSyncSessionMgmt(&msg);
+
+    return msg.data.line_mwi_active.ret_val;
+}
+
+
+/**
+ *  CCAPP msg post to Set linekey values
+ *
+ *  @param  line - line identifier
+ *  @param  SpeedDial - Speeddial string to be changed
+ *  @param  label - Feature lable to be changed
+ *
+ *  @return none
+ */
+void
+ui_update_label_n_speeddial (line_t line, line_t button_no, string_t speed_dial, string_t label)
+{
+    feature_update_t msg;
+
+    TNP_DEBUG(DEB_F_PREFIX"line=%d speeddial=%s displayname=%s\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), line,
+                   speed_dial, label);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_LABEL_N_SPEED;
+    msg.update.ccFeatUpd.data.cfg_lbl_n_spd.line = line;
+    msg.update.ccFeatUpd.data.cfg_lbl_n_spd.button = (unsigned char) button_no;
+    msg.update.ccFeatUpd.data.cfg_lbl_n_spd.speed = strlib_malloc(speed_dial, sizeof(speed_dial));
+    msg.update.ccFeatUpd.data.cfg_lbl_n_spd.label = strlib_malloc(label, sizeof(label));
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send DEVICE_LABEL_N_SPEED(%d) msg \n", __FUNCTION__, button_no);
+    }
+}
+
+/**
+ *  Inform CCAPP of registration state of given line in Line Plane
+ *
+ *  @param  line - line identifier
+ *  @param  registered - whether the line was registered or not
+ *
+ *  @return none
+ */
+void
+ui_set_sip_registration_state (line_t line, boolean registered)
+{
+    feature_update_t msg;
+    int value;
+
+    TNP_DEBUG(DEB_F_PREFIX"%s %d: %s\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__),
+                (line==CC_ALL_LINES) ? "ALL LINES":"LINE" ,line,
+                        (registered)? "REGISTERED":"UN-REGISTERED");
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_REG_STATE;
+    msg.update.ccFeatUpd.data.line_info.line = line;
+    msg.update.ccFeatUpd.data.line_info.info = registered ? CC_REGISTERED : CC_UNREGISTERED;
+    config_get_value(CFGID_PROXY_REGISTER, &value, sizeof(value));
+    if (value == 0) {
+        msg.update.ccFeatUpd.data.line_info.info = CC_REGISTERED;
+    }
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_STATE(%d, %d) msg \n", __FUNCTION__, line, registered);
+    }
+}
+
+/**
+ *  Inform CCAPP of registration state of given line in Line Plane
+ *
+ *   @param  registered - line registered true/false
+ *
+ *  @return none
+ */
+void
+ui_update_registration_state_all_lines (boolean registered)
+{
+    DEF_DEBUG(DEB_F_PREFIX"***********ALL LINES %s****************\n",
+                        DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__),
+                        (registered)? "REGISTERED":"UN-REGISTERED");
+
+    ui_set_sip_registration_state(CC_ALL_LINES, registered);
+
+}
+
+/**
+ * Inform the CCAPP that all registration attempts with
+ * all call controls have failed.
+ *
+ * @param  none
+ *
+ * @return none
+ */
+void
+ui_reg_all_failed (void)
+{
+    feature_update_t msg;
+
+    TNP_DEBUG(DEB_F_PREFIX"***********Registration to all CUCMs failed.***********\n",
+                        DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__));
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = CCAPP_REG_ALL_FAIL;
+    msg.update.ccFeatUpd.data.line_info.line = CC_ALL_LINES;
+    msg.update.ccFeatUpd.data.line_info.info = FALSE;
+
+    if ( ccappTaskPostMsg(CCAPP_REG_ALL_FAIL, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_STATE() msg \n", __FUNCTION__);
+    }
+}
+
+/**
+ *
+ * inform CCAPP about the status of the CCMs
+ *
+ * @param ccm_addr - IP address string
+ * @param status   - status (notconnected, active, standby, notavail)
+ *
+ * @return none
+ */
+void
+ui_set_ccm_conn_status (char * ccm_addr, int status)
+{
+    feature_update_t msg;
+
+    DEF_DEBUG(DEB_F_PREFIX"***********CUCM %s %s***********\n",
+            DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), ccm_addr,
+            ((status == 0) ?"Not connected":((status == 1)?"STAND BY":
+            ((status == 2)?"ACTIVE":"UNKNOWN"))));
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_CCM_CONN_STATUS;
+    msg.update.ccFeatUpd.data.ccm_conn.addr = ccm_addr?strlib_malloc(ccm_addr, strlen(ccm_addr)):strlib_empty();
+    msg.update.ccFeatUpd.data.ccm_conn.status = status;
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send DEVICE_CCM_CONN_STATUS(%d) msg \n", __FUNCTION__, status);
+    }
+}
+
+/**
+ *  Treat given line and callid as being put on hold by user.
+ *
+ *  @param  line    - line identifier
+ *  @param  call_id - call identifier
+ *
+ *  @return none
+ */
+void
+ui_set_local_hold (line_t line, callid_t call_id)
+{
+    /* THIS IS A NOP FOR TNP */
+    TNP_DEBUG(DEB_L_C_F_PREFIX"called\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, "ui_set_local_hold"));
+    return;
+}
+
+/**
+ *  Sets the call forward status as a flashing arrow and status line
+ *  accordingly.
+ *
+ *  @param line       - line identifier
+ *  @param cfa        - call forward all true/false
+ *  @param cfa_number - string representing call forwarded to number
+ *
+ *  @return none
+ */
+void
+ui_cfwd_status (line_t line, boolean cfa, char *cfa_number, boolean lcl_fwd)
+{
+    feature_update_t msg;
+
+    TNP_DEBUG(DEB_F_PREFIX"line=%d cfa=%d cfa_number=%s lcl_fwd=%d", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__),
+              line, cfa, cfa_number, lcl_fwd);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_FEATURE_CFWD;
+    msg.update.ccFeatUpd.data.cfwd.line = line;
+    msg.update.ccFeatUpd.data.cfwd.isFwd = cfa;
+    msg.update.ccFeatUpd.data.cfwd.isLocal = lcl_fwd;
+    msg.update.ccFeatUpd.data.cfwd.cfa_num = cfa_number?strlib_malloc(cfa_number, strlen(cfa_number)):strlib_empty();
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send DEVICE_FEATURE_CFWD(%d) msg \n", __FUNCTION__, cfa);
+    }
+}
+
+/**
+ *  Get the Idle prompt string.
+ *
+ *  @return the idle prompt phrase from locale
+ */
+char *
+ui_get_idle_prompt_string (void)
+{
+    TNP_DEBUG(DEB_F_PREFIX"called\n", DEB_F_PREFIX_ARGS(UI_API, "ui_get_idle_prompt_string"));
+    return platform_get_phrase_index_str(IDLE_PROMPT);
+}
+
+/**
+ *  Set the Appropriate Idle prompt string.
+ *
+ *  @param pString    - New Idle Prompt String
+ *  @param prompt     - The Idle Prompt To Be Modified
+ *
+ *  @return the idle prompt phrase from locale
+ */
+void
+ui_set_idle_prompt_string (string_t pString, int prompt)
+{
+    TNP_DEBUG(DEB_F_PREFIX"Prompt=%d, Prompt string=%s NOP operation\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), prompt, pString);
+}
+
+/**
+ *  Updates placed call information for call history
+ *
+ *  @param line      - line identifier
+ *  @param call_id   - call identifier
+ *  @param cldName   - called name
+ *  @param cldNumber - called number
+ *
+ *  @return none
+ */
+void
+ui_update_placed_call_info (line_t line, callid_t call_id, string_t cldName,
+                            string_t cldNumber)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"calledName:calledNumber %s:%s\n",
+              DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__), cldName, cldNumber);
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        TNP_DEBUG(DEB_F_PREFIX"invalid callid\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__));
+        return;
+    }
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = CALL_PLACED_INFO;
+       msg.update.ccSessionUpd.data.plcd_info.cldName = strlib_empty();
+       msg.update.ccSessionUpd.data.plcd_info.cldNum = strlib_empty();
+
+       if ( cldName) {
+      msg.update.ccSessionUpd.data.plcd_info.cldName = strlib_update(
+                 msg.update.ccSessionUpd.data.plcd_info.cldName, cldName);
+       }
+       if ( cldNumber) {
+      msg.update.ccSessionUpd.data.plcd_info.cldNum = strlib_update(
+                 msg.update.ccSessionUpd.data.plcd_info.cldNum, cldNumber);
+       }
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_PLACED_INFO(%s) msg \n", __FUNCTION__, cldNumber);
+    }
+}
+
+
+/**
+ *  Description: remove last digit from input box
+ *
+ *  @param  line -    line identifier
+ *  @param  call_id - call identifier
+ *
+ *  @return none
+ */
+void
+ui_delete_last_digit (line_t line_id, callid_t call_id)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"called\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line_id, call_id, __FUNCTION__));
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(line_id, call_id);
+    msg.eventID = CALL_DELETE_LAST_DIGIT;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_DELETE_LAST_DIGIT() msg \n", __FUNCTION__);
+    }
+}
+
+/**
+ *  Keep/Remove BackSpace key if presented
+ *
+ *  @param   line_id - line identifier
+ *  @param   call_id - call identifier
+ *  @param   enable  - TRUE -> enable backspace softkey
+ *
+ *  @return none
+ */
+void
+ui_control_featurekey_bksp (line_t line_id, callid_t call_id, boolean enable)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"enable=%d\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line_id, call_id, __FUNCTION__),
+              enable);
+
+    msg.sessionID = createSessionId(line_id, call_id);
+    msg.eventID = CALL_ENABLE_BKSP;
+    msg.update.ccSessionUpd.data.action = enable;
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_ENABLE_BKSP(%d) msg \n", __FUNCTION__, enable);
+    }
+}
+
+
+/**
+ *  Select(lock-down) the call specified.
+ *  For tnp this visually places a check box on the bubble.
+ *
+ *  @param  line_id  - line identifier
+ *  @param  call_id  - call identifier
+ *  @param  selected - TRUE -> call is locked-down in signaling,
+ *                     mark as such on UI
+ *
+ *  @return none
+ */
+void
+ui_call_selected (line_t line_id, callid_t call_id, int selected)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"selected=%d\n",
+              DEB_L_C_F_PREFIX_ARGS(UI_API, line_id, call_id, __FUNCTION__), selected);
+
+    msg.sessionID = createSessionId(line_id, call_id);
+    msg.eventID = CALL_SELECTED;
+    msg.update.ccSessionUpd.data.action = selected;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_SELECTED(%d) msg \n", __FUNCTION__, selected);
+    }
+}
+
+/**
+ * Send the BLF state to the UI apps.
+ *
+ * @param[in] state    - TRUE/FALSE
+ *
+ * @return none
+ */
+void ui_BLF_notification (int request_id, cc_blf_state_t blf_state, int app_id)
+{
+    feature_update_t msg;
+
+    TNP_DEBUG(DEB_F_PREFIX"state=%d app_id=%d\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), blf_state, app_id);
+
+    msg.sessionType = SESSIONTYPE_CALLCONTROL;
+    msg.featureID = DEVICE_FEATURE_BLF;
+    msg.update.ccFeatUpd.data.blf_data.state = blf_state;
+    msg.update.ccFeatUpd.data.blf_data.request_id = request_id;
+    msg.update.ccFeatUpd.data.blf_data.app_id = app_id;
+
+    if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send DEVICE_FEATURE_BLF(state=%d, app_id=%d) msg \n",
+                    __FUNCTION__, blf_state, app_id);
+    }
+}
+
+/**
+ *
+ * Change the softkey set to preservation key set (with just the EndCall key)
+ * From the platform perspective, this is strictly a softkey set change
+ * operation. There is no underlying state change in the call or its status.
+ * In other words, after this invocation, the clients can still issue
+ * ui_call_state() or any other call operation if they deem fit and
+ * the softkey set will adhere to that state.
+ *
+ * @param line_id  - line identifier
+ * @param call_id  - call identifier
+ *
+ * @return none
+ */
+void
+ui_call_in_preservation (line_t line_id, callid_t call_id)
+{
+    TNP_DEBUG(DEB_L_C_F_PREFIX"called\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line_id, call_id, __FUNCTION__));
+
+    /* simply update the state . A Preservation event from
+       CUCM is just for the session */
+    ui_call_state (evCallPreservation , line_id, call_id, CC_CAUSE_NORMAL);
+}
+
+/**
+ * request ui to present a named softkey set for given call.
+ * The softkey set passed(set_name param) must be known to the platform.
+ * enable_mask is a set of bits (starting from LSB) one for each softkey.
+ * (so least significant bit represents left most softkey and so on).
+ * This function is typically used to mask certain softkeys in a given set.
+ * Use of this API as general purpose mechanism to present softkeys is
+ * discouraged because it breaks encapsulation. This API is introduced
+ * for the tough cases where adapter can not determine which keys to
+ * mask based on its state alone.
+ *
+ * @param line_id     - line identifier
+ * @param call_id     - call identifier
+ * @param set_name    - name of the softkey set
+ * @param sk_mask_list - the softkey events that need to be masked
+ * @param len          - length of the softkey list array
+ *
+ * @return  none
+ */
+void
+ui_select_feature_key_set (line_t line_id, callid_t call_id, char *set_name,
+                           int sk_mask_list[], int len)
+{
+    int i;
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"called\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line_id, call_id, __FUNCTION__));
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    if (len <= 0 || len > MAX_SOFT_KEYS) {
+        TNP_DEBUG(DEB_F_PREFIX"Incorrect softkey array length passed in : %d\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), len);
+        return;
+    }
+
+    memset( &msg, 0, sizeof(session_update_t));
+
+    msg.sessionID = createSessionId(line_id, call_id);
+    msg.eventID = CALL_SELECT_FEATURE_SET;
+
+    if ( set_name == NULL ) {
+       // No point continuing here
+       return;
+    }
+
+    msg.update.ccSessionUpd.data.feat_set.featSet = set_name?strlib_malloc(set_name, sizeof(set_name)):strlib_empty();
+    for (i = 0; i < len; i++) {
+      msg.update.ccSessionUpd.data.feat_set.featMask[i] = sk_mask_list[i];
+    }
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_SELECT_FEATURE_SET() msg \n", __FUNCTION__);
+    }
+}
+
+
+/* Test Interface Operations */
+
+/**
+ *  Inject a simulated keypress into the Java Infrastructure
+ *
+ *  @param   uri - string
+ *
+ *  @return none
+ */
+void
+ui_execute_uri (char *uri)
+{
+    session_mgmt_t msg;
+
+    TNP_DEBUG(DEB_F_PREFIX"uri=%s\n", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), uri);
+
+    msg.func_id = SESSION_MGMT_EXECUTE_URI;
+    msg.data.uri.uri = STRLIB_CREATE(uri);
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_MGMT, &msg, sizeof(session_mgmt_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(DEB_F_PREFIX"failed to send EXECUTE_URI() msg\n", DEB_F_PREFIX_ARGS(PLAT_API, __FUNCTION__));
+    }
+}
+
+/* Log Message Interface Operations */
+
+/**
+ *
+ * Update Security (mainly lock) Icon on the call bubble.
+ * NOTE: Only used to indicate media security (ENCRYPTED or not).
+ * For updating the signaling security(shield icon), use uiCallInfo or
+ * uiUpdateCallInfo.
+ *
+ * @param  line - line identifier
+ * @param  call_id - call identifier
+ * @param  call_security - follows the cc_security_e enum
+ *
+ * @return none
+ */
+void
+ui_update_call_security (line_t line, callid_t call_id,
+                        cc_security_e call_security)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"security=%d\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__),
+              call_security);
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = CALL_SECURITY;
+    msg.update.ccSessionUpd.data.security = call_security;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_SECURITY(%d) msg \n", __FUNCTION__, call_security);
+       }
+}
+
+/*
+ *Just for TNP currently.
+ */
+void
+ui_update_conf_invoked (line_t line, callid_t call_id,
+                        boolean invoked)
+{
+      //Nothing to do here
+}
+
+
+/**
+ *
+ * Cancel the feature plane given by call_id.
+ *
+ * @param  line - line identifier
+ * @param  call_id - call identifier
+ * @param  target_call_id - target call id
+ *
+ * @return none
+ */
+void
+ui_terminate_feature (line_t line, callid_t call_id,
+                        callid_t target_call_id)
+{
+    session_update_t msg;
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"target_call_id=%d\n", DEB_L_C_F_PREFIX_ARGS(UI_API, line, call_id, __FUNCTION__),
+              target_call_id);
+
+    msg.sessionID = createSessionId(line, call_id);
+    msg.eventID = CALL_FEATURE_CANCEL;
+    if (target_call_id != CC_NO_CALL_ID) {
+        msg.update.ccSessionUpd.data.target_sess_id = createSessionId(line, target_call_id);
+    } else {
+        msg.update.ccSessionUpd.data.target_sess_id = 0;
+    }
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_FEATURE_CANCEL(%d) msg \n", __FUNCTION__, target_call_id);
+    }
+}
+
+/* APIs required for CTI operations */
+
+/* NOP for TNP: the speaker mode is set by media manager. if call control
+ * need to set the mode explicitely use vcm_set_speaker_mode.
+ */
+void
+ui_set_speaker_mode (boolean mode)
+{
+    return;
+}
+
+void
+ui_cfwdall_req (unsigned int line)
+{
+    lsm_clear_cfwd_all_ccm(line);
+    return;
+}
+
+
+/***********************************************************/
+
+char *
+Basic_is_phone_forwarded (line_t line)
+{
+    TNP_DEBUG(DEB_F_PREFIX"called for line %d\n", DEB_F_PREFIX_ARGS(UI_API, "Basic_is_phone_forwarded"), line);
+    return ((char *) lsm_is_phone_forwarded(line));
+}
+
+void
+ui_sip_config_done (void)
+{
+}
+
+/**
+ * convert to numeric dtmf tone code that ms
+ * understands from ascii code
+ */
+static int map_digit(char k)
+{
+    switch(k) {
+    case '1':
+        return 1;
+    case '2':
+        return 2;
+    case '3':
+        return 3;
+    case '4':
+        return 4;
+    case '5':
+        return 5;
+    case '6':
+        return 6;
+    case '7':
+        return 7;
+    case '8':
+        return 8;
+    case '9':
+        return 9;
+    case '0':
+        return 0;
+    case '*':
+        return 10;
+    case'#':
+        return 11;
+    case 'A':
+        return 12;
+    case 'B':
+        return 13;
+    case 'C':
+        return 14;
+    case 'D':
+        return 15;
+    default:
+        return -1;
+    }
+}
+
+
+/**
+ * Emulate keypad button press
+ *
+ * @param digitstr - one or more digits to be pressed
+ * @param direction - either VCM_PLAY_TONE_TO_EAR or VCM_PLAY_TONE_TO_NET
+ *                    or VCM_PLAY_TONE_TO_ALL
+ *
+ * @return none
+ */
+void
+ui_keypad_button (char *digitstr, int direction)
+{
+    int digit;
+    unsigned int i;
+
+
+    for (i=0; i<strlen(digitstr); i++) {
+        digit = map_digit(digitstr[i]);
+        if (digit != -1) {
+            /* burst it out */
+            vcmDtmfBurst(digit, direction, 100);
+            cprSleep(100+10);
+        }
+    }
+
+}
+
+/**
+ *  Send a log message to the Java Status messages screen
+ *
+ *  @param   message - string
+ *
+ *  @return none
+ */
+void
+ui_log_status_msg (char *msg)
+{
+    ui_set_notification(CC_NO_LINE, CC_NO_CALL_ID, msg, 0, FALSE, 1);
+}
+
+/**
+ *  Set the log disposition for call history application
+ *
+ *  @param   callid_t - callID of the call
+ *  @param   int - log disposition
+ *
+ *  @return none
+ */
+
+void ui_log_disposition (callid_t call_id, int logdisp)
+{
+    session_update_t msg;
+    fsmdef_dcb_t *dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+
+    if (call_id == CC_NO_CALL_ID || dcb == NULL) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"called\n", DEB_L_C_F_PREFIX_ARGS(UI_API, dcb->line, call_id, __FUNCTION__));
+
+    msg.sessionID = createSessionId(dcb->line, call_id);
+    msg.eventID = CALL_LOGDISP;
+    msg.update.ccSessionUpd.data.action = logdisp;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR("%s: failed to send CALL_PRESERVATION_ACTIVE(%d) msg \n", __FUNCTION__, call_id);
+    }
+}
+
+/**
+ *  Stub for ui_control_feature
+ *
+ * @param line_id     - line identifier
+ * @param call_id     - call identifier
+ * @param feature     - name of the softkey set
+ * @param enable      - enable/disable
+ *
+ * @return  none
+ */
+void
+ui_control_feature (line_t line_id, callid_t call_id,
+                       int feat_list[], int len, int enable)
+{
+    // do nothing.
+}
+
+/*
+ *  Helper for the following four functions which all load up a
+ *  session_update message and post it.
+ *
+ */
+static void post_message_helper(
+    group_call_event_t eventId,
+    call_events event,
+    line_t nLine,
+    callid_t nCallId,
+    uint16_t call_instance_id,
+    char *sdp,
+    cc_int32_t status)
+{
+    session_update_t msg;
+
+    if (nCallId == CC_NO_CALL_ID) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    msg.sessionID = createSessionId(nLine, nCallId);
+
+    msg.eventID = eventId;
+    msg.update.ccSessionUpd.data.state_data.state = event;
+    msg.update.ccSessionUpd.data.state_data.inst = call_instance_id;
+    msg.update.ccSessionUpd.data.state_data.line_id = nLine;
+    msg.update.ccSessionUpd.data.state_data.sdp = sdp;
+    if (eventId == SET_LOCAL_DESC || eventId == SET_REMOTE_DESC) {
+        msg.update.ccSessionUpd.data.state_data.cause = status;
+    }
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_STATE(%d) msg \n", __FUNCTION__, event);
+    }
+
+    return;
+}
+
+/**
+ *  Send data from createOffer to the UI, can send success with SDP string
+ *  or can send error
+ *
+ *  @return none
+ */
+void ui_create_offer(call_events event, line_t nLine, callid_t nCallID,
+                        uint16_t call_instance_id, char* sdp)
+{
+    TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d attr=%d call_instance=%d\n",
+              DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
+
+    post_message_helper(CREATE_OFFER, event, nLine, nCallID, call_instance_id, sdp, 0);
+
+    return;
+}
+
+/**
+ *  Send data from createAnswer to the UI, can send success with SDP string
+ *  or can send error
+ *
+ *  @return none
+ */
+void ui_create_answer(call_events event, line_t nLine, callid_t nCallID,
+                        uint16_t call_instance_id, char* sdp)
+{
+    TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d\n",
+              DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
+
+    post_message_helper(CREATE_ANSWER, event, nLine, nCallID, call_instance_id, sdp, 0);
+
+    return;
+}
+
+/**
+ *  Send data from setLocalDescription to the UI
+ *
+ *  @return none
+ */
+
+void ui_set_local_description(call_events event, line_t nLine, callid_t nCallID,
+                        uint16_t call_instance_id, char* sdp, cc_int32_t status)
+{
+    TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d\n",
+              DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
+
+    post_message_helper(SET_LOCAL_DESC, event, nLine, nCallID, call_instance_id, sdp, status);
+
+    return;
+}
+
+/**
+ *  Send data from setRemoteDescription to the UI
+ *
+ *  @return none
+ */
+
+void ui_set_remote_description(call_events event, line_t nLine, callid_t nCallID,
+                        uint16_t call_instance_id, char* sdp, cc_int32_t status)
+{
+    TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d\n",
+              DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
+
+    post_message_helper(SET_REMOTE_DESC, event, nLine, nCallID, call_instance_id, sdp, status);
+
+    return;
+}
+
+
+/**
+ *  Send Remote Stream data to the UI
+ *
+ *  @return none
+ */
+
+void ui_on_remote_stream_added(call_events event, line_t nLine, callid_t nCallID, uint16_t call_instance_id, cc_media_remote_track_table_t media_track)
+{
+    session_update_t msg;
+    fsmdef_dcb_t *dcb = fsmdef_get_dcb_by_call_id(nCallID);
+
+    if (nCallID == CC_NO_CALL_ID || dcb == NULL) {
+        /* no operation when no call ID */
+        return;
+    }
+
+    TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d\n",
+              DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
+
+
+    msg.sessionID = createSessionId(nLine, nCallID);
+
+    msg.eventID = REMOTE_STREAM_ADD;
+    msg.update.ccSessionUpd.data.state_data.state = event;
+    msg.update.ccSessionUpd.data.state_data.inst = call_instance_id;
+    msg.update.ccSessionUpd.data.state_data.line_id = nLine;
+    msg.update.ccSessionUpd.data.state_data.media_stream_track_id = media_track.track[0].media_stream_track_id;
+    msg.update.ccSessionUpd.data.state_data.media_stream_id = (unsigned int)media_track.media_stream_id;
+
+    if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
+        CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_STATE(%d) msg \n", __FUNCTION__, event);
+    }
+
+    return;
+}
diff --git a/libs/sipcc/core/gsm/ccapi.c b/libs/sipcc/core/gsm/ccapi.c
new file mode 100755 (executable)
index 0000000..9f9a658
--- /dev/null
@@ -0,0 +1,1749 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "cpr_memory.h"
+#include "cpr_stdlib.h"
+#include "ccapi.h"
+#include "ccsip_task.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "phntask.h"
+#include "phone.h"
+#include "text_strings.h"
+#include "string_lib.h"
+#include "gsm.h"
+#include "vcm.h"
+#include "sip_common_regmgr.h"
+#include "util_string.h"
+
+static const char *cc_src_names[] = {
+    "GSM",
+    "UI",
+    "SIP",
+    "MISC_APP",
+    "CCAPP"
+};
+
+#define CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, msg) \
+    DEF_DEBUG(DEB_L_C_F_PREFIX"%s -> %s: %-20s\n",\
+                       DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__),\
+            cc_src_name(src_id), cc_src_name(dst_id), msg)
+
+
+callid_t cc_get_new_call_id (void)
+{
+    static callid_t call_id = CC_NO_CALL_ID;
+
+    if (++call_id == 0) {
+        call_id = 1;
+    }
+
+    return call_id;
+}
+
+
+const char *
+cc_src_name (cc_srcs_t id)
+{
+    if ((id <= CC_SRC_MIN) || (id >= CC_SRC_MAX)) {
+        return get_debug_string(GSM_UNDEFINED);
+    }
+
+    return cc_src_names[id];
+}
+
+
+static void
+cc_print_msg (char *pData, int len)
+{
+    int ix;
+    int msg_id = *((int *) pData);
+
+    buginf("\n" CCA_F_PREFIX "cc_msg= %s, 0x=", __FUNCTION__,
+                            cc_msg_name((cc_msgs_t) msg_id));
+    for (ix = 0; ix < len; ix++) {
+        if ((ix % 8 == 0) && ix) {
+            buginf("  ");
+        }
+        if (ix % 24 == 0) {
+            buginf("\n");
+        }
+        buginf("%02x ", *pData++);
+    }
+    buginf("\n");
+}
+
+
+/*
+ * Return a SysBuf and initialize
+ * Parameters supplied by application:
+ *  - MinSize: size of buffer requested
+ */
+cprBuffer_t
+cc_get_msg_buf (int min_size)
+{
+    cprBuffer_t buf;
+
+    if (min_size > CPR_MAX_MSG_SIZE) {
+        /* Size requested exceeds maximum ethernet buffer */
+        GSM_ERR_MSG(get_debug_string(DEBUG_MSG_BUFFER_TOO_BIG),
+                    __FUNCTION__, min_size);
+        return (cprBuffer_t)NULL;
+    }
+
+    buf = gsm_get_buffer((uint16_t) min_size);
+    if (!buf) {
+        GSM_ERR_MSG(get_debug_string(DEBUG_SYSBUF_UNAVAILABLE), __FUNCTION__);
+        return (cprBuffer_t)NULL;
+    }
+
+    /* Clean out the data region of the message */
+    memset(buf, 0, min_size);
+
+    CC_DEBUG(DEB_F_PREFIX "Msg id = 0x%0x\n", DEB_F_PREFIX_ARGS(CC_API, __FUNCTION__), buf);
+
+    return buf;
+}
+
+static cc_rcs_t
+cc_send_cmd_msg (uint32_t cmd, cprBuffer_t buf, uint16_t len, cc_srcs_t dst_id)
+{
+    cpr_status_e rc;
+
+    CC_DEBUG_MSG cc_print_msg((char *) buf, len);
+
+    switch (dst_id) {
+    case CC_SRC_GSM:
+        rc = gsm_send_msg(cmd, buf, len);
+        if (rc == CPR_FAILURE) {
+            cc_free_msg_data((cc_msg_t *) buf);
+            cpr_free(buf);
+        }
+        break;
+    case CC_SRC_SIP:
+        rc = SIPTaskSendMsg(cmd, buf, len, NULL);
+        if (rc == CPR_FAILURE) {
+            cc_free_msg_data((cc_msg_t *) buf);
+            cpr_free(buf);
+        }
+        break;
+    default:
+        rc = CPR_FAILURE;
+        break;
+    }
+
+    return (rc == CPR_SUCCESS) ? CC_RC_SUCCESS : CC_RC_ERROR;
+}
+
+static cc_rcs_t
+cc_send_msg (cprBuffer_t buf, uint16_t len, cc_srcs_t dst_id)
+{
+    cpr_status_e rc;
+
+    CC_DEBUG_MSG cc_print_msg((char *) buf, len);
+
+    switch (dst_id) {
+    case CC_SRC_GSM:
+        rc = gsm_send_msg(GSM_SIP, buf, len);
+        if (rc == CPR_FAILURE) {
+            cc_free_msg_data((cc_msg_t *) buf);
+            cpr_free(buf);
+        }
+        break;
+    case CC_SRC_SIP:
+        rc = SIPTaskSendMsg(SIP_GSM, buf, len, NULL);
+        if (rc == CPR_FAILURE) {
+            cc_free_msg_data((cc_msg_t *) buf);
+            cpr_free(buf);
+        }
+        break;
+    default:
+        rc = CPR_FAILURE;
+        break;
+    }
+
+    return (rc == CPR_SUCCESS) ? CC_RC_SUCCESS : CC_RC_ERROR;
+}
+
+
+/*
+ *  ROUTINE:     cc_initialize_msg_body_parts_info
+ *
+ *  DESCRIPTION: Initializes the msg body part.
+ *
+ *  PARAMETERS:
+ *      msg_body - pointer to cc_msgbody_info_t to be initialized.
+ *
+ *  RETURNS:
+ *      None.
+ *
+ *  NOTES:       None
+ */
+void
+cc_initialize_msg_body_parts_info (cc_msgbody_info_t *msg_body)
+{
+    if (msg_body == NULL) {
+        return;
+    }
+    msg_body->num_parts = 0;
+    memset(&msg_body->parts[0], 0, sizeof(msg_body->parts));
+}
+
+/*
+ *  ROUTINE:     cc_mv_msg_body_parts
+ *
+ *  DESCRIPTION: Move the body parts from source to destination.
+ *
+ *  PARAMETERS:
+ *      dst_msg - pointer to destination cc_msgbody_info_t
+ *      src_msg - pointer to source cc_msgbody_info_t
+ *
+ *  RETURNS:
+ *      None
+ *
+ *  NOTES:       The function attempts to free the message bodies
+ *               that might be in the destination to prevent
+ *               memory leak from overridden the destination by
+ *               moving the new message source.
+ *
+ *               The dst_msg must be pointed to the initialized
+ *               message block either with all zero or at least
+ *               number of parts is set to zero to prevent
+ *               freeing an invalid address.
+ */
+void
+cc_mv_msg_body_parts (cc_msgbody_info_t *dst_msg, cc_msgbody_info_t *src_msg)
+{
+    if (dst_msg == NULL) {
+        GSM_ERR_MSG(CCA_F_PREFIX "dst is NULL\n", __FUNCTION__);
+        return;
+    }
+
+    /* Free the msg. bodies that might be in the dest first */
+    cc_free_msg_body_parts(dst_msg);
+
+    if (src_msg != NULL) {
+        /* copy all of the parts */
+        *dst_msg = *src_msg;
+        src_msg->num_parts = 0;
+    }
+}
+
+/*
+ *  ROUTINE:     cc_free_msg_body_parts
+ *
+ *  DESCRIPTION: Free elements in the msg body part structure.
+ *
+ *  PARAMETERS:
+ *      msg_body - pointer to cc_msgbody_info_t for freeing.
+ *
+ *  RETURNS:
+ *      None
+ *
+ *  NOTES:       None
+ */
+void
+cc_free_msg_body_parts (cc_msgbody_info_t *msg_body)
+{
+    cc_msgbody_t *part;
+
+    if ((msg_body == NULL) || (msg_body->num_parts == 0)) {
+        /* Nothing to be freed */
+        return;
+    }
+
+    /* Free all of the bodies and their contents */
+    part = &msg_body->parts[0];
+    for (; msg_body->num_parts; msg_body->num_parts--, part++) {
+        if (part->body != NULL) {
+            cpr_free(part->body);
+            part->body = NULL;
+        }
+        if (part->content_id != NULL) {
+            cpr_free(part->content_id);
+            part->content_id = NULL;
+        }
+    }
+}
+
+/*
+ *  ROUTINE:     cc_get_msg_body_info_ptr_from_feature_data
+ *
+ *  DESCRIPTION: The function gets msg pointer from a cc_feature_data_t
+ *               structure if there is one. The msg body
+ *               info does not aways attach to all features
+ *               i.e. only certain feature IDs have msg body
+ *               in the feature data. The function returns
+ *               pointer to the msg body if the feature has
+ *               valid data and for those features that contain
+ *               msg body.
+ *
+ *  PARAMETERS:
+ *      id   - cc_feature_t.
+ *      data - pointer to cc_feature_data_t of the feature data to
+ *             whose embedded resources need to be freed.
+ *
+ *  RETURNS:
+ *      msg_body - pointer to msg_body_info_t if there is
+ *                 a msg.
+ *
+ *  NOTES:       None
+ */
+static cc_msgbody_info_t *
+cc_get_msg_body_info_ptr_from_feature_data (cc_features_t id,
+                                            cc_feature_data_t *data)
+{
+    cc_msgbody_info_t *msg_body = NULL;
+
+    if (data == NULL) {
+        return (NULL);
+    }
+
+    switch (id) {
+    case CC_FEATURE_HOLD:
+        msg_body = &data->hold.msg_body;
+        break;
+    case CC_FEATURE_RESUME:
+    case CC_FEATURE_MEDIA:
+        msg_body = &data->resume.msg_body;
+        break;
+    case CC_FEATURE_UPDATE:
+        msg_body = &data->update.msg_body;
+        break;
+    default:
+        /*
+         * Other ones do not have msg body info yet, add the handling here
+         * when adding feature that needs msg body info.
+         */
+        break;
+    }
+    return (msg_body);
+}
+
+/*
+ *  ROUTINE:     cc_cp_caller
+ *
+ *  DESCRIPTION: Copy caller ID and other fields from source to destination.
+ *
+ *  PARAMETERS:
+ *      dst_caller - pointer to destination cc_caller_id_t
+ *      src_caller - pointer to source cc_caller_id_t
+ *
+ *  RETURNS:
+ *      None
+ *
+ *  Note: See also cc_mv_caller_id() cc_free_caller().
+ */
+static void
+cc_cp_caller (cc_caller_id_t *dst_caller, cc_caller_id_t *src_caller)
+{
+    if ((src_caller == NULL) || (dst_caller == NULL)) {
+        return;
+    }
+
+    dst_caller->calling_name = strlib_empty();
+    if (src_caller->calling_name != NULL) {
+        dst_caller->calling_name = strlib_update(dst_caller->calling_name,
+                                                 src_caller->calling_name);
+    }
+
+    dst_caller->calling_number = strlib_empty();
+    if (src_caller->calling_number != NULL) {
+        dst_caller->calling_number = strlib_update(dst_caller->calling_number,
+                                                   src_caller->calling_number);
+    }
+
+    dst_caller->alt_calling_number = strlib_empty();
+    if (src_caller->alt_calling_number != NULL) {
+        dst_caller->alt_calling_number = strlib_update(dst_caller->alt_calling_number,
+                                                   src_caller->alt_calling_number);
+    }
+
+    dst_caller->called_name = strlib_empty();
+    if (src_caller->called_name != NULL) {
+        dst_caller->called_name = strlib_update(dst_caller->called_name,
+                                                src_caller->called_name);
+    }
+
+    dst_caller->called_number = strlib_empty();
+    if (src_caller->called_number != NULL) {
+        dst_caller->called_number = strlib_update(dst_caller->called_number,
+                                                  src_caller->called_number);
+    }
+
+    dst_caller->orig_called_name = strlib_empty();
+    if (src_caller->orig_called_name != NULL) {
+        dst_caller->orig_called_name =
+            strlib_update(dst_caller->orig_called_name,
+                          src_caller->orig_called_name);
+    }
+
+    dst_caller->orig_called_number = strlib_empty();
+    if (src_caller->orig_called_number != NULL) {
+        dst_caller->orig_called_number =
+            strlib_update(dst_caller->orig_called_number,
+                          src_caller->orig_called_number);
+    }
+
+    dst_caller->last_redirect_name = strlib_empty();
+    if (src_caller->last_redirect_name != NULL) {
+        dst_caller->last_redirect_name =
+            strlib_update(dst_caller->last_redirect_name,
+                          src_caller->last_redirect_name);
+    }
+
+    dst_caller->last_redirect_number = strlib_empty();
+    if (src_caller->last_redirect_number) {
+        dst_caller->last_redirect_number =
+            strlib_update(dst_caller->last_redirect_number,
+                          src_caller->last_redirect_number);
+    }
+
+    dst_caller->orig_rpid_number = strlib_empty();
+    if (src_caller->orig_rpid_number != NULL) {
+        dst_caller->orig_rpid_number =
+            strlib_update(dst_caller->orig_rpid_number,
+                          src_caller->orig_rpid_number);
+    }
+    dst_caller->display_calling_number = src_caller->display_calling_number;
+    dst_caller->display_called_number = src_caller->display_called_number;
+    dst_caller->call_type = src_caller->call_type;
+    dst_caller->call_instance_id = src_caller->call_instance_id;
+}
+
+/*
+ *  ROUTINE:     cc_mv_caller_id
+ *
+ *  DESCRIPTION: Move caller ID fields from source to destination.
+ *               The caller ID fields from the destination will be
+ *               freed if necessary prior to the move. This allows
+ *               caller to replace the older IDs with the new IDs
+ *               without data duplication.
+ *
+ *  PARAMETERS:
+ *      dst_caller - pointer to destination cc_caller_id_t
+ *      src_caller - pointer to source cc_caller_id_t
+ *
+ *  RETURNS:
+ *      None
+ *
+ *  Note: See also cc_cp_caller() cc_free_caller().
+ */
+void
+cc_mv_caller_id (cc_caller_id_t *dst_caller, cc_caller_id_t *src_caller)
+{
+    if ((src_caller == NULL) || (dst_caller == NULL)) {
+        return;
+    }
+
+    /*
+     * Move the IDs from the source to the destination.
+     * After moved the IDs from the source to the destination then
+     * the source needs to be NULL.
+     */
+    if (src_caller->calling_name != NULL) {
+        if (dst_caller->calling_name != NULL) {
+            strlib_free(dst_caller->calling_name);
+        }
+        dst_caller->calling_name = src_caller->calling_name;
+        src_caller->calling_name = NULL;
+    }
+
+    if (src_caller->calling_number != NULL) {
+        if (dst_caller->calling_number != NULL) {
+            strlib_free(dst_caller->calling_number);
+        }
+        dst_caller->calling_number = src_caller->calling_number;
+        src_caller->calling_number = NULL;
+    }
+
+    if (src_caller->alt_calling_number != NULL) {
+        if (dst_caller->alt_calling_number != NULL) {
+            strlib_free(dst_caller->alt_calling_number);
+        }
+        dst_caller->alt_calling_number = src_caller->alt_calling_number;
+        src_caller->alt_calling_number = NULL;
+    }
+
+    if (src_caller->called_name != NULL) {
+        if (dst_caller->called_name != NULL) {
+            strlib_free(dst_caller->called_name);
+        }
+        dst_caller->called_name = src_caller->called_name;
+        src_caller->called_name = NULL;
+    }
+
+    if (src_caller->called_number != NULL) {
+        if (dst_caller->called_number != NULL) {
+            strlib_free(dst_caller->called_number);
+        }
+        dst_caller->called_number = src_caller->called_number;
+        src_caller->called_number = NULL;
+    }
+
+    if (src_caller->orig_called_name != NULL) {
+        if (dst_caller->orig_called_name != NULL) {
+            strlib_free(dst_caller->orig_called_name);
+        }
+        dst_caller->orig_called_name = src_caller->orig_called_name;
+        src_caller->orig_called_name = NULL;
+    }
+
+    if (src_caller->orig_called_number != NULL) {
+        if (dst_caller->orig_called_number != NULL) {
+            strlib_free(dst_caller->orig_called_number);
+        }
+        dst_caller->orig_called_number = src_caller->orig_called_number;
+        src_caller->orig_called_number = NULL;
+    }
+
+    if (src_caller->last_redirect_name != NULL) {
+        if (dst_caller->last_redirect_name != NULL) {
+            strlib_free(dst_caller->last_redirect_name);
+        }
+        dst_caller->last_redirect_name = src_caller->last_redirect_name;
+        src_caller->last_redirect_name = NULL;
+    }
+
+    if (src_caller->last_redirect_number != NULL) {
+        if (dst_caller->last_redirect_number != NULL) {
+            strlib_free(dst_caller->last_redirect_number);
+        }
+        dst_caller->last_redirect_number = src_caller->last_redirect_number;
+        src_caller->last_redirect_number = NULL;
+    }
+
+    if (src_caller->orig_rpid_number != NULL) {
+        if (dst_caller->orig_rpid_number != NULL) {
+            strlib_free(dst_caller->orig_rpid_number);
+        }
+        dst_caller->orig_rpid_number = src_caller->orig_rpid_number;
+        src_caller->orig_rpid_number = NULL;
+    }
+    dst_caller->display_calling_number = src_caller->display_calling_number;
+    dst_caller->display_called_number = src_caller->display_called_number;
+}
+
+/*
+ *  ROUTINE:     cc_free_caller_id
+ *
+ *  DESCRIPTION: Free caller ID fields. The IDs that are not NULL IDs will be
+ *               freed because others might have been moved already.
+ *
+ *  PARAMETERS:
+ *      caller - pointer to destination cc_caller_id_t
+ *
+ *  RETURNS:
+ *      None
+ *
+ *  Note: See also cc_cp_caller() cc_mv_caller().
+ */
+static void
+cc_free_caller_id (cc_caller_id_t *caller)
+{
+    if (caller == NULL) {
+        return;
+    }
+
+    if (caller->calling_name != NULL) {
+        strlib_free(caller->calling_name);
+    }
+
+    if (caller->calling_number != NULL) {
+        strlib_free(caller->calling_number);
+    }
+
+    if (caller->alt_calling_number != NULL) {
+        strlib_free(caller->alt_calling_number);
+    }
+
+    if (caller->called_name != NULL) {
+        strlib_free(caller->called_name);
+    }
+
+    if (caller->called_number != NULL) {
+        strlib_free(caller->called_number);
+    }
+
+    if (caller->orig_called_name != NULL) {
+        strlib_free(caller->orig_called_name);
+    }
+
+    if (caller->orig_called_number != NULL) {
+        strlib_free(caller->orig_called_number);
+    }
+
+    if (caller->last_redirect_name != NULL) {
+        strlib_free(caller->last_redirect_name);
+    }
+
+    if (caller->last_redirect_number) {
+        strlib_free(caller->last_redirect_number);
+    }
+
+    if (caller->orig_rpid_number != NULL) {
+        strlib_free(caller->orig_rpid_number);
+    }
+}
+
+/*
+ *  ROUTINE:     cc_free_msg_data
+ *
+ *  DESCRIPTION: Free resources embedded in CCAPI msg. data
+ *               structure.
+ *
+ *  PARAMETERS:
+ *      data - pointer to cc_msg_t whose embedded resources
+ *             need to be freed.
+ *
+ *  RETURNS:
+ *      None
+ */
+void
+cc_free_msg_data (cc_msg_t *msg)
+{
+    cc_msgbody_info_t *msg_body = NULL;
+    cc_caller_id_t *caller_id = NULL;
+
+    if (msg == NULL) {
+        return;
+    }
+    switch (msg->msg.setup.msg_id) {
+    case CC_MSG_SETUP:
+        msg_body = &msg->msg.setup.msg_body;
+        caller_id = &msg->msg.setup.caller_id;
+        strlib_free(msg->msg.setup.recv_info_list);
+        break;
+    case CC_MSG_SETUP_ACK:
+        msg_body = &msg->msg.setup_ack.msg_body;
+        caller_id = &msg->msg.setup_ack.caller_id;
+        break;
+    case CC_MSG_PROCEEDING:
+        caller_id = &msg->msg.proceeding.caller_id;
+        break;
+    case CC_MSG_ALERTING:
+        msg_body = &msg->msg.alerting.msg_body;
+        caller_id = &msg->msg.alerting.caller_id;
+        break;
+    case CC_MSG_CONNECTED:
+        msg_body = &msg->msg.connected.msg_body;
+        caller_id = &msg->msg.connected.caller_id;
+        strlib_free(msg->msg.connected.recv_info_list);
+        break;
+    case CC_MSG_CONNECTED_ACK:
+        msg_body = &msg->msg.connected_ack.msg_body;
+        caller_id = &msg->msg.connected_ack.caller_id;
+        break;
+    case CC_MSG_FEATURE:
+        if (msg->msg.feature.data_valid) {
+            msg_body = cc_get_msg_body_info_ptr_from_feature_data(
+                               msg->msg.feature.feature_id,
+                               &msg->msg.feature.data);
+
+            if (msg->msg.feature.feature_id == CC_FEATURE_CALLINFO) {
+                caller_id = &msg->msg.feature.data.call_info.caller_id;
+            }
+        }
+        break;
+    case CC_MSG_FEATURE_ACK:
+        if (msg->msg.feature_ack.data_valid) {
+            msg_body = cc_get_msg_body_info_ptr_from_feature_data(
+                               msg->msg.feature_ack.feature_id,
+                               &msg->msg.feature_ack.data);
+        }
+        break;
+    case CC_MSG_OPTIONS_ACK:
+        msg_body = &msg->msg.options_ack.msg_body;
+        break;
+    case CC_MSG_AUDIT_ACK:
+        msg_body = &msg->msg.audit_ack.msg_body;
+        break;
+    case CC_MSG_INFO:
+        strlib_free(msg->msg.info.message_body);
+        strlib_free(msg->msg.info.content_type);
+        strlib_free(msg->msg.info.info_package);
+        break;
+    default:
+        return;
+    }
+    cc_free_msg_body_parts(msg_body);
+    cc_free_caller_id(caller_id);
+}
+
+/*
+ *  ROUTINE:     cc_cp_msg_body_parts
+ *
+ *  DESCRIPTION: Copy elements in the msg body part structure.
+ *
+ *  PARAMETERS:
+ *      dst_msg - pointer to destination cc_msgbody_info_t
+ *      src_msg - pointer to source cc_msgbody_info_t
+ *
+ *  RETURNS:
+ *      CC_RC_ERROR
+ *      CC_RC_SUCCESS
+ *
+ *  NOTES:       The function attempts to free the message bodies
+ *               that might be in the destination to prevent
+ *               memory leak from overridden the destination by
+ *               moving the new message source.
+ *
+ *               The dst_msg must be pointed to the initialized
+ *               message block either with all zero or at least
+ *               number of parts is set to zero to prevent
+ *               freeing an invalid address.
+ */
+cc_rcs_t
+cc_cp_msg_body_parts (cc_msgbody_info_t *dst_msg, cc_msgbody_info_t *src_msg)
+{
+    uint32_t        i, body_length;
+    cc_msgbody_t   *src_part, *dst_part;
+    cc_rcs_t        status;
+    int             len;
+
+    if ((dst_msg == NULL) || (src_msg == NULL)) {
+        return CC_RC_ERROR;
+    }
+    /* Free the msg. bodies that might be in the dest first */
+    cc_free_msg_body_parts(dst_msg);
+
+    src_part = &src_msg->parts[0];
+    dst_part = &dst_msg->parts[0];
+
+    /* Copy all of the fields of all body parts */
+    status = CC_RC_SUCCESS;
+    for (i = 0; i < src_msg->num_parts; i++, src_part++, dst_part++) {
+        dst_part->content_type = src_part->content_type;
+        dst_part->content_disposition = src_part->content_disposition;
+
+        /* Copy body */
+        dst_part->body = NULL;
+        dst_part->body_length = 0;
+        if ((src_part->body != NULL) && (src_part->body_length > 0)) {
+            body_length = src_part->body_length;
+            dst_part->body = (char *) cpr_malloc(body_length);
+
+            if (dst_part->body == NULL) {
+                /* Unable to allocate memory for body */
+                status = CC_RC_ERROR;
+                break;
+            } else {
+                /*
+                 * Copy body including NULL char but the length field
+                 * does not include the extra NULL char.
+                 */
+                memcpy(dst_part->body, src_part->body, body_length);
+                dst_part->body_length = src_part->body_length;
+            }
+        }
+
+        dst_part->content_id = NULL;
+        /* Copy content ID */
+        if (src_part->content_id != NULL) {
+            len = strlen(src_part->content_id) + 1;
+            dst_part->content_id = (char *) cpr_malloc(len);
+            if (dst_part->content_id != NULL) {
+                memcpy(dst_part->content_id, src_part->content_id, len);
+            } else {
+                /* Unable to allocate memory for content ID */
+                status = CC_RC_ERROR;
+                break;
+            }
+        }
+    }
+    dst_msg->num_parts = i;     /* number of part copied */
+    dst_msg->content_type = src_msg->content_type;
+
+    if (status != CC_RC_SUCCESS) {
+        /*
+         * Unable to duplicate the body parts, free all of the allocated
+         * resources
+         */
+        cc_free_msg_body_parts(dst_msg);
+    }
+    return status;
+}
+
+void
+cc_int_setup (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+              line_t line, cc_caller_id_t *caller_id,
+              cc_alerting_type alert_info, vcm_ring_mode_t alerting_ring,
+              vcm_tones_t alerting_tone, cc_redirect_t *redirect,
+              cc_call_info_t *call_info_p, boolean replaces,
+              string_t recv_info_list, cc_msgbody_info_t *msg_body)
+{
+    cc_setup_t *pmsg;
+
+    if (caller_id == NULL) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG("%s: caller id is NULL\n", __FUNCTION__);
+        return;
+    }
+
+    CC_DEBUG(DEB_L_C_F_PREFIX "    CGPD= %s, CGPN= %s,\n    CDPD= %s, CDPN= %s\n",
+                        DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__),
+             caller_id->calling_name, caller_id->calling_number,
+             caller_id->called_name, caller_id->called_number);
+
+    pmsg = (cc_setup_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id        = CC_MSG_SETUP;
+    pmsg->src_id        = src_id;
+    pmsg->call_id       = call_id;
+    pmsg->line          = line;
+    pmsg->alert_info    = alert_info;
+    pmsg->alerting_ring = alerting_ring;
+    pmsg->alerting_tone = alerting_tone;
+    cc_cp_caller(&pmsg->caller_id, caller_id);
+
+    pmsg->call_info.type = CC_FEAT_NONE;
+    if (call_info_p) {
+        pmsg->call_info = *call_info_p;
+    }
+
+    pmsg->replaces = replaces;
+
+    if (redirect != NULL) {
+        pmsg->redirect = *redirect;
+    }
+
+    /* Info Package */
+    if (recv_info_list && (*recv_info_list != '\0')) {
+        pmsg->recv_info_list = strlib_copy(recv_info_list);
+    } else {
+        pmsg->recv_info_list = strlib_empty();
+    }
+
+    /* Move body parts if there are any */
+    pmsg->msg_body.num_parts = 0;
+    cc_mv_msg_body_parts(&pmsg->msg_body, msg_body);
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_setup_ack (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                  line_t line, cc_caller_id_t *caller_id,
+                  cc_msgbody_info_t *msg_body)
+{
+    cc_setup_ack_t *pmsg;
+
+    pmsg = (cc_setup_ack_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_SETUP_ACK;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    if (caller_id != NULL) {
+        cc_cp_caller(&pmsg->caller_id, caller_id);
+    }
+    /* Move body parts if there are any */
+    pmsg->msg_body.num_parts = 0;
+    cc_mv_msg_body_parts(&pmsg->msg_body, msg_body);
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+}
+
+
+void
+cc_int_proceeding (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                   line_t line, cc_caller_id_t *caller_id)
+{
+    cc_proceeding_t *pmsg;
+
+    pmsg = (cc_proceeding_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_PROCEEDING;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    if (caller_id != NULL) {
+        cc_cp_caller(&pmsg->caller_id, caller_id);
+    }
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg(pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_alerting (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                 line_t line, cc_caller_id_t *caller_id,
+                 cc_msgbody_info_t *msg_body, boolean inband)
+{
+    cc_alerting_t *pmsg;
+
+
+    pmsg = (cc_alerting_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_ALERTING;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    if (caller_id != NULL) {
+        cc_cp_caller(&pmsg->caller_id, caller_id);
+    }
+    /* Move body parts if there are any */
+    pmsg->msg_body.num_parts = 0;
+    cc_mv_msg_body_parts(&pmsg->msg_body, msg_body);
+
+    pmsg->inband = inband;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+    CC_DEBUG(DEB_L_C_F_PREFIX "    inband= %d\n",
+               DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), inband);
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_connected (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                  line_t line, cc_caller_id_t *caller_id,
+                  string_t recv_info_list, cc_msgbody_info_t *msg_body)
+{
+    cc_connected_t *pmsg;
+
+    pmsg = (cc_connected_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_CONNECTED;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    if (caller_id != NULL) {
+        cc_cp_caller(&pmsg->caller_id, caller_id);
+    }
+
+    /* Info Package */
+    if (recv_info_list && (*recv_info_list != '\0')) {
+        pmsg->recv_info_list = strlib_copy(recv_info_list);
+    } else {
+        pmsg->recv_info_list = strlib_empty();
+    }
+
+    /* Move body parts if there are any */
+    pmsg->msg_body.num_parts = 0;
+    cc_mv_msg_body_parts(&pmsg->msg_body, msg_body);
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_connected_ack (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                      line_t line, cc_caller_id_t *caller_id,
+                      cc_msgbody_info_t *msg_body)
+{
+    cc_connected_ack_t *pmsg;
+
+    pmsg = (cc_connected_ack_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_CONNECTED_ACK;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    if (caller_id != NULL) {
+        cc_cp_caller(&pmsg->caller_id, caller_id);
+    }
+    /* Move body parts if there are any */
+    pmsg->msg_body.num_parts = 0;
+    cc_mv_msg_body_parts(&pmsg->msg_body, msg_body);
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_release (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                line_t line, cc_causes_t cause, const char *dialstring,
+                cc_kfact_t *kfactor)
+{
+    cc_release_t *pmsg;
+
+    if (dialstring == NULL) {
+        CC_DEBUG(DEB_L_C_F_PREFIX "    cause= %s\n",
+                       DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), cc_cause_name(cause));
+    } else {
+        CC_DEBUG(DEB_L_C_F_PREFIX "    cause= %s, dialstring= %s\n",
+                       DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), cc_cause_name(cause), dialstring);
+    }
+
+    pmsg = (cc_release_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_RELEASE;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    pmsg->cause   = cause;
+
+    if (dialstring) {
+        sstrncpy(pmsg->dialstring, dialstring, CC_MAX_DIALSTRING_LEN);
+    }
+    if (kfactor != NULL) {
+        sstrncpy(pmsg->kfactor.rxstats, kfactor->rxstats, CC_KFACTOR_STAT_LEN);
+        sstrncpy(pmsg->kfactor.txstats, kfactor->txstats, CC_KFACTOR_STAT_LEN);
+    }
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_release_complete (cc_srcs_t src_id, cc_srcs_t dst_id,
+                         callid_t call_id, line_t line, cc_causes_t cause,
+                         cc_kfact_t *kfactor)
+{
+    cc_release_complete_t *pmsg;
+
+
+    pmsg = (cc_release_complete_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_RELEASE_COMPLETE;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    pmsg->cause   = cause;
+    if (kfactor != NULL) {
+        sstrncpy(pmsg->kfactor.rxstats, kfactor->rxstats, CC_KFACTOR_STAT_LEN);
+        sstrncpy(pmsg->kfactor.txstats, kfactor->txstats, CC_KFACTOR_STAT_LEN);
+    }
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+    CC_DEBUG(DEB_L_C_F_PREFIX "    cause= %s\n",
+               DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), cc_cause_name(cause));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_feature2 (cc_msgs_t msg_id, cc_srcs_t src_id, cc_srcs_t dst_id,
+                 callid_t call_id, line_t line, cc_features_t feature_id,
+                 cc_feature_data_t *data)
+{
+    cc_feature_t *pmsg;
+    cc_msgbody_info_t *msg_body;
+
+    pmsg = (cc_feature_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG("%s: no buffer available for feat=%s\n", __FUNCTION__,
+                    cc_feature_name(feature_id));
+        return;
+    }
+
+    pmsg->msg_id     = msg_id;
+    pmsg->src_id     = src_id;
+    pmsg->call_id    = call_id;
+    pmsg->line       = line;
+    pmsg->feature_id = feature_id;
+    pmsg->data_valid = (data == NULL) ? (FALSE) : (TRUE);
+
+    if (pmsg->data_valid == TRUE) {
+        pmsg->data = *data;
+        /*
+         * For call Info feature, need to copy the caller ID
+         */
+        if (feature_id == CC_FEATURE_CALLINFO) {
+            /* Copy the caller ID */
+            cc_cp_caller(&pmsg->data.call_info.caller_id,
+                         &data->call_info.caller_id);
+        }
+        /*
+         * Clear the msg body from the source now since the msg. bodies
+         * has been transferred to to the CCAPI msg.
+         */
+        msg_body = cc_get_msg_body_info_ptr_from_feature_data(feature_id, data);
+        cc_initialize_msg_body_parts_info(msg_body);
+    }
+
+    if ((feature_id == CC_FEATURE_XFER) ||
+        (feature_id == CC_FEATURE_BLIND_XFER)) {
+        if (data != NULL) {
+            CC_DEBUG(DEB_L_C_F_PREFIX
+                     "method= %d, call_id= %d, cause= %s dialstring= %s\n",
+                     DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__),
+                                        data->xfer.method, data->xfer.target_call_id,
+                     cc_cause_name(data->xfer.cause), data->xfer.dialstring);
+        }
+    }
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_feature_name(feature_id));
+    CC_DEBUG(DEB_L_C_F_PREFIX "feature= %s, data= %p\n",
+               DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), cc_feature_name(feature_id), data);
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG("%s: unable to send msg for feat=%s\n", __FUNCTION__,
+                    cc_feature_name(feature_id));
+    }
+    return;
+}
+
+/*
+ *  Helper function for the next six functions that populates and sends
+ *  a feature message
+ *
+ */
+static void send_message_helper(
+    cc_msgs_t msg_id,
+    cc_srcs_t src_id,
+    cc_srcs_t dst_id,
+    callid_t call_id,
+    line_t line,
+    cc_features_t feature_id,
+    cc_feature_data_t *data,
+    string_t sdp,
+    cc_jsep_action_t action)
+{
+    cc_feature_t *pmsg;
+    cc_msgbody_info_t *msg_body;
+
+    pmsg = (cc_feature_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        GSM_ERR_MSG("%s: no buffer available for feat=%s\n", __FUNCTION__,
+                    cc_feature_name(feature_id));
+        return;
+    }
+
+    pmsg->msg_id     = msg_id;
+    pmsg->src_id     = src_id;
+    pmsg->call_id    = call_id;
+    pmsg->line       = line;
+    pmsg->feature_id = feature_id;
+    pmsg->data_valid = (data == NULL) ? (FALSE) : (TRUE);
+
+    if (msg_id == CC_MSG_SETLOCALDESC || msg_id == CC_MSG_SETREMOTEDESC) {
+        pmsg->action = action;
+    }
+
+    if (msg_id == CC_MSG_CREATEANSWER || msg_id == CC_MSG_SETLOCALDESC || msg_id == CC_MSG_SETREMOTEDESC) {
+        sstrncpy(pmsg->sdp, sdp, sizeof(pmsg->sdp));
+    }
+
+    if (pmsg->data_valid == TRUE) {
+        pmsg->data = *data;
+        /*
+         * For call Info feature, need to copy the caller ID
+         */
+        if (feature_id == CC_FEATURE_CALLINFO) {
+            /* Copy the caller ID */
+            cc_cp_caller(&pmsg->data.call_info.caller_id,
+                         &data->call_info.caller_id);
+        }
+        /*
+         * Clear the msg body from the source now since the msg. bodies
+         * has been transferred to to the CCAPI msg.
+         */
+        msg_body = cc_get_msg_body_info_ptr_from_feature_data(feature_id, data);
+        cc_initialize_msg_body_parts_info(msg_body);
+    }
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_feature_name(feature_id));
+    CC_DEBUG(DEB_L_C_F_PREFIX "feature= %s, data= %p\n",
+        DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), cc_feature_name(feature_id), data);
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG("%s: unable to send msg for feat=%s\n", __FUNCTION__,
+                    cc_feature_name(feature_id));
+    }
+
+    return;
+}
+
+void
+cc_createoffer (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                line_t line, cc_features_t feature_id, cc_feature_data_t *data)
+{
+    send_message_helper(CC_MSG_CREATEOFFER, src_id, dst_id, call_id, line,
+        feature_id, data, NULL, 0);
+
+    return;
+}
+
+
+void
+cc_createanswer (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                line_t line, cc_features_t feature_id, string_t sdp, cc_feature_data_t *data)
+{
+    send_message_helper(CC_MSG_CREATEANSWER, src_id, dst_id, call_id, line,
+        feature_id, data, sdp, 0);
+
+    return;
+}
+
+
+void cc_setlocaldesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
+                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp,  cc_feature_data_t *data)
+{
+    send_message_helper(CC_MSG_SETLOCALDESC, src_id, dst_id, call_id, line,
+        feature_id, data, sdp, action);
+
+    return;
+}
+
+void cc_setremotedesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
+                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp, cc_feature_data_t *data)
+{
+    send_message_helper(CC_MSG_SETREMOTEDESC, src_id, dst_id, call_id, line,
+        feature_id, data, sdp, action);
+
+    return;
+}
+
+void cc_localdesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
+                    cc_features_t feature_id, cc_feature_data_t *data)
+{
+    send_message_helper(CC_MSG_LOCALDESC, src_id, dst_id, call_id, line,
+        feature_id, data, NULL, 0);
+
+    return;
+}
+
+void cc_remotedesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
+                    cc_features_t feature_id, cc_feature_data_t *data)
+{
+    send_message_helper(CC_MSG_REMOTEDESC, src_id, dst_id, call_id, line,
+        feature_id, data, NULL, 0);
+
+    return;
+}
+
+
+void
+cc_int_feature_ack (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                    line_t line, cc_features_t feature_id,
+                    cc_feature_data_t *data, cc_causes_t cause)
+{
+    cc_feature_ack_t *pmsg;
+    cc_msgbody_info_t *msg_body;
+
+    pmsg = (cc_feature_ack_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id     = CC_MSG_FEATURE_ACK;
+    pmsg->src_id     = src_id;
+    pmsg->call_id    = call_id;
+    pmsg->line       = line;
+    pmsg->feature_id = feature_id;
+    pmsg->data_valid = (data == NULL) ? (FALSE) : (TRUE);
+
+    if (pmsg->data_valid == TRUE) {
+        pmsg->data = *data;
+        /*
+         * Clear the msg body from the source now since the msg. bodies
+         * has been transferred to to the CCAPI msg.
+         */
+        msg_body = cc_get_msg_body_info_ptr_from_feature_data(feature_id, data);
+        cc_initialize_msg_body_parts_info(msg_body);
+    }
+    pmsg->cause = cause;
+
+    if ((feature_id == CC_FEATURE_XFER) ||
+        (feature_id == CC_FEATURE_BLIND_XFER)) {
+        if (data != NULL) {
+            CC_DEBUG(DEB_L_C_F_PREFIX
+                     "method= %d, call_id= %d, cause= %s dialstring= %s\n",
+                     DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__),
+                                        data->xfer.method, data->xfer.target_call_id,
+                     cc_cause_name(data->xfer.cause), data->xfer.dialstring);
+        }
+    }
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+    CC_DEBUG(DEB_L_C_F_PREFIX "feature= %s, data= %p, cause= %s\n",
+                       DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), cc_feature_name(feature_id), data,
+             cc_cause_name(cause));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_offhook (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t prim_call_id,
+                cc_hold_resume_reason_e consult_reason, callid_t call_id,
+                line_t line, char *global_call_id, monitor_mode_t monitor_mode,
+                cfwdall_mode_t cfwdall_mode)
+{
+    cc_offhook_t *pmsg;
+
+    pmsg = (cc_offhook_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_OFFHOOK;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    if (global_call_id != NULL) {
+        sstrncpy(pmsg->global_call_id, global_call_id, CC_GCID_LEN);
+    }
+    pmsg->prim_call_id = prim_call_id;
+    pmsg->hold_resume_reason = consult_reason;
+    pmsg->monitor_mode = monitor_mode;
+    pmsg->cfwdall_mode = cfwdall_mode;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_line (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line)
+{
+    cc_line_t *pmsg;
+
+    pmsg = (cc_line_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_LINE;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_onhook (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t prim_call_id,
+               cc_hold_resume_reason_e consult_reason, callid_t call_id,
+               line_t line, boolean softkey, cc_onhook_reason_e active_list)
+{
+    cc_onhook_t *pmsg;
+
+    pmsg = (cc_onhook_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id       = CC_MSG_ONHOOK;
+    pmsg->src_id       = src_id;
+    pmsg->call_id      = call_id;
+    pmsg->line         = line;
+    pmsg->softkey      = softkey;
+    pmsg->prim_call_id = prim_call_id;
+    pmsg->hold_resume_reason = consult_reason;
+    pmsg->active_list  = active_list;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_digit_begin (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                    line_t line, int digit)
+{
+    cc_digit_begin_t *pmsg;
+
+    pmsg = (cc_digit_begin_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_DIGIT_BEGIN;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->digit   = digit;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_dialstring (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                   line_t line, const char *dialstring, const char *g_call_id,
+                   monitor_mode_t monitor_mode)
+{
+    cc_dialstring_t *pmsg;
+
+    if (dialstring == NULL) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG("%s: no dialstring\n", __FUNCTION__);
+        return;
+    }
+
+    CC_DEBUG(DEB_L_C_F_PREFIX "dialstring= %s\n",
+               DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__),dialstring);
+
+    pmsg = (cc_dialstring_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_DIALSTRING;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    sstrncpy(pmsg->dialstring, dialstring, CC_MAX_DIALSTRING_LEN);
+    sstrncpy(pmsg->g_call_id, g_call_id, CC_GCID_LEN);
+    pmsg->monitor_mode    = monitor_mode;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+
+void
+cc_int_mwi (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
+            boolean on, int type, int newCount, int oldCount, int hpNewCount, int hpOldCount)
+{
+    cc_mwi_t *pmsg;
+
+    pmsg = (cc_mwi_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_MWI;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    pmsg->msgSummary.on = on;
+    pmsg->msgSummary.type = type;
+    pmsg->msgSummary.newCount = newCount;
+    pmsg->msgSummary.oldCount = oldCount;
+    pmsg->msgSummary.hpNewCount = hpNewCount;
+    pmsg->msgSummary.hpOldCount = hpOldCount;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+    CC_DEBUG(DEB_L_C_F_PREFIX "    mwi status= %d\n new count= %d old count= %d",
+               DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), on, newCount, oldCount);
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+void
+cc_int_options_sdp_req (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                        line_t line, void *pMessage)
+{
+    cc_options_sdp_req_t *pmsg;
+
+    pmsg = (cc_options_sdp_req_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id   = CC_MSG_OPTIONS;
+    pmsg->src_id   = src_id;
+    pmsg->call_id  = call_id;
+    pmsg->line     = line;
+    pmsg->pMessage = pMessage;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+    CC_DEBUG(DEB_L_C_F_PREFIX " message ptr=%p\n",
+               DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), pMessage);
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+void
+cc_int_options_sdp_ack (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                        line_t line, void *pMessage, cc_msgbody_info_t *msg_body)
+{
+    cc_options_sdp_ack_t *pmsg;
+
+    pmsg = (cc_options_sdp_ack_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id   = CC_MSG_OPTIONS_ACK;
+    pmsg->src_id   = src_id;
+    pmsg->call_id  = call_id;
+    pmsg->line     = line;
+    pmsg->pMessage = pMessage;
+    /* Move body parts if there are any */
+    pmsg->msg_body.num_parts = 0;
+    cc_mv_msg_body_parts(&pmsg->msg_body, msg_body);
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+    CC_DEBUG(DEB_L_C_F_PREFIX " message ptr=%p\n",
+               DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), pMessage);
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+void
+cc_int_audit_sdp_req (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                      line_t line, boolean apply_ringout)
+{
+    cc_audit_sdp_req_t *pmsg;
+
+    pmsg = (cc_audit_sdp_req_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_AUDIT;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    pmsg->apply_ringout = apply_ringout;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+    CC_DEBUG(DEB_L_C_F_PREFIX "\n", DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+void
+cc_int_audit_sdp_ack (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                      line_t line, cc_msgbody_info_t *msg_body)
+{
+    cc_audit_sdp_ack_t *pmsg;
+
+    pmsg = (cc_audit_sdp_ack_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id  = CC_MSG_AUDIT_ACK;
+    pmsg->src_id  = src_id;
+    pmsg->call_id = call_id;
+    pmsg->line    = line;
+    /* Move body parts if there are any */
+    pmsg->msg_body.num_parts = 0;
+    cc_mv_msg_body_parts(&pmsg->msg_body, msg_body);
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+void
+cc_int_fail_fallback (cc_srcs_t src_id, cc_srcs_t dst_id, int rsp_type,
+                      cc_regmgr_rsp_e rsp_id, boolean waited)
+{
+    cc_regmgr_t *pmsg;
+
+
+    pmsg = (cc_regmgr_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id    = CC_MSG_FAILOVER_FALLBACK;
+    pmsg->src_id    = src_id;
+    pmsg->rsp_type  = rsp_type;
+    pmsg->rsp_id    = rsp_id;
+    pmsg->wait_flag = waited;
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, 0, 0, cc_msg_name(pmsg->msg_id));
+    CC_DEBUG(DEB_F_PREFIX "rsp_type= %s rsp_id= %s waited = %d\n",
+             DEB_F_PREFIX_ARGS(CC_API, __FUNCTION__),
+             rsp_type == RSP_START ? "RSP_START" : "RSP_COMPLETE",
+             rsp_id == CC_REG_FAILOVER_RSP  ? "REG_FAILOVER_RSP" : "REG_FALLBACK_RSP",
+             waited);
+
+    if (cc_send_cmd_msg(REG_MGR_STATE_CHANGE, (cprBuffer_t) pmsg,
+            sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+void
+cc_int_info (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+             line_t line, string_t info_package, string_t content_type,
+             string_t message_body)
+{
+    cc_info_t *pmsg;
+
+
+    pmsg = (cc_info_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        // nobody checks, CC_RC_ERROR, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_NO_MSG_BUFFER), __FUNCTION__);
+        return;
+    }
+
+    pmsg->msg_id       = CC_MSG_INFO;
+    pmsg->call_id      = call_id;
+    pmsg->line         = line;
+    pmsg->info_package = strlib_copy(info_package);
+    pmsg->content_type = strlib_copy(content_type);
+    pmsg->message_body = strlib_copy(message_body);
+
+    CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_msg_name(pmsg->msg_id));
+
+    if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
+        // nobody checks the return code, so generate error message
+        GSM_ERR_MSG(get_debug_string(CC_SEND_FAILURE), __FUNCTION__);
+    }
+    return;
+}
+
+void
+cc_init (void)
+{
+    /* Placeholder function for any initialization tasks */
+}
+
diff --git a/libs/sipcc/core/gsm/ccapi_strings.c b/libs/sipcc/core/gsm/ccapi_strings.c
new file mode 100644 (file)
index 0000000..23e6da7
--- /dev/null
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#define __CC_FEATURE_STRINGS__ // this is required to include feature strings.
+#define __CC_MESSAGES_STRINGS__ // this is required to include message strings.
+#define __CC_CAUSE_STRINGS__   // this is required to include cause strings.
+#include "text_strings.h"
+#include "ccapi.h"
+
+/**
+ * This function will be invoked by debug print functions.
+ *
+ * @param id - feature id
+ *
+ * @return feature name string.
+ */
+const char *cc_feature_name (cc_features_t id)
+{
+    if ((id <= CC_FEATURE_MIN) || (id >= CC_FEATURE_MAX)) {
+        return get_debug_string(GSM_UNDEFINED);
+    }
+
+    return cc_feature_names[id - CC_FEATURE_NONE];
+}
+
+
+/**
+ * This function will be invoked by debug print functions.
+ *
+ * @param id - cc msg id
+ *
+ * @return cc msg name string.
+ */
+const char *cc_msg_name (cc_msgs_t id)
+{
+    if ((id <= CC_MSG_MIN) || (id >= CC_MSG_MAX)) {
+        return get_debug_string(GSM_UNDEFINED);
+    }
+
+    return cc_msg_names[id];
+}
+
+/**
+ * This function will be invoked by debug print functions.
+ *
+ * @param id - cc cause id
+ *
+ * @return cc cause name string.
+ */
+const char *cc_cause_name (cc_causes_t id)
+{
+    if ((id <= CC_CAUSE_MIN) || (id >= CC_CAUSE_MAX)) {
+        return get_debug_string(GSM_UNDEFINED);
+    }
+
+    return cc_cause_names[id - CC_CAUSE_OK];
+}
+
diff --git a/libs/sipcc/core/gsm/dcsm.c b/libs/sipcc/core/gsm/dcsm.c
new file mode 100755 (executable)
index 0000000..0139e64
--- /dev/null
@@ -0,0 +1,723 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "cpr_errno.h"
+#include "phone.h"
+#include "phntask.h"
+#include "lsm.h"
+#include "ccapi.h"
+#include "phone_debug.h"
+#include "fim.h"
+#include "sdp.h"
+#include "debug.h"
+#include "gsm_sdp.h"
+#include "uiapi.h"
+#include "gsm.h"
+#include "prot_configmgr.h"
+#include "singly_link_list.h"
+
+typedef enum dcsm_state {
+    DCSM_S_MIN = -1,
+    DCSM_S_READY,
+    DCSM_S_WAITING,
+    DCSM_S_MAX
+} dcsm_state_e;
+
+#define DCSM_MAX_CALL_IDS     (LSM_MAX_CALLS)
+
+static struct dcsm_icb_t {
+    callid_t            call_ids[DCSM_MAX_CALL_IDS];
+    line_t              line;
+    int                 gsm_state;
+    sll_handle_t        s_msg_list;
+
+    dcsm_state_e        state;
+} dcsm_cb;
+
+static const char *dcsm_state_names[] = {
+    "DCSM_READY",
+    "DCSM_WAITING"
+};
+
+extern cc_int32_t g_dcsmDebug;
+
+static sm_rcs_t dcsm_wait_ev_feature_handling(void *event, int event_id);
+static sm_rcs_t dcsm_wait_ev_offhook_handling(void *event, int event_id);
+static sm_rcs_t dcsm_wait_ev_dialstring_handling(void *event, int event_id);
+
+typedef sm_rcs_t (*pdcsm_sm_evt_handler)(void *event, int event_id);
+
+typedef struct _dcsm_table_t {
+    int min_state;
+    int max_state;
+    int min_event;
+    int max_event;
+    pdcsm_sm_evt_handler *table;
+} dcsm_table_t;
+
+
+static pdcsm_sm_evt_handler dcsm_function_table[DCSM_S_MAX][CC_MSG_MAX] =
+{
+/* DCSM_S_READY ------------------------------------------------------------ */
+    {
+    /* DCSM_E_SETUP            */ NULL,
+    /* DCSM_E_SETUP_ACK        */ NULL,
+    /* DCSM_E_PROCEEDING       */ NULL,
+    /* DCSM_E_ALERTING         */ NULL,
+    /* DCSM_E_CONNECTED        */ NULL,
+    /* DCSM_E_CONNECTED_ACK    */ NULL,
+    /* DCSM_E_RELEASE          */ NULL,
+    /* DCSM_E_RELEASE_COMPLETE */ NULL,
+    /* DCSM_E_FEATURE          */ NULL,
+    /* DCSM_E_FEATURE_ACK      */ NULL,
+    /* DCSM_E_OFFHOOK          */ NULL,
+    /* DCSM_E_ONHOOK           */ NULL,
+    /* DCSM_E_LINE             */ NULL,
+    /* DCSM_E_DIGIT_BEGIN      */ NULL,
+    /* DCSM_E_DIGIT_END        */ NULL,
+    /* DCSM_E_DIALSTRING       */ NULL,
+    /* DCSM_E_MWI              */ NULL,
+    /* DCSM_E_SESSION_AUDIT    */ NULL
+    },
+
+/* DCSM_S_WAITING----------------------------------------------------- */
+    {
+    /* DCSM_E_SETUP            */ NULL,
+    /* DCSM_E_SETUP_ACK        */ NULL,
+    /* DCSM_E_PROCEEDING       */ NULL,
+    /* DCSM_E_ALERTING         */ NULL,
+    /* DCSM_E_CONNECTED        */ NULL,
+    /* DCSM_E_CONNECTED_ACK    */ NULL,
+    /* DCSM_E_RELEASE          */ NULL,
+    /* DCSM_E_RELEASE_COMPLETE */ NULL,
+    /* DCSM_E_FEATURE          */ dcsm_wait_ev_feature_handling,
+    /* DCSM_E_FEATURE_ACK      */ NULL,
+    /* DCSM_E_OFFHOOK          */ dcsm_wait_ev_offhook_handling,
+    /* DCSM_E_ONHOOK           */ NULL,
+    /* DCSM_E_LINE             */ NULL,
+    /* DCSM_E_DIGIT_BEGIN      */ NULL,
+    /* DCSM_E_DIGIT_END        */ NULL,
+    /* DCSM_E_DIALSTRING       */ dcsm_wait_ev_dialstring_handling,
+    /* DCSM_E_MWI              */ NULL,
+    /* DCSM_E_SESSION_AUDIT    */ NULL
+    },
+};
+
+static dcsm_table_t dcsm_sm_table;
+static dcsm_table_t *pdcsm_sm_table = &dcsm_sm_table;
+
+/*
+ * Adds event to the dcsm queue.
+ *
+ *  @param event - Call control event to add
+ *
+ *  @return TRUE if the event is added.
+
+ *
+ */
+static boolean
+dcsm_add_event_to_queue (void *event)
+{
+    (void) sll_append(dcsm_cb.s_msg_list, event);
+
+    return TRUE;
+}
+
+/*
+ * Get a event from the queue.
+ *
+ *  @param none
+ *
+ *  @return pointer to the event.
+
+ *
+ */
+static void *
+dcsm_get_first_event_from_queue (void)
+{
+    void *msg_ptr = NULL;
+
+    msg_ptr = sll_next(dcsm_cb.s_msg_list, NULL);
+
+    sll_remove(dcsm_cb.s_msg_list, msg_ptr);
+
+    return(msg_ptr);
+}
+
+/*
+ * Get dcsm state name.
+ *
+ *  @param int - state id
+ *
+ *  @return ponter to the state name either
+ *  DCSM_READY or DCSM_WAITING.
+ *
+ */
+const char *dcsm_get_state_name (int state)
+{
+    if ((state <= DCSM_S_MIN) ||
+            (state >= DCSM_S_MAX)) {
+        return (get_debug_string(GSM_UNDEFINED));
+    }
+
+    return dcsm_state_names[state];
+}
+
+/**
+ * Feed the event to FIM state machine a direct function call
+ *  to process message retrieved from the queue.
+ *
+ * @param void * - pointer to the message to be processed
+ *
+ * @return none.
+ */
+static void dcsm_process_event_to_gsm (void *msg_ptr)
+{
+    if (fim_process_event(msg_ptr, FALSE) == TRUE) {
+
+        fim_free_event(msg_ptr);
+
+        /* Release buffer too */
+        cpr_free(msg_ptr);
+    }
+}
+
+
+/**
+ * Process ready state events.
+ *
+ * @param none
+ *
+ * @return none.
+ */
+
+static void dcsm_do_ready_state_job (void)
+{
+    static const char fname[] = "dcsm_do_ready_state_job";
+    void            *msg_ptr;
+    int             event_id;
+    cc_feature_t    *feat_msg = NULL;
+    callid_t        call_id = CC_NO_CALL_ID;
+
+    if (dcsm_cb.state != DCSM_S_READY) {
+        DEF_DEBUG(DEB_F_PREFIX": not in ready state.\n",
+            DEB_F_PREFIX_ARGS("DCSM", fname));
+        return;
+    }
+
+    msg_ptr = dcsm_get_first_event_from_queue();
+
+    /* Check if there is any msg available */
+    if (msg_ptr != NULL) {
+        event_id = (int)(((cc_setup_t *)msg_ptr)->msg_id);
+        if (event_id == CC_MSG_FEATURE) {
+            feat_msg = (cc_feature_t *) msg_ptr;
+            if (feat_msg != NULL) {
+                call_id = feat_msg->call_id;
+            }
+        }
+
+        DEF_DEBUG(DEB_F_PREFIX"%d: event (%s%s)\n",
+            DEB_F_PREFIX_ARGS("DCSM", fname), call_id,
+                     cc_msg_name((cc_msgs_t)(event_id)),
+                     feat_msg ? cc_feature_name(feat_msg->feature_id):" ");
+        dcsm_process_event_to_gsm(msg_ptr);
+    }
+}
+
+/**
+ * Function process events based on the state of DCSM.
+ *
+ * @param none
+ *
+ * @return none.
+ */
+void dcsm_process_jobs (void)
+{
+    dcsm_do_ready_state_job();
+}
+
+/**
+ * Adds call_id to the list of call_id's which made DCSM to move
+ *  to waiting state.
+ *
+ * @param callid_t - call id that has to be added to the list
+ *
+ * @return none.
+ */
+static void dcsm_add_call_id_to_list (callid_t call_id)
+{
+    static const char fname[] = "dcsm_add_call_id_to_list";
+    int i, loc = -1;
+
+    for (i=0; i< DCSM_MAX_CALL_IDS; i++) {
+        if (dcsm_cb.call_ids[i] == CC_NO_CALL_ID) {
+            loc = i;
+        } else if (dcsm_cb.call_ids[i] == call_id) {
+            //Call_id already present so do not try to add again
+            return;
+        }
+    }
+
+    if (loc == -1) {
+
+        /* Should never happen as there is a space to store call_id
+         * for each calls
+        */
+        DCSM_ERROR(DCSM_F_PREFIX"DCSM No space to store call_id.\n",
+                                    DEB_F_PREFIX_ARGS("DCSM", fname));
+        return;
+    }
+
+    dcsm_cb.call_ids[loc] = call_id;
+}
+
+/**
+ * Remove call_id from the list and see if the call_ids list is null.
+ *
+ * @param callid_t - call id that to be removed from the list.
+ *
+ * @return TRUE - if the call_ids list is empty.
+ *          FALSE - call_ids list is not empty.
+ */
+static boolean dcsm_remove_check_for_empty_list (callid_t call_id)
+{
+    int         i;
+    boolean      call_id_present = FALSE;
+
+    for (i=0; i< DCSM_MAX_CALL_IDS; i++) {
+        if (dcsm_cb.call_ids[i] == call_id) {
+            dcsm_cb.call_ids[i] = CC_NO_CALL_ID;
+
+            /* Found out that other call_id exist by setting
+             * call_id_preset, so return true, don't have to
+             * continue
+             */
+            if (call_id_present == TRUE) {
+                return FALSE;
+            }
+
+        } else if (dcsm_cb.call_ids[i] != CC_NO_CALL_ID) {
+            call_id_present = TRUE;
+        }
+    }
+
+    if (call_id_present == TRUE) {
+        return(FALSE);
+    }
+
+    return(TRUE);
+}
+
+
+/*
+ *      The function responsible for setting gsm state machine.
+ *
+ *  @param callid_t - call id of the call
+ *         state - current GSM state
+ *
+ *  @return void
+ *
+ */
+void
+dcsm_update_gsm_state (fsm_fcb_t *fcb, callid_t call_id, int state)
+{
+    int    last_state;
+    static const char fname[] = "dcsm_update_gsm_state";
+    fsmdef_dcb_t *dcb;
+
+    if (fcb->fsm_type != FSM_TYPE_DEF) {
+        DEF_DEBUG(DEB_F_PREFIX"%d: Not handling for %s\n",
+                  DEB_F_PREFIX_ARGS("DCSM", fname), call_id,
+                  fsm_type_name(fcb->fsm_type));
+        return;
+    }
+
+    last_state = dcsm_cb.state;
+
+    switch (state) {
+        case FSMDEF_S_RELEASING:
+            dcb = fsmdef_get_dcb_by_call_id(call_id);
+            if (dcb && dcb->send_release == FALSE) {
+                /* This call is already released from SIP persepctive. so no need to wait */
+                break;
+            }
+        case FSMDEF_S_CONNECTING:
+        case FSMDEF_S_HOLD_PENDING:
+        case FSMDEF_S_RESUME_PENDING:
+            dcsm_add_call_id_to_list(call_id);
+
+            dcsm_cb.state = DCSM_S_WAITING;
+            break;
+        case FSMDEF_S_MIN:
+        case FSMDEF_S_IDLE:
+        case FSMDEF_S_COLLECT_INFO:
+        case FSMDEF_S_CALL_SENT:
+        case FSMDEF_S_OUTGOING_PROCEEDING:
+        case FSMDEF_S_KPML_COLLECT_INFO:
+        case FSMDEF_S_OUTGOING_ALERTING:
+        case FSMDEF_S_INCOMING_ALERTING:
+        case FSMDEF_S_JOINING:
+        case FSMDEF_S_CONNECTED:
+        case FSMDEF_S_CONNECTED_MEDIA_PEND:
+        case FSMDEF_S_HOLDING:
+        case FSMDEF_S_PRESERVED:
+        case FSMDEF_S_MAX:
+            /* If there are no other call_id then move it to
+             * ready state else, it will remain in waiting
+             * state
+             */
+            if (dcsm_remove_check_for_empty_list(call_id) == TRUE) {
+                dcsm_cb.state = DCSM_S_READY;
+                /* Check if there are any pending events in the queue
+                 * if so send a DCSM_EV_READY to the GSM so dcsm will
+                 * get a chance to execute.
+                 */
+                if (sll_count(dcsm_cb.s_msg_list) > 0 ) {
+                    if (gsm_send_msg(DCSM_EV_READY, NULL, 0) == CPR_FAILURE) {
+                        DCSM_ERROR(DCSM_F_PREFIX"send DCSM_EV_READY ERROR.\n",
+                                        DEB_F_PREFIX_ARGS(DCSM, fname));
+                    }
+                }
+            }
+
+            break;
+        default:
+            break;
+    }
+
+    DEF_DEBUG(DEB_F_PREFIX"%d : %s --> %s\n",
+            DEB_F_PREFIX_ARGS("DCSM", fname), call_id,
+            dcsm_get_state_name(last_state),
+            dcsm_get_state_name(dcsm_cb.state));
+    return;
+}
+
+/**
+ *
+ * Feature passed through when state machine is in waiting state
+ *
+ * @param sm_event_t
+ *
+ * @return  sm_rcs_t
+ *
+ * @pre     (none)
+ */
+static sm_rcs_t
+dcsm_wait_ev_offhook_handling (void *event, int event_id)
+{
+    static const char fname[] = "dcsm_wait_ev_offhook_handling";
+
+    DEF_DEBUG(DEB_F_PREFIX": offhook\n",
+                     DEB_F_PREFIX_ARGS("DCSM", fname));
+
+    dcsm_add_event_to_queue(event);
+    return (SM_RC_END);
+}
+/**
+ *
+ * Feature passed through when state machine is in waiting state
+ *
+ * @param sm_event_t
+ *
+ * @return  sm_rcs_t
+ *
+ * @pre     (none)
+ */
+static sm_rcs_t
+dcsm_wait_ev_feature_handling (void *event, int event_id)
+{
+    static const char fname[] = "dcsm_wait_ev_feature_handling";
+    cc_feature_t     *feat_msg     = (cc_feature_t *) event;
+    sm_rcs_t   rc = SM_RC_END;
+    cc_features_t      ftr_id = CC_FEATURE_UNDEFINED;
+    callid_t           call_id = CC_NO_CALL_ID;
+
+    if (feat_msg != NULL) {
+        ftr_id  = feat_msg->feature_id;
+        call_id = feat_msg->call_id;
+    }
+
+    DEF_DEBUG(DEB_F_PREFIX"%d: id= %s%s\n",
+                     DEB_F_PREFIX_ARGS("DCSM", fname), call_id,
+                    cc_msg_name((cc_msgs_t)(event_id)),
+                     feat_msg ? cc_feature_name(feat_msg->feature_id):" ");
+
+    switch (ftr_id) {
+        case CC_FEATURE_ANSWER:
+        case CC_FEATURE_NEW_CALL:
+        case CC_FEATURE_REDIAL:
+        case CC_FEATURE_RESUME:
+        case CC_FEATURE_JOIN:
+            dcsm_add_event_to_queue(event);
+            DEF_DEBUG(DEB_F_PREFIX"%d: Event queued\n",
+                     DEB_F_PREFIX_ARGS("DCSM", fname), call_id);
+            rc = SM_RC_END;
+            break;
+        default:
+            DEF_DEBUG(DEB_F_PREFIX"%d: Feature msg not handled\n",
+                     DEB_F_PREFIX_ARGS("DCSM", fname), call_id);
+
+            rc = SM_RC_CONT;
+            break;
+    }
+
+
+    return (rc);
+
+}
+
+/**
+ *
+ * Feature passed through when state machine is in waiting state
+ *
+ * @param sm_event_t
+ *
+ * @return  sm_rcs_t
+ *
+ * @pre     (none)
+ */
+static sm_rcs_t
+dcsm_wait_ev_dialstring_handling (void *event, int event_id)
+{
+    static const char fname[] = "dcsm_wait_ev_dialstring_handling";
+
+    DEF_DEBUG(DEB_F_PREFIX": dialstring\n",
+                     DEB_F_PREFIX_ARGS("DCSM", fname));
+
+    dcsm_add_event_to_queue(event);
+    return (SM_RC_END);
+}
+
+/**
+ *
+ * Feature passed through when state machine is in waiting state
+ *
+ * @param sm_event_t
+ *
+ * @return  sm_rcs_t
+ *
+ * @pre     (none)
+ */
+sm_rcs_t
+dcsm_process_event (void *event, int event_id)
+{
+    static const char fname[] = "dcsm_process_event";
+    callid_t   call_id;
+    int        state_id;
+    sm_rcs_t   rc       = SM_RC_CONT;
+    fsm_fcb_t *fcb      = (fsm_fcb_t *) event;
+    cc_feature_t  *feat_msg = NULL;
+    pdcsm_sm_evt_handler hdlr; /* cached handler in order to compute its addr once */
+
+    call_id  = fcb->call_id;
+
+    if (event_id == CC_MSG_FEATURE) {
+        feat_msg = (cc_feature_t *) event;
+
+        if (feat_msg != NULL){
+            call_id = feat_msg->call_id;
+        }
+    }
+
+    DEF_DEBUG(DEB_F_PREFIX"DCSM %-4d:(%s:%s%s)\n",
+                     DEB_F_PREFIX_ARGS("DCSM", fname), call_id,
+                     dcsm_get_state_name(dcsm_cb.state),
+                     cc_msg_name((cc_msgs_t)(event_id)),
+                     feat_msg ? cc_feature_name(feat_msg->feature_id):" ");
+
+    state_id = dcsm_cb.state; // Get current state;
+
+   /*
+     * validate the state and event
+     * and that there is a valid function for this state-event pair.
+     */
+    if ((state_id > pdcsm_sm_table->min_state) &&
+        (state_id < pdcsm_sm_table->max_state) &&
+        (event_id > pdcsm_sm_table->min_event) &&
+        (event_id < pdcsm_sm_table->max_event)) {
+
+        if ((hdlr = pdcsm_sm_table->table[pdcsm_sm_table->max_event * state_id +
+                            event_id]) != NULL) {
+            DEF_DEBUG(DEB_F_PREFIX"%-4d: dcsm entry: (%s)\n",
+                         DEB_F_PREFIX_ARGS("DCSM", fname), call_id,
+                     cc_msg_name((cc_msgs_t)(event_id)));
+
+            rc = hdlr(event, event_id);
+        }
+
+    }
+
+    return (rc);
+
+}
+
+/*
+ *      Function responsible for searching the list.
+ *
+ *  @param cac_data_t *key_p - pointer to the key.
+ *  @param cac_data_t *cac_data - cac data.
+ *
+ *  @return void
+ *
+ */
+static sll_match_e
+dcsm_match_event (void *key_p, void *msg_data)
+{
+    return SLL_MATCH_FOUND;
+}
+
+/**
+ *
+ * Show function for DCSM
+ *
+ * @param argc - number of parameter passed
+ *        argv - argument passed
+ *
+ * @return     0 - if function successful
+ */
+cc_int32_t
+dcsm_show_cmd (cc_int32_t argc, const char *argv[])
+{
+    void *msg_ptr;
+    int i;
+    cc_setup_t     *msg;
+    cc_msgs_t       msg_id;
+    line_t    line;
+    callid_t  call_id;
+    cc_feature_t  *feat_msg = NULL;
+
+
+    /*
+     * check if need help
+     */
+    if ((argc == 2) && (argv[1][0] == '?')) {
+        debugif_printf("show dcsm\n");
+        return (0);
+    }
+
+
+    if (dcsm_cb.s_msg_list == NULL) {
+        return(0);
+    }
+
+    debugif_printf("\n-------------------------- DCSM Data --------------------------");
+    debugif_printf("\nDCSM State = %s",dcsm_get_state_name(dcsm_cb.state));
+    debugif_printf("\nDCSM waiting calls \n");
+
+    for (i=0; i< DCSM_MAX_CALL_IDS; i++) {
+        if (dcsm_cb.call_ids[i] != CC_NO_CALL_ID) {
+            debugif_printf("%d ", dcsm_cb.call_ids[i]);
+        }
+    }
+    debugif_printf("\n");
+
+    debugif_printf("\nDCSM waiting events \n");
+    i = 0;
+    msg_ptr = sll_next(dcsm_cb.s_msg_list, NULL);
+    while (msg_ptr) {
+        msg_ptr = sll_next(dcsm_cb.s_msg_list, msg_ptr);
+
+        if (msg_ptr) {
+            msg = (cc_setup_t *) msg_ptr;
+            msg_id   = msg->msg_id;
+            call_id  = msg->call_id;
+            line     = msg->line;
+            if ((int)msg_id == CC_MSG_FEATURE) {
+                feat_msg = (cc_feature_t *) msg_ptr;
+            }
+
+            debugif_printf("Event %d (%d/%d): (%s%s)\n",
+                       i++, line, call_id, cc_msg_name(msg_id),
+                        feat_msg ? cc_feature_name(feat_msg->feature_id):" ");
+        }
+    }
+    debugif_printf("\n-------------------------- DCSM Data Done-----------------------");
+
+    return (0);
+}
+
+
+/**
+ *
+ * Initialize dcsm state machine.
+ *
+ * @param none
+ *
+ * @return  none
+ *
+ * @pre     none
+ */
+void
+dcsm_init (void)
+{
+    static const char fname[] = "dcsm_init";
+    int i;
+
+    /*
+     * Initialize the state/event table.
+     */
+    dcsm_sm_table.min_state = DCSM_S_MIN;
+    dcsm_sm_table.max_state = DCSM_S_MAX;
+    dcsm_sm_table.min_event = CC_MSG_MIN;
+    dcsm_sm_table.max_event = CC_MSG_MAX;
+    dcsm_sm_table.table     = (&(dcsm_function_table[0][0]));
+
+    dcsm_cb.state = DCSM_S_READY;
+
+    for (i=0; i< DCSM_MAX_CALL_IDS; i++) {
+        dcsm_cb.call_ids[i] = CC_NO_CALL_ID;
+    }
+
+    /* allocate and initialize cac list */
+    dcsm_cb.s_msg_list = sll_create((sll_match_e(*)(void *, void *))
+                            dcsm_match_event);
+
+    if (dcsm_cb.s_msg_list == NULL) {
+        DCSM_ERROR(DCSM_F_PREFIX"DCSM CB creation failed.\n",
+                                    DEB_F_PREFIX_ARGS("DCSM", fname));
+
+    }
+
+}
+
+/**
+ *
+ * Shut down routine for dcsm state machine.
+ *
+ * @param none
+ *
+ * @return  none
+ *
+ * @pre     none
+ */
+void
+dcsm_shutdown (void)
+{
+    void *msg_ptr;
+
+    if (dcsm_cb.s_msg_list == NULL) {
+        return;
+    }
+
+    msg_ptr = sll_next(dcsm_cb.s_msg_list, NULL);
+    while (msg_ptr) {
+        msg_ptr = sll_next(dcsm_cb.s_msg_list, msg_ptr);
+
+        if (msg_ptr) {
+            fim_free_event(msg_ptr);
+
+            /* Release buffer too */
+            cpr_free(msg_ptr);
+        }
+    }
+
+    sll_destroy(dcsm_cb.s_msg_list);
+    dcsm_cb.s_msg_list = NULL;
+}
+
diff --git a/libs/sipcc/core/gsm/fim.c b/libs/sipcc/core/gsm/fim.c
new file mode 100755 (executable)
index 0000000..e910db6
--- /dev/null
@@ -0,0 +1,761 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "phone.h"
+#include "fim.h"
+#include "lsm.h"
+#include "fsm.h"
+#include "sm.h"
+#include "ccapi.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "util_string.h"
+#include "sdp.h"
+#include "gsm_sdp.h"
+#include "ccsip_sdp.h"
+#include "platform_api.h"
+
+extern void set_next_sess_video_pref(int pref);
+extern sm_rcs_t dcsm_process_event(void *event, int event_id);
+
+#define FIM_MAX_CHNS (LSM_MAX_CALLS)
+#define FIM_MAX_SCBS (FSM_TYPE_MAX)
+#define FIM_MAX_ICBS (FSM_TYPE_MAX * FIM_MAX_CHNS)
+
+static fim_scb_t *fim_scbs;
+static fim_icb_t *fim_icbs;
+
+
+static void
+fim_mwi (cc_mwi_t *msg)
+{
+    cc_action_data_t data;
+
+    data.mwi.on = msg->msgSummary.on;
+    data.mwi.type = msg->msgSummary.type;
+    data.mwi.newCount = msg->msgSummary.newCount;
+    data.mwi.oldCount = msg->msgSummary.oldCount;
+    data.mwi.hpNewCount = msg->msgSummary.hpNewCount;
+    data.mwi.hpOldCount = msg->msgSummary.hpOldCount;
+
+    (void)cc_call_action(msg->call_id, msg->line, CC_ACTION_MWI, &data);
+}
+
+
+static void
+fim_init_call_chns (void)
+{
+    int          chn;
+    fsm_types_t  type;
+    fim_icb_t   *icb = NULL;
+    static const char fname[] = "fim_init_call_chns";
+
+    fim_scbs = (fim_scb_t *) cpr_calloc(FIM_MAX_SCBS, sizeof(fim_scb_t));
+    if (fim_scbs == NULL) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Failed to allocate FIM SCBs.\n", fname);
+        return;
+    }
+
+    fim_icbs = (fim_icb_t *) cpr_calloc(FIM_MAX_ICBS, sizeof(fim_icb_t));
+    if (fim_icbs == NULL) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Failed to allocate FIM ICBs.\n", fname);
+        cpr_free(fim_scbs);
+        fim_scbs = NULL;
+        return;
+    }
+
+    /*
+     * Initialize the icbs (fim control blocks).
+     */
+    icb = fim_icbs;
+    for (chn = 0; chn < FIM_MAX_CHNS; chn++) {
+        for (type = FSM_TYPE_HEAD; type < FSM_TYPE_MAX; type++, icb++) {
+            icb->call_id = CC_NO_CALL_ID;
+            icb->scb     = &(fim_scbs[type]);
+            icb->cb      = NULL;
+
+            /*
+             * Set the next_chn pointers if this is the head of the chain,
+             * Set non-head icbs and the last head to NULL.
+             */
+            if ((type == FSM_TYPE_HEAD) && (chn < (FIM_MAX_CHNS - 1))) {
+                icb->next_chn = icb + FSM_TYPE_MAX;
+            } else {
+                icb->next_chn = NULL;
+            }
+
+            /*
+             * Set the next_icb pointers if this icb is not the last
+             * one on the chain.
+             */
+            if (type < (FSM_TYPE_MAX - 1)) {
+                icb->next_icb = icb + 1;
+            } else {
+                icb->next_icb = NULL;
+            }
+        }                       /* for (jscb = FSM_TYPE_HEAD; i < FSM_TYPE_MAX; i++) { */
+    }                           /* for (ichn = 0; ichn < FIM_MAX_CHNS; ichn++) { */
+
+    /*
+     * Initialize the scbs (state machine control blocks).
+     */
+    icb = fim_icbs;
+    for (type = FSM_TYPE_HEAD; type < FSM_TYPE_MAX; type++, icb++) {
+        icb->scb->type = type;
+        fsm_init_scb(icb, CC_NO_CALL_ID);
+    }
+}
+
+static void
+fim_free_call_chn (fim_icb_t *call_chn, line_t line, boolean update_call_cnt)
+{
+    static const char fname[] = "fim_free_call_chn";
+    fim_icb_t *icb = call_chn;
+
+    FIM_DEBUG(get_debug_string(GSM_DBG_PTR), "FIM", call_chn->call_id, fname,
+              "call_chn", call_chn);
+
+    /*
+     * Go through the chain and free each icb.
+     */
+    for (icb = call_chn; icb != NULL; icb = icb->next_icb) {
+        if (icb->scb->free_cb != NULL) {
+            icb->scb->free_cb(icb, icb->call_id);
+        }
+        icb->call_id = CC_NO_CALL_ID;
+        icb->cb = NULL;
+    }
+    if (update_call_cnt == TRUE) {
+        lsm_decrement_call_chn_cnt(line);
+    }
+    else {
+        FIM_DEBUG(get_debug_string(GSM_DBG_PTR), "lsm not decremented", call_chn->call_id, fname,
+              "call_chn", call_chn);
+    }
+}
+
+
+static fim_icb_t *
+fim_get_call_chn_by_call_id (callid_t call_id)
+{
+    static const char fname[] = "fim_get_call_chn_by_call_id";
+    fim_icb_t      *call_chn = NULL;
+    fim_icb_t      *icb = NULL;
+
+    for (icb = fim_icbs; icb != NULL; icb = icb->next_chn) {
+        if (icb->call_id == call_id) {
+            call_chn = icb;
+            break;
+        }
+    }
+
+    FIM_DEBUG(get_debug_string(GSM_DBG_PTR), "FIM", call_id, fname, "chn",
+              call_chn);
+
+    return call_chn;
+}
+
+static fim_icb_t *
+fim_get_new_call_chn (callid_t call_id)
+{
+    static const char fname[] = "fim_get_new_call_chn";
+    fim_icb_t      *call_chn = NULL;
+    fim_icb_t      *icb = NULL;
+
+    /*
+     * Verify that this call_id is not already used.
+     */
+    call_chn = fim_get_call_chn_by_call_id(call_id);
+    if (call_chn != NULL) {
+        FIM_DEBUG(get_debug_string(GSM_DBG1), "FIM", call_id, fname,
+                  "call_id in use");
+
+        return NULL;
+    }
+
+    /*
+     * Construct a new call chain.
+     */
+    call_chn = fim_get_call_chn_by_call_id(CC_NO_CALL_ID);
+    if (call_chn == NULL) {
+        /*
+         * No more free call_chns are available.
+         */
+        FIM_DEBUG(get_debug_string(GSM_DBG1), "FIM", call_id, fname,
+                  "no free call_chns");
+
+        return NULL;
+    }
+
+    call_chn->call_id = call_id;
+    call_chn->ui_locked = FALSE;
+
+    /*
+     * Set the control blocks for the icbs.
+     */
+    for (icb = call_chn; icb != NULL; icb = icb->next_icb) {
+        FIM_DEBUG(get_debug_string(GSM_DBG1), "FIM", call_id, fname,
+                  fsm_type_name(icb->scb->type));
+
+        if (icb->scb->get_cb) {
+            icb->scb->get_cb(icb, call_id);
+            icb->call_id = call_id;
+            icb->ui_locked = FALSE;
+        }
+    }
+
+    FIM_DEBUG(get_debug_string(GSM_DBG_PTR), "FIM", call_chn->call_id, fname,
+              "call_chn", call_chn);
+
+    return call_chn;
+}
+
+void
+fim_free_event (void *data)
+{
+    cc_msg_t *msg = (cc_msg_t *) data;
+
+    /* Free CCAPI msg. data that are not consumed */
+    cc_free_msg_data(msg);
+}
+
+static void
+fim_process_options_msg (void *data)
+{
+    static const char fname[] = "fim_process_options_msg";
+    cc_sdp_t             *local_sdp_p = NULL;
+    cc_causes_t           cause = CC_CAUSE_MIN;
+    cc_msgbody_info_t     msg_body;
+    cc_options_sdp_req_t *options_msg = (cc_options_sdp_req_t *) data;
+
+    gsmsdp_create_options_sdp(&local_sdp_p);
+
+    cause = gsmsdp_encode_sdp(local_sdp_p, &msg_body);
+    if (cause != CC_CAUSE_OK) {
+        FIM_DEBUG(get_debug_string(GSM_DBG1), "FIM", options_msg->call_id,
+                  fname, "Unable to build SDP\n");
+    } else {
+        cc_int_options_sdp_ack(CC_SRC_GSM, CC_SRC_SIP,
+                               options_msg->call_id,
+                               options_msg->line,
+                               options_msg->pMessage, &msg_body);
+    }
+    sipsdp_src_dest_free(CCSIP_SRC_SDP_BIT, &local_sdp_p);
+}
+
+void
+fim_lock_ui (callid_t call_id)
+{
+    fim_icb_t *call_chn = fim_get_call_chn_by_call_id(call_id);
+
+    if (call_chn == NULL) {
+        FIM_DEBUG(DEB_F_PREFIX"unknown call id\n", DEB_F_PREFIX_ARGS(FIM, "fim_lock_ui"));
+        return;
+    }
+    call_chn->ui_locked = TRUE;
+}
+
+void
+fim_unlock_ui (callid_t call_id)
+{
+    fim_icb_t *call_chn = fim_get_call_chn_by_call_id(call_id);
+
+    if (call_chn == NULL) {
+        FIM_DEBUG(DEB_F_PREFIX"unknown call id\n", DEB_F_PREFIX_ARGS(FIM, "fim_unlock_ui"));
+        return;
+    }
+    call_chn->ui_locked = FALSE;
+}
+
+static boolean
+fsm_event_filtered_by_ui_lock (int event_id, cc_features_t feature_id)
+{
+    /*
+     * If UI has been locked by GSM due to pending states, we want
+     * to filter the events that come in from the UI. We will still
+     * allow all GSM and SIP stack originated events through to the
+     * GSM sm.
+     */
+    if (event_id == CC_MSG_ONHOOK) {
+        return FALSE;
+    }
+
+    if ((event_id == CC_MSG_FEATURE) &&
+        (feature_id == CC_FEATURE_END_CALL ||
+         feature_id == CC_FEATURE_REQ_PEND_TIMER_EXP ||
+         feature_id == CC_FEATURE_RESUME ||
+         feature_id == CC_FEATURE_HOLD)) {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/**
+ *
+ * Check if the feature event is generic feature
+ *
+ * @param cc_features_t        feature_id
+ *
+ * @return  TRUE if the feature is generic or else FALSE
+ *
+ * @pre     None
+ */
+boolean fim_is_app_generic_features (cc_features_t feat_id)
+{
+    return(FALSE);
+}
+
+/**
+ *
+ * Check if the message is the feature event that may requires a new
+ * call chain.
+ *
+ * @param msg  - pointer to cc_setup_t.
+ *
+ * @return  TRUE if the feature that may requires a new call chain.
+ *
+ * @pre     (msg != NULL)
+ */
+static boolean
+fim_check_feature_event (cc_setup_t *msg)
+{
+
+    if ((((cc_feature_t *)msg)->feature_id == CC_FEATURE_SELECT) ||
+        (((cc_feature_t *)msg)->feature_id == CC_FEATURE_DIRTRXFR) ||
+        (((cc_feature_t *)msg)->feature_id == CC_FEATURE_B2BCONF) ||
+        (((cc_feature_t *)msg)->feature_id == CC_FEATURE_CFWD_ALL)) {
+        return (TRUE);
+    }
+    return (FALSE);
+}
+
+/*
+ * fim_feature_need_outgoing_call_context
+ *
+ * Description:
+ * This function determines whther a new feature invokation needs
+ * an early outgoing call context created in the FIM main module or not.
+ *
+ * Parameters:
+       msg - pointer to the cc_setup_t.
+ *
+ * Returns:
+ *     TRUE  - the feature requires outgoing call context to be created.
+ *     FALSE - the feature does not requries outgoing call context to
+ *             be created during initial FIM displacthing.
+ */
+static boolean
+fim_feature_need_outgoing_call_context (cc_setup_t *msg)
+{
+    if (((cc_feature_t *)msg)->feature_id == CC_FEATURE_NOTIFY) {
+        /*
+         * these features do not need outgoing context to be created early
+         */
+        return (FALSE);
+    }
+    return (TRUE);
+}
+
+/**
+ *
+ * Process events received for GSM
+ *
+ * @param void       event data
+ *        cac_passed indicate if the event passed cac request.
+ *                   Usually set to true once the cac response is
+ *                   received so that it won't do another request.
+ *
+ * @return  true if message memory has to be released by caller
+ *          false if the message memory is not to be released by
+ *          caller.
+ *
+ * @pre     (data not_eq NULL)
+ * @pre     (CC_MSG_MIN < msg->msg_id <CC_MSG_MAX)
+ */
+boolean
+fim_process_event (void *data, boolean cac_passed)
+{
+    static const char fname[] = "fim_process_event";
+    cc_setup_t     *msg = (cc_setup_t *) data;
+    cc_msgs_t       msg_id   = msg->msg_id;
+    callid_t        call_id  = msg->call_id;
+    line_t          line     = msg->line;
+    int             event_id = msg_id;
+    sm_event_t      event;
+    fim_icb_t      *call_chn = NULL;
+    fim_icb_t      *icb      = NULL;
+    boolean         done     = FALSE;
+    sm_rcs_t        rc       = SM_RC_ERROR;
+    fim_cb_hdr_t   *cb_hdr   = NULL;
+    fsm_fcb_t      *fcb      = NULL;
+    cc_feature_t   *feat_msg = (cc_feature_t *) data;
+    cc_feature_data_t * feat_data = &(feat_msg->data);
+    boolean        update_call_cnt = TRUE;
+    uint32_t       no_of_session = 1;
+    callid_t       bw_call_id;
+
+    FIM_DEBUG(DEB_L_C_F_PREFIX"Msg name = %s\n", DEB_L_C_F_PREFIX_ARGS(FIM, line, call_id, fname),
+        cc_msg_name(msg_id));
+
+    /*
+     * Validate the incoming event.
+     */
+    if ((event_id <= CC_MSG_MIN) || (event_id >= CC_MSG_MAX)) {
+        cc_call_state(call_id, line, CC_STATE_UNKNOWN, NULL);
+        return(TRUE);
+    }
+
+    /* Make sure to process the device events
+     */
+
+    if (dcsm_process_event(data, event_id) == SM_RC_END) {
+        /* Keep the message in the dcsm state handler */
+        return(FALSE);
+    }
+
+    /*
+     * Grab non-call control events and hand them off to other functions that
+     * are not implemented by the fsms.
+     */
+    if (msg_id == CC_MSG_MWI) {
+        fim_mwi((cc_mwi_t *) msg);
+    }
+
+    if (event_id == CC_MSG_OPTIONS) {
+        fim_process_options_msg(data);
+        return(TRUE);
+    }
+
+
+    if (platWlanISActive() && cac_passed == FALSE) {
+        /* The WLAN will request for bandwidth only for the events received from
+         * UI or other external entity. For internal events there is allocated bandwidth
+         * and do not need to request again.
+        */
+
+        if ((msg->src_id != CC_SRC_GSM) &&
+        ((event_id == CC_MSG_SETUP)      ||
+        (event_id == CC_MSG_OFFHOOK)    ||
+        (event_id == CC_MSG_DIALSTRING) ||
+        (event_id == CC_MSG_LINE)       ||
+        ((event_id == CC_MSG_FEATURE) &&
+        ((((cc_feature_t *) msg)->feature_id == CC_FEATURE_NEW_CALL))))) {
+
+                bw_call_id = call_id;
+
+                if ((event_id == CC_MSG_SETUP) &&
+                        ((((cc_setup_t *)msg)->call_info.type == CC_FEAT_MONITOR))) {
+                    no_of_session = 2;
+                    bw_call_id = msg->call_info.data.join.join_call_id;
+                }
+
+                if (fsm_cac_call_bandwidth_req (bw_call_id, no_of_session, msg) != CC_CAUSE_OK) {
+                return(TRUE);
+            }
+        /* Do not release the msg once it returns from the call
+         */
+        return(FALSE);
+        }
+    }
+
+    if ((event_id == CC_MSG_FEATURE) && ((call_id == CC_NO_CALL_ID) ||
+        fim_is_app_generic_features(((cc_feature_t *) msg)->feature_id))) {
+        if (((cc_feature_t *)msg)->feature_id == CC_FEATURE_BLF_ALERT_TONE) {
+            (void)cc_call_action(0, 0, CC_ACTION_PLAY_BLF_ALERTING_TONE, NULL);
+        } else if (((cc_feature_t *)msg)->feature_id == CC_FEATURE_UPD_MEDIA_CAP) {
+            fsmdef_update_media_cap_feature_event(feat_msg);
+        }
+        return(TRUE);
+    }
+
+    /*
+     * Throw away any messages with call_id < 1 (which means they are invalid).
+     *
+     * The GSM will send messages back to itself with a call_id < 1 because
+     * it uses the NULL_DCB. The fsmxfr will use a NULL_DCB that has invalid
+     * data in it, but the DCB points to a valid DCB. This way, the fsmxfr does
+     * not have to keep checking for the NULL.
+     */
+    if (call_id < 1) {
+        return(TRUE);
+    }
+
+
+    /*
+    * Get the call chain associated with this call_id.
+    */
+    call_chn = fim_get_call_chn_by_call_id(call_id);
+
+    if (call_chn == NULL) {
+        /*
+         * No call chain, so get a new call chain,
+         * but only if the event is a call establishment event.
+         */
+        if ((event_id == CC_MSG_SETUP)      ||
+            (event_id == CC_MSG_OFFHOOK)    ||
+            (event_id == CC_MSG_DIALSTRING) ||
+            (event_id == CC_MSG_LINE)       ||
+            (event_id == CC_MSG_CREATEOFFER) ||
+            (event_id == CC_MSG_CREATEANSWER) ||
+            (event_id == CC_MSG_SETLOCALDESC) ||
+            (event_id == CC_MSG_SETREMOTEDESC) ||
+            (event_id == CC_MSG_SETPEERCONNECTION) ||
+            (event_id == CC_MSG_ADDSTREAM) ||
+            (event_id == CC_MSG_REMOVESTREAM) ||
+            (event_id == CC_MSG_ADDCANDIDATE) ||
+            ((event_id == CC_MSG_FEATURE) &&
+             ((((cc_feature_t *) msg)->feature_id == CC_FEATURE_NEW_CALL)))) {
+            call_chn = fim_get_new_call_chn(call_id);
+
+            /*
+             * Make sure we got a new call chain.
+             */
+            if (call_chn == NULL) {
+                FIM_DEBUG(get_debug_string(GSM_DBG1), "FIM", call_id, fname,
+                          "no call_chn");
+
+                fsm_display_no_free_lines();
+
+                cc_call_state(call_id, line, CC_STATE_UNKNOWN, NULL);
+                return(TRUE);
+            }
+
+        } else if ((event_id == CC_MSG_FEATURE) &&
+                   (fim_check_feature_event(msg))) {
+
+            call_chn = fim_get_new_call_chn(call_id);
+
+            /*
+             * Make sure we got a new call chain.
+             */
+            if (call_chn == NULL) {
+                FIM_DEBUG(get_debug_string(GSM_DBG1), "FIM", call_id, fname,
+                          "no call_chn");
+
+                fsm_display_no_free_lines();
+
+                cc_call_state(call_id, line, CC_STATE_UNKNOWN, NULL);
+                return(TRUE);
+            }
+
+            if (fim_feature_need_outgoing_call_context(msg)) {
+                /* Get new outgoing call context using DEF state m/c fsm */
+                if (fsm_get_new_outgoing_call_context(call_id, line,
+                        fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF),
+                        FALSE) != CC_CAUSE_OK) {
+                    cc_call_state(call_id, line, CC_STATE_UNKNOWN, NULL);
+                    return(TRUE);
+                }
+            }
+
+        } else {
+            FIM_DEBUG(get_debug_string(GSM_DBG1), "FIM", call_id, fname,
+                      "not a call establishment event\n");
+            if( feat_msg->feature_id == CC_FEATURE_UPD_SESSION_MEDIA_CAP) {
+                FIM_DEBUG(DEB_L_C_F_PREFIX"set_next_sess_video_pref = %d\n",
+                        DEB_L_C_F_PREFIX_ARGS(FIM, line, call_id, fname),
+                        feat_data->caps.support_direction);
+                set_next_sess_video_pref(feat_data->caps.support_direction);
+            }
+            return(TRUE);
+        }
+        if (event_id != CC_MSG_SETUP) {
+            /*
+             * Increment the call chain cnt for this line
+             * For incoming calls, we don't know the line number
+             * when SETUP msg is received, so it's an exception, it'll be
+             * added in fsmdef_idle_setup
+             */
+           lsm_increment_call_chn_cnt(line);
+        }
+    } /* if (call_chn == NULL) */
+
+
+    /*
+     * Pass the event to the call chain unless UI lock has been enabled.
+     */
+    if (call_chn->ui_locked && ((cc_feature_t *) msg)->src_id == CC_SRC_UI) {
+        if (fsm_event_filtered_by_ui_lock(event_id,
+                ((cc_feature_t *)msg)->feature_id)) {
+            FIM_DEBUG(DEB_L_C_F_PREFIX" %s filtered by UI lock\n",
+                      DEB_L_C_F_PREFIX_ARGS(FIM, line, call_id, fname),
+                      cc_feature_name(((cc_feature_t *)msg)->feature_id));
+            return(TRUE);
+        }
+    }
+
+    /*
+     * Skip the head.
+     */
+    icb = call_chn->next_icb;
+    MOZ_ASSERT(icb);
+    while (icb && !done) {
+        /*
+         * Set the required event data so the entity can process the event.
+         */
+        cb_hdr = (fim_cb_hdr_t *) (icb->cb);
+
+        MOZ_ASSERT(cb_hdr);
+        if (!cb_hdr) {
+            done = TRUE;
+            break;
+        }
+
+        event.data  = cb_hdr;
+        event.state = cb_hdr->state;
+        event.event = event_id;
+        event.msg   = data;
+
+        fcb = (fsm_fcb_t *) icb->cb;
+
+        /*
+         * For example, if we receive INVITE and CANCEL messages back to back,
+         * we may be decrementing the call count of wrong line because SIP stack did
+         * not get the correct value for line yet.
+         * so set the line value to correct value from DCB.
+         * For RIU calls, there will be no DCB. However, line value cames from SIP stack
+         * correctly.
+         */
+
+        if ((fcb->fsm_type == FSM_TYPE_DEF) && (event_id == CC_MSG_RELEASE)) {
+            if (fcb->dcb != NULL) {
+                line = fcb->dcb->line;
+            }
+        }
+
+        /*
+         * This update_call_cnt is only used when RC_CLEANUP is returned.
+         */
+        update_call_cnt = TRUE; // by default, always update call count
+        if (fcb->dcb != NULL) {
+            update_call_cnt = (fcb->dcb->call_not_counted_in_mnc_bt) ? FALSE: TRUE;
+        }
+
+        FIM_DEBUG(DEB_L_C_F_PREFIX" %s(%s:%s)\n",
+                  DEB_L_C_F_PREFIX_ARGS(FIM, line, call_id, fname), fsm_type_name(icb->scb->type),
+                  fsm_state_name(fcb->fsm_type, event.state),
+                  cc_msg_name((cc_msgs_t) (event.event)));
+
+        rc = sm_process_event(icb->scb->sm, &event);
+
+
+        switch (rc) {
+        case SM_RC_CONT:
+        case SM_RC_DEF_CONT:
+            icb = icb->next_icb;
+            break;
+
+        case SM_RC_END:
+            done = TRUE;
+            break;
+
+        case SM_RC_ERROR:
+            FIM_DEBUG(DEB_L_C_F_PREFIX" fsm sm error(%d:%d)\n",
+                      DEB_L_C_F_PREFIX_ARGS(FIM, line, call_id, fname), event.state, event.event);
+            done = TRUE;
+            cc_call_state(call_id, line, CC_STATE_UNKNOWN, NULL);
+            break;
+
+        case SM_RC_CLEANUP:
+            done = TRUE;
+            cc_call_state(call_id, line, CC_STATE_UNKNOWN, NULL);
+            break;
+
+        default:
+            done = TRUE;
+            cc_call_state(call_id, line, CC_STATE_UNKNOWN, NULL);
+            break;
+        }                       /* switch (rc) */
+
+        if ((rc == SM_RC_END) && (fcb->fsm_type == FSM_TYPE_DEF) &&
+            (event_id == CC_MSG_FEATURE))
+        {
+            if ( ((cc_feature_t *) msg)->feature_id == CC_FEATURE_CFWD_ALL){
+                lsm_decrement_call_chn_cnt(line);
+            }
+        }
+
+        if (icb == NULL) {
+            done = TRUE;
+            FIM_DEBUG("icb is null and get here!");
+        }
+
+    }                           /* while (done != TRUE) { */
+
+    if (rc == SM_RC_CLEANUP) {
+        fim_free_call_chn(call_chn, line, update_call_cnt);
+
+    }
+
+    return(TRUE);
+}
+
+
+cc_int32_t
+fim_show_cmd (cc_int32_t argc, const char *argv[])
+{
+    fim_icb_t      *icb = NULL;
+    fim_scb_t      *scb = NULL;
+    int             i = 0;
+
+    /*
+     * Check if need help.
+     */
+    if ((argc == 2) && (argv[1][0] == '?')) {
+        debugif_printf("show fim\n");
+    }
+
+    /*
+     * Print the icbs.
+     */
+    debugif_printf("\n---------------------------------- FIM icbs -----------------------------------");
+    debugif_printf("\ni   call_id  type    icb         next_chn    next_icb    cb          scb");
+    debugif_printf("\n-------------------------------------------------------------------------------\n");
+
+    FSM_FOR_ALL_CBS(icb, fim_icbs, FIM_MAX_ICBS) {
+        debugif_printf("%-3d  %-7d  %-6s  0x%8p  0x%8p  0x%8p  0x%8p  0x%8p\n",
+                       i++, icb->call_id, fsm_type_name(icb->scb->type),
+                       icb, icb->next_chn, icb->next_icb, icb->cb, icb->scb);
+    }
+
+    /*
+     * Print the scbs.
+     */
+    i = 0;
+    debugif_printf
+        ("\n------------------------ FIM scbs ------------------------");
+    debugif_printf("\ni   type    scb         sm          get_cb      free_cb");
+    debugif_printf
+        ("\n----------------------------------------------------------\n");
+
+    FSM_FOR_ALL_CBS(scb, fim_scbs, FIM_MAX_SCBS) {
+        debugif_printf("%-2d  %-6s  0x%8p  0x%8p  0x%8p  0x%8p\n",
+                       i++, fsm_type_name(scb->type), scb,
+                       scb->sm, scb->get_cb, scb->free_cb);
+    }
+
+    return (0);
+}
+
+
+void
+fim_init (void)
+{
+
+    fim_init_call_chns();
+}
+
+void
+fim_shutdown (void)
+{
+    cpr_free(fim_scbs);
+    cpr_free(fim_icbs);
+    fim_scbs = NULL;
+    fim_icbs = NULL;
+}
diff --git a/libs/sipcc/core/gsm/fsm.c b/libs/sipcc/core/gsm/fsm.c
new file mode 100755 (executable)
index 0000000..ebc9f90
--- /dev/null
@@ -0,0 +1,1383 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "fsm.h"
+#include "fim.h"
+#include "lsm.h"
+#include "sm.h"
+#include "gsm.h" /* for GSM_ERR_MSG */
+#include "ccapi.h"
+#include "phone_debug.h"
+#include "debug.h"
+#include "text_strings.h"
+#include "sip_interface_regmgr.h"
+#include "resource_manager.h"
+#include "platform_api.h"
+
+#define FSM_MAX_FCBS (LSM_MAX_CALLS * (FSM_TYPE_MAX - 1))
+#define FSM_S_IDLE   0
+
+extern sm_table_t *pfsmcnf_sm_table;
+extern sm_table_t *pfsmb2bcnf_sm_table;
+extern sm_table_t *pfsmxfr_sm_table;
+extern sm_table_t *pfsmdef_sm_table;
+
+static fsm_fcb_t *fsm_fcbs;
+static fsmdef_dcb_t fsm_dcb;
+extern uint16_t g_numofselected_calls;
+extern void dcsm_update_gsm_state(fsm_fcb_t *fcb, callid_t call_id, int state);
+
+
+static const char *fsm_type_names[] = {
+    "HEAD",
+    "CNF",
+    "B2BCNF",
+    "XFR",
+    "DEF"
+};
+
+static resource_manager_t *ci_map_p[MAX_REG_LINES + 1];
+static resource_manager_t *shown_calls_ci_map_p[MAX_REG_LINES + 1];
+
+const char *
+fsm_type_name (fsm_types_t type)
+{
+    if ((type <= FSM_TYPE_MIN) || (type >= FSM_TYPE_MAX)) {
+        return (get_debug_string(GSM_UNDEFINED));
+    }
+
+    return (fsm_type_names[type]);
+}
+
+
+const char *
+fsm_state_name (fsm_types_t type, int id)
+{
+    switch (type) {
+    case FSM_TYPE_DEF:
+        return (fsmdef_state_name(id));
+
+    case FSM_TYPE_XFR:
+        return (fsmxfr_state_name(id));
+
+    case FSM_TYPE_CNF:
+        return (fsmcnf_state_name(id));
+
+    case FSM_TYPE_B2BCNF:
+        return (fsmb2bcnf_state_name(id));
+
+    case FSM_TYPE_NONE:
+        return ("IDLE");
+
+    default:
+        return (get_debug_string(GSM_UNDEFINED));
+    }
+}
+
+
+void
+fsm_sm_ftr (cc_features_t ftr_id, cc_srcs_t src_id)
+{
+    FSM_DEBUG_SM(get_debug_string(FSM_DBG_SM_FTR_ENTRY),
+                 cc_feature_name(ftr_id), cc_src_name(src_id));
+}
+
+
+void
+fsm_sm_ignore_ftr (fsm_fcb_t *fcb, int fname, cc_features_t ftr_id)
+{
+    FSM_DEBUG_SM(get_debug_string(FSM_DBG_IGNORE_FTR),
+                 fsm_type_name(fcb->fsm_type), fcb->call_id, fname,
+                 cc_feature_name(ftr_id));
+}
+
+
+void
+fsm_sm_ignore_src (fsm_fcb_t *fcb, int fname, cc_srcs_t src_id)
+{
+    FSM_DEBUG_SM(get_debug_string(FSM_DBG_IGNORE_SRC),
+                 fsm_type_name(fcb->fsm_type), fcb->call_id, fname,
+                 cc_src_name(src_id));
+}
+
+
+void
+fsm_init_fcb (fsm_fcb_t *fcb, callid_t call_id, fsmdef_dcb_t *dcb,
+              fsm_types_t type)
+{
+    fcb->call_id = call_id;
+
+    fcb->state     = FSM_S_IDLE;
+    fcb->old_state = FSM_S_IDLE;
+
+    fcb->fsm_type = type;
+
+    fcb->dcb = dcb;
+
+    fcb->xcb = NULL;
+
+    fcb->ccb = NULL;
+
+    fcb->b2bccb = NULL;
+}
+
+
+/*
+ *  ROUTINE:     fsm_get_fcb_by_call_id_and_type
+ *
+ *  DESCRIPTION: return the fcb referenced by the given call_id and type
+ *
+ *  PARAMETERS:  fsm_id
+ *
+ *  RETURNS:     fcb
+ *      !NULL: fcb found
+ *      NULL:  fcb not found
+ *
+ *  NOTES:
+ */
+fsm_fcb_t *
+fsm_get_fcb_by_call_id_and_type (callid_t call_id, fsm_types_t type)
+{
+    static const char fname[] = "fsm_get_fcb_by_call_id_and_type";
+    fsm_fcb_t      *fcb;
+    fsm_fcb_t      *fcb_found = NULL;
+
+    FSM_FOR_ALL_CBS(fcb, fsm_fcbs, FSM_MAX_FCBS) {
+        if ((fcb->call_id == call_id) && (fcb->fsm_type == type)) {
+            fcb_found = fcb;
+            break;
+        }
+    }
+
+    FSM_DEBUG_SM(get_debug_string(GSM_DBG_PTR), "FSM", call_id,
+                 fname, "fcb", fcb_found);
+
+    return (fcb_found);
+}
+
+/*
+ *  DESCRIPTION: return the fcb referenced by the given call_id and type
+ *
+ *  PARAMETERS:  fsm_id
+ *
+ *  @return void
+ *      !NULL: fcb found
+ *      NULL:  fcb not found
+ *
+ */
+void
+fsm_get_fcb_by_selected_or_connected_call_fcb (callid_t call_id, fsm_fcb_t **con_fcb_found,
+                                               fsm_fcb_t **sel_fcb_found)
+{
+    static const char fname[] = "fsm_get_fcb_by_selected_or_connected_call_fcb";
+    fsm_fcb_t      *fcb;
+
+    *con_fcb_found = NULL;
+    *sel_fcb_found = NULL;
+
+    FSM_FOR_ALL_CBS(fcb, fsm_fcbs, FSM_MAX_FCBS) {
+
+        if (fcb->call_id == call_id) {
+            /* Do not count current call_id */
+            continue;
+        }
+        if (fcb->fsm_type == FSM_TYPE_DEF &&
+            (fcb->state == FSMDEF_S_CONNECTED ||
+             fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND ||
+             fcb->state == FSMDEF_S_OUTGOING_ALERTING)) {
+            *con_fcb_found = fcb;
+        } else if (fcb->fsm_type == FSM_TYPE_DEF && fcb->dcb->selected) {
+            *sel_fcb_found = fcb;
+            break;
+        }
+    }
+
+    FSM_DEBUG_SM(get_debug_string(GSM_DBG_PTR), "FSM", call_id,
+                 fname, "fcb", con_fcb_found);
+
+}
+
+/*
+ *  ROUTINE:     fsm_get_fcb_by_call_id
+ *
+ *  DESCRIPTION: return the fcb referenced by the given call_id
+ *
+ *  PARAMETERS:  fsm_id
+ *
+ *  RETURNS:     fcb
+ *      !NULL: fcb found
+ *      NULL:  fcb not found
+ *
+ *  NOTES:
+ */
+fsm_fcb_t *
+fsm_get_fcb_by_call_id (callid_t call_id)
+{
+    static const char fname[] = "fsm_get_fcb_by_call_id";
+    fsm_fcb_t      *fcb;
+    fsm_fcb_t      *fcb_found = NULL;
+
+    FSM_FOR_ALL_CBS(fcb, fsm_fcbs, FSM_MAX_FCBS) {
+        if (fcb->call_id == call_id) {
+            fcb_found = fcb;
+            break;
+        }
+    }
+
+    FSM_DEBUG_SM(get_debug_string(GSM_DBG_PTR), "FSM", call_id,
+                 fname, "fcb", fcb_found);
+
+    return (fcb_found);
+}
+
+
+/*
+ *  ROUTINE:     fsm_get_new_fcb
+ *
+ *  DESCRIPTION: return a new fcb initialized with the given data
+ *
+ *  PARAMETERS:
+ *      call_id:    call_id
+ *      type:       feature type
+ *
+ *  RETURNS:
+ *      fcb: the new fcb
+ */
+fsm_fcb_t *
+fsm_get_new_fcb (callid_t call_id, fsm_types_t fsm_type)
+{
+    static const char fname[] = "fsm_get_new_fcb";
+    fsm_fcb_t *fcb;
+
+    /*
+     * Get free fcb by using CC_NO_CALL_ID as the call_id because a free fcb
+     * will have a call_id of CC_NO_CALL_ID.
+     */
+    fcb = fsm_get_fcb_by_call_id(CC_NO_CALL_ID);
+    if (fcb != NULL) {
+        fsm_init_fcb(fcb, call_id, FSMDEF_NO_DCB, fsm_type);
+    }
+
+    FSM_DEBUG_SM(get_debug_string(GSM_DBG_PTR), "FSM", call_id,
+                 fname, "fcb", fcb);
+
+    return (fcb);
+}
+
+
+fsmdef_dcb_t *
+fsm_get_dcb (callid_t call_id)
+{
+    fsmdef_dcb_t *dcb;
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+    /*
+     * Return the fsm default dcb if a dcb was not found.
+     */
+    if (dcb == NULL) {
+        dcb = &fsm_dcb;
+    }
+
+    return (dcb);
+}
+
+
+void
+fsm_get_fcb (fim_icb_t *icb, callid_t call_id)
+{
+    icb->cb = fsm_get_new_fcb(call_id, icb->scb->type);
+}
+
+
+void
+fsm_init_scb (fim_icb_t *icb, callid_t call_id)
+{
+    icb->scb->get_cb = &fsm_get_fcb;
+
+    switch (icb->scb->type) {
+
+    case FSM_TYPE_B2BCNF:
+        icb->scb->sm = pfsmb2bcnf_sm_table;
+        icb->scb->free_cb = fsmb2bcnf_free_cb;
+
+        break;
+
+    case FSM_TYPE_CNF:
+        icb->scb->sm = pfsmcnf_sm_table;
+        icb->scb->free_cb = fsmcnf_free_cb;
+
+        break;
+    case FSM_TYPE_XFR:
+        icb->scb->sm = pfsmxfr_sm_table;
+        icb->scb->free_cb = fsmxfr_free_cb;
+
+        break;
+
+    case FSM_TYPE_DEF:
+        icb->scb->sm = pfsmdef_sm_table;
+        icb->scb->free_cb = fsmdef_free_cb;
+
+        break;
+
+    case FSM_TYPE_HEAD:
+    default:
+        icb->scb->get_cb  = NULL;
+        icb->scb->free_cb = NULL;
+        icb->scb->sm      = NULL;
+    }
+
+}
+
+
+void
+fsm_change_state (fsm_fcb_t *fcb, int fname, int new_state)
+{
+
+    DEF_DEBUG(DEB_L_C_F_PREFIX"%s: %s -> %s\n",
+                 DEB_L_C_F_PREFIX_ARGS(FSM, ((fcb->dcb == NULL)? CC_NO_LINE: fcb->dcb->line),
+                 fcb->call_id, "fsm_change_state"),
+                 fsm_type_name(fcb->fsm_type),
+                 fsm_state_name(fcb->fsm_type, fcb->state),
+                 fsm_state_name(fcb->fsm_type, new_state));
+
+    fcb->old_state = fcb->state;
+    fcb->state = new_state;
+    NOTIFY_STATE_CHANGE(fcb, fcb->call_id, new_state);
+
+}
+
+
+void
+fsm_release (fsm_fcb_t *fcb, int fname, cc_causes_t cause)
+{
+    fsm_change_state(fcb, fname, FSM_S_IDLE);
+
+    /* Cleanup any pending cac request for that call */
+    fsm_cac_call_release_cleanup(fcb->call_id);
+
+    fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE);
+}
+
+
+cc_int32_t
+fsm_show_cmd (cc_int32_t argc, const char *argv[])
+{
+    fsm_fcb_t      *fcb;
+    int             i = 0;
+    void           *cb = NULL;
+
+    /*
+     * check if need help
+     */
+    if ((argc == 2) && (argv[1][0] == '?')) {
+        debugif_printf("show fsm\n");
+        return 0;
+    }
+
+    /*
+     * Print the fcbs
+     */
+    debugif_printf("\n----------------------------- FSM fcbs -------------------------------");
+    debugif_printf("\ni    call_id  fcb         type       state      dcb         cb        ");
+    debugif_printf("\n----------------------------------------------------------------------\n");
+
+    FSM_FOR_ALL_CBS(fcb, fsm_fcbs, FSM_MAX_FCBS) {
+        switch (fcb->fsm_type) {
+        case FSM_TYPE_CNF:
+            cb = fcb->ccb;
+            break;
+
+        case FSM_TYPE_B2BCNF:
+            cb = fcb->ccb;
+            break;
+
+        case FSM_TYPE_XFR:
+            cb = fcb->xcb;
+            break;
+
+        case FSM_TYPE_DEF:
+            cb = fcb->dcb;
+            break;
+
+        default:
+            cb = NULL;
+        }
+
+        debugif_printf("%-3d  %-7d  0x%8p  %-9s  %-9s  0x%8p  0x%8p\n",
+                       i++, fcb->call_id, fcb, fsm_type_name(fcb->fsm_type),
+                       fsm_state_name(fcb->fsm_type, fcb->state),
+                       fcb->dcb, cb);
+    }
+
+    return (0);
+}
+
+
+void
+fsm_init (void)
+{
+    fsm_fcb_t *fcb;
+
+    fsmdef_init_dcb(&fsm_dcb, 0, FSMDEF_CALL_TYPE_NONE, NULL, LSM_NO_LINE,
+                    NULL);
+
+    fsmdef_init();
+    fsmb2bcnf_init();
+    fsmcnf_init();
+    fsmxfr_init();
+
+    fsm_cac_init();
+
+    /*
+     * Initialize the fcbs.
+     */
+    fsm_fcbs = (fsm_fcb_t *) cpr_calloc(FSM_MAX_FCBS, sizeof(fsm_fcb_t));
+    if (fsm_fcbs == NULL) {
+        GSM_ERR_MSG(GSM_F_PREFIX"Failed to allcoate FSM FCBs.\n", "fsm_init");
+        return;
+    }
+
+    FSM_FOR_ALL_CBS(fcb, fsm_fcbs, FSM_MAX_FCBS) {
+        fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE);
+    }
+
+    /*
+     * Init call instance id map.
+     */
+    fsmutil_init_ci_map();
+}
+
+void
+fsm_shutdown (void)
+{
+    fsmdef_shutdown();
+
+    fsmb2bcnf_shutdown();
+
+    fsmcnf_shutdown();
+    fsmxfr_shutdown();
+
+    fsm_cac_shutdown();
+
+    cpr_free(fsm_fcbs);
+    fsm_fcbs = NULL;
+
+    /*
+     * Free call instance id map.
+     */
+    fsmutil_free_ci_map();
+}
+
+/*
+ *  ROUTINE:     fsm_set_fcb_dcbs
+ *
+ *  DESCRIPTION: Set the dcbs for the fsms. The fsm call chain is setup before
+ *               the dcb is known, so this function is called after the dcb
+ *               is known to set the dcb in the fcbs.
+ *
+ *  PARAMETERS:
+ *      dcb
+ *
+ *  RETURNS:     NONE
+ *
+ *  NOTES:
+ */
+cc_causes_t
+fsm_set_fcb_dcbs (fsmdef_dcb_t *dcb)
+{
+    callid_t        call_id = dcb->call_id;
+    fsm_fcb_t      *fcb;
+    fsm_types_t     i;
+
+    for (i = FSM_TYPE_CNF; i < FSM_TYPE_MAX; i++) {
+        fcb = fsm_get_fcb_by_call_id_and_type(call_id, i);
+        if (fcb == NULL) {
+            return CC_CAUSE_ERROR;
+        }
+        fcb->dcb = dcb;
+    }
+
+    return CC_CAUSE_OK;
+}
+
+
+/*
+ *  ROUTINE:     fsm_get_new_outgoing_call_context
+ *
+ *  DESCRIPTION: get a new outgoing call context
+ *
+ *  PARAMETERS:
+ *      fcb
+ *      call_id
+ *      line
+ *
+ *  RETURNS:     rc
+ *      FSM_SUCCESS: context successfully created
+ *      FSM_SUCCESS: context unsuccessfully created
+ *
+ *  NOTES:
+ */
+cc_causes_t
+fsm_get_new_outgoing_call_context (callid_t call_id, line_t line,
+                                  fsm_fcb_t *fcb, boolean expline)
+{
+    static const char fname[] = "fsm_get_new_outgoing_call_context";
+    fsmdef_dcb_t   *dcb;
+    cc_causes_t     cause = CC_CAUSE_OK;
+    cc_causes_t     lsm_rc;
+
+    /*
+     * Get a dcb to handle the call.
+     */
+    dcb = fsmdef_get_new_dcb(call_id);
+    if (dcb == NULL) {
+        return CC_CAUSE_NO_RESOURCE;
+    }
+
+    /*
+     * Get a free facility associated with this line.
+     */
+    lsm_rc = lsm_get_facility_by_line(call_id, line, expline, dcb);
+    if (lsm_rc != CC_CAUSE_OK) {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_FAC_ERR), call_id, fname,
+                     "lsm_get_facility_by_line failed", cc_cause_name(lsm_rc));
+    }
+
+    /*
+     * If no line was returned, then init the dcb with an invalid line to
+     * indicate that no line was returned.
+     */
+    if (lsm_rc != CC_CAUSE_OK) {
+        line = LSM_NO_LINE;
+    }
+    fsmdef_init_dcb(dcb, call_id, FSMDEF_CALL_TYPE_OUTGOING, NULL, line, fcb);
+
+    cause = fsm_set_fcb_dcbs(dcb);
+    if (cause == CC_CAUSE_OK) {
+        cause = lsm_rc;
+    }
+
+    FSM_DEBUG_SM(get_debug_string(FSM_DBG_FAC_FOUND), call_id, fname,
+                 dcb->line);
+
+    return cause;
+}
+
+
+/*
+ *  ROUTINE:     fsm_get_new_incoming_call_context
+ *
+ *  DESCRIPTION: get a new incoming call context
+ *
+ *  PARAMETERS:
+ *      fcb
+ *      call_id
+ *
+ *  RETURNS:     rc
+ *      FSM_SUCCESS: context successfully created
+ *      FSM_SUCCESS: context unsuccessfully created
+ *
+ *  NOTES:
+ */
+cc_causes_t
+fsm_get_new_incoming_call_context (callid_t call_id, fsm_fcb_t *fcb,
+                                   const char *called_number, boolean expline)
+{
+    static const char fname[] = "fsm_get_new_incoming_call_context";
+    fsmdef_dcb_t   *dcb;
+    line_t          free_line;
+    cc_causes_t     cause;
+    cc_causes_t     lsm_rc;
+
+
+    /*
+     * Get a dcb to handle the call.
+     */
+    dcb = fsmdef_get_new_dcb(call_id);
+    if (dcb == NULL) {
+        return CC_CAUSE_NO_RESOURCE;
+    }
+
+    /*
+     * Get a free facility associated with this called_number.
+     */
+    if ((lsm_rc = lsm_get_facility_by_called_number(call_id, called_number,
+                                                    &free_line, expline, dcb))
+        != CC_CAUSE_OK) {
+        /*
+         * Set a default free line (This should really be changed
+         * to a special invalid value and GSM modified to recognize it.)
+         * The dcb is needed in order for GSM to clean up the call correctly.
+         */
+        free_line = 1;
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_FAC_ERR), call_id, fname,
+                     "lsm_get_facility_by_called_number",
+                     cc_cause_name(lsm_rc));
+    }
+
+    fsmdef_init_dcb(dcb, call_id, FSMDEF_CALL_TYPE_INCOMING, called_number,
+                    free_line, fcb);
+
+    cause = fsm_set_fcb_dcbs(dcb);
+    if (cause == CC_CAUSE_OK) {
+        cause = lsm_rc;
+    }
+
+    FSM_DEBUG_SM(get_debug_string(FSM_DBG_FAC_FOUND), call_id, fname,
+                 dcb->line);
+
+    return cause;
+}
+
+/*
+ * fsmutil_is_cnf_leg
+ *
+ * Description:
+ *
+ * Returns TRUE if conferencing is active for the call_id
+ *
+ *
+ * Parameters:
+ *
+ * call_id - ccapi call identifier associated with the call.
+ * fsmxcb_ccbs - pointer to all conf ccbs
+ * max_ccbs - Max number of ccbs to check
+ *
+ * Returns TRUE if the conferencing is active for the call_id
+ */
+int
+fsmutil_is_cnf_leg (callid_t call_id, fsmcnf_ccb_t *fsmcnf_ccbs,
+                    unsigned short max_ccbs)
+{
+    fsmcnf_ccb_t *ccb;
+
+    FSM_FOR_ALL_CBS(ccb, fsmcnf_ccbs, max_ccbs) {
+        if ((ccb->cnf_call_id == call_id) || (ccb->cns_call_id == call_id)) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/*
+ * fsmutil_is_xfr_leg
+ *
+ * Description:
+ *
+ * Returns transfer mode if the specified leg identified by call_id is
+ * participating in a xfer
+ *
+ * Parameters:
+ *
+ * call_id -   ccapi call identifier associated with the call.
+ * fsmxfr_xcbs - pointer to all xfr ccbs
+ * max_xcbs - Max number of ccbs to check
+ *
+ * Returns: fsmxfr_modes_t
+ */
+int
+fsmutil_is_xfr_leg (callid_t call_id, fsmxfr_xcb_t *fsmxfr_xcbs,
+                    unsigned short max_xcbs)
+{
+    fsmxfr_xcb_t *xcb;
+
+    FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, max_xcbs) {
+        if ((xcb->xfr_call_id == call_id) || (xcb->cns_call_id == call_id)) {
+            return xcb->mode;
+        }
+    }
+    return FSMXFR_MODE_MIN;
+}
+
+void
+fsm_display_no_free_lines (void)
+{
+    char tmp_str[STATUS_LINE_MAX_LEN];
+
+    if ((platGetPhraseText(STR_INDEX_NO_FREE_LINES,
+                                 (char *)tmp_str,
+                                 (STATUS_LINE_MAX_LEN - 1))) == CPR_SUCCESS) {
+        lsm_ui_display_notify(tmp_str, NO_FREE_LINES_TIMEOUT);
+    }
+}
+
+/*
+ *  Function: fsm_display_use_line_or_join_to_complete
+ *
+ *  Description:
+ *      The function is used to put up the status line
+ *      "Use Line or Join to Complete"
+ *
+ *  @param None
+ *
+ *  @return None
+ */
+void
+fsm_display_use_line_or_join_to_complete (void)
+{
+    char tmp_str[STATUS_LINE_MAX_LEN];
+
+    if ((platGetPhraseText(STR_INDEX_USE_LINE_OR_JOIN_TO_COMPLETE,
+                                 (char *)tmp_str,
+                                 (STATUS_LINE_MAX_LEN - 1))) == CPR_SUCCESS) {
+        lsm_ui_display_notify(tmp_str, NO_FREE_LINES_TIMEOUT);
+    }
+}
+
+void
+fsm_display_feature_unavailable (void)
+{
+    char tmp_str[STATUS_LINE_MAX_LEN];
+
+    if ((platGetPhraseText(STR_INDEX_FEAT_UNAVAIL,
+                                 (char *)tmp_str,
+                                 (STATUS_LINE_MAX_LEN - 1))) == CPR_SUCCESS) {
+        lsm_ui_display_notify(tmp_str, NO_FREE_LINES_TIMEOUT);
+    }
+}
+
+void
+fsm_set_call_status_feature_unavailable (callid_t call_id, line_t line)
+{
+    char tmp_str[STATUS_LINE_MAX_LEN];
+
+    if ((platGetPhraseText(STR_INDEX_FEAT_UNAVAIL,
+                                 (char *)tmp_str,
+                                 (STATUS_LINE_MAX_LEN - 1))) == CPR_SUCCESS) {
+        ui_set_call_status(tmp_str, line, lsm_get_ui_id(call_id));
+    }
+}
+
+/**
+ *
+ * Get total number of selected calls.
+ *
+ * @param void
+ *
+ * @return  uint16_t
+ *
+ * @pre     (none)
+ */
+uint16_t fsmutil_get_num_selected_calls (void)
+{
+    return(g_numofselected_calls);
+}
+
+/**
+ * This function will hide/unhide ringingin calls.
+ *
+ * @param[in] hide - indicates whether calls should be hidden or unhidden
+ *
+ * @return none
+ */
+void fsm_display_control_ringin_calls (boolean hide)
+{
+    fsm_fcb_t      *fcb;
+
+    FSM_FOR_ALL_CBS(fcb, fsm_fcbs, FSM_MAX_FCBS) {
+        if ((fcb->state == FSMDEF_S_INCOMING_ALERTING) &&
+            (lsm_is_it_priority_call(fcb->call_id) == FALSE)) { /* priority call should not be hidden */
+            lsm_display_control_ringin_call (fcb->call_id, fcb->dcb->line, hide);
+            if (hide == TRUE) {
+                fsmutil_clear_shown_calls_ci_element(fcb->dcb->caller_id.call_instance_id, fcb->dcb->line);
+            } else {
+                fsmutil_set_shown_calls_ci_element(fcb->dcb->caller_id.call_instance_id, fcb->dcb->line);
+            }
+        }
+    }
+}
+
+/*
+ * fsmutil_init_groupid
+ *
+ * Description:
+ *
+ * Assign the group id to the default control block.
+ *
+ * Parameters:
+ *    dcb       - default control block
+ *    call_id   - ccapi call identifier associated with the call
+ *    call_type - incoming or outgoing call
+ *
+ * Returns: None. groupid will be assigned in the dcb.
+ */
+void
+fsmutil_init_groupid (fsmdef_dcb_t *dcb, callid_t call_id,
+                      fsmdef_call_types_t call_type)
+{
+    fsmcnf_ccb_t *ccb = NULL;
+
+    /* if this was a consult leg of a conference then there will be
+     * a ccb on the primary leg
+     */
+    dcb->group_id = CC_NO_GROUP_ID;
+    if (call_type != FSMDEF_CALL_TYPE_NONE) {
+        ccb = fsmcnf_get_ccb_by_call_id(call_id);
+        if (ccb) {
+            /* consult leg of a conference */
+            fsmdef_dcb_t *other_dcb = NULL;
+
+            other_dcb =
+                fsmdef_get_dcb_by_call_id(fsmcnf_get_other_call_id(ccb, call_id));
+            if (other_dcb) {
+                dcb->group_id = other_dcb->group_id;
+            }
+        } else {
+            /* either a primary or some other leg; not part of any conference */
+
+            dcb->group_id = dcb->call_id;
+        }
+    }
+    return;
+}
+
+/**
+ *
+ *    Find out if the call pointed by call_id is a consult call
+ *    of a conference, transfer.
+ *
+ * @param line       line related to that call
+ * @param call_id    call_id
+ *
+ * @return  call attributes
+ *
+ * @pre     none
+ */
+int
+fsmutil_get_call_attr (fsmdef_dcb_t *dcb,
+                    line_t line, callid_t call_id)
+{
+    int call_attr;
+
+    if (fsmutil_is_cnf_consult_call(call_id) == TRUE) {
+        call_attr = LOCAL_CONF_CONSULT;
+    } else if (fsmutil_is_b2bcnf_consult_call(call_id) == TRUE) {
+        call_attr = CONF_CONSULT;
+    } else if (fsmutil_is_xfr_consult_call(call_id) == TRUE) {
+        call_attr = XFR_CONSULT;
+    } else {
+        if (dcb == NULL) {
+            return(NORMAL_CALL);
+        }
+
+        switch (dcb->active_feature) {
+        case CC_FEATURE_CFWD_ALL:
+            call_attr = CC_ATTR_CFWD_ALL;
+            break;
+        default:
+            call_attr = NORMAL_CALL;
+            break;
+        }
+    }
+    return call_attr;
+}
+
+/*
+ * fsmutil_is_cnf_consult_leg
+ *
+ * Description:
+ *    Returns TRUE if the specified call_id is for a call that is
+ *    the consultative call of a conference.
+ *
+ * Parameters:
+ *    call_id     - ccapi call identifier associated with the call.
+ *    fsmxcb_ccbs - pointer to all conf ccbs
+ *    max_ccbs    - Max number of ccbs to check
+ *
+ * Returns: TRUE if consultative call; otherwise, FALSE.
+ */
+int
+fsmutil_is_cnf_consult_leg (callid_t call_id, fsmcnf_ccb_t *fsmcnf_ccbs,
+                            uint16_t max_ccbs)
+{
+    fsmcnf_ccb_t *ccb;
+
+    FSM_FOR_ALL_CBS(ccb, fsmcnf_ccbs, max_ccbs) {
+        if (ccb->cns_call_id == call_id) {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+
+/*
+ * fsmutil_is_xfr_consult_leg
+ *
+ * Description:
+ *    Returns TRUE if the specified call_id is for a call that is
+ *    the consultative call of a transfer only when that consult
+ *    was an initiated transfer.
+ *
+ * Parameters:
+ *    call_id     - ccapi call identifier associated with the call.
+ *    fsmxfr_xcbs - pointer to all xfr ccbs
+ *    max_xcbs    - Max number of ccbs to check
+ *
+ * Returns: TRUE if consultative call; otherwise, FALSE.
+ */
+int
+fsmutil_is_xfr_consult_leg (callid_t call_id, fsmxfr_xcb_t *fsmxfr_xcbs,
+                            uint16_t max_xcbs)
+{
+    fsmxfr_xcb_t *xcb;
+
+    FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, max_xcbs) {
+        if ((xcb->mode == FSMXFR_MODE_TRANSFEROR) &&
+            (xcb->cns_call_id == call_id)) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/*
+ * fsmutil_clear_feature_invocation_state
+ *
+ * Description:
+ *    This function clears the feature invocation state of the feature id
+ *    supplied. This function is used by the code that would receive the
+ *    feature ack (from SIP stack).
+ *
+ * Note: Code responsible for invoking features and receiving feature acks
+ *       must call this function to clear the feature invocation state.
+ *
+ * Parameters:
+ *    fsmdef_dcb_t  *dcb - dcb associated with the call
+ *    cc_features_t feature_id - feature id in question
+ *
+ * Returns: None
+ */
+static void
+fsmutil_clear_feature_invocation_state (fsmdef_dcb_t *dcb,
+                                       cc_features_t feature_id)
+{
+    if ((feature_id < CC_FEATURE_NONE) || (feature_id >= CC_FEATURE_MAX)) {
+        /* Log Error */
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"Invalid feature id -> %d\n", dcb->line, dcb->call_id, "fsmutil_clear_feature_invocation_state", feature_id);
+        return;
+    }
+
+    rm_clear_element((resource_manager_t *) dcb->feature_invocation_state,
+                     (int16_t) feature_id);
+}
+
+/*
+ * fsmutil_process_feature_ack
+ *
+ * Description:
+ *    This function implements the generic feature ack processing.
+ *    Feature invocation state is Cleared when feature ack is received.
+ *
+ * Parameters:
+ *    fsmdef_dcb_t  *dcb - dcb associated with the call
+ *    cc_features_t feature_id - feature id in question
+ *
+ * Returns: None
+ */
+void
+fsmutil_process_feature_ack (fsmdef_dcb_t *dcb, cc_features_t feature_id)
+{
+    /* clear the feature invocation state (was set when invoked) */
+    fsmutil_clear_feature_invocation_state(dcb, feature_id);
+}
+
+/*
+ * fsmutil_clear_all_feature_invocation_state
+ *
+ * Description:
+ *    This function clears the feature invocation state of ALL features.
+ *
+ *    This function may be used by the code that would initialize/reset
+ *    the state machine.
+ *
+ * Parameters:
+ *    fsmdef_dcb_t  *dcb - dcb associated with the call
+ *
+ * Returns: None
+ */
+void
+fsmutil_clear_all_feature_invocation_state (fsmdef_dcb_t *dcb)
+{
+    rm_clear_all_elements((resource_manager_t *) dcb->feature_invocation_state);
+}
+
+/*
+ * fsmutil_init_feature_invocation_state
+ *
+ * Description:
+ *    Utility function to allocate and init a feature invocation state table.
+ *
+ * Parameters:
+ *    dcb - dcb whose feature invocation state table is being created
+ *
+ * Returns:
+ *    none
+ */
+void
+fsmutil_init_feature_invocation_state (fsmdef_dcb_t *dcb)
+{
+    static const char fname[] = "fsmutil_init_feature_invocation_state";
+
+    dcb->feature_invocation_state = rm_create(CC_FEATURE_MAX);
+
+    if (!dcb->feature_invocation_state) {
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"failed to allocate feature invocation state table\n", dcb->line, dcb->call_id,
+                     fname);
+    }
+}
+
+
+/*
+ * fsmutil_free_feature_invocation_state
+ *
+ * Description:
+ *    Utility function to free the feature invocation state table of the
+ *    specified dcb.
+ *
+ * Parameters:
+ *    None
+ *
+ * Returns:
+ *    None
+ */
+void
+fsmutil_free_feature_invocation_state (fsmdef_dcb_t *dcb)
+{
+    rm_destroy((resource_manager_t *) dcb->feature_invocation_state);
+    dcb->feature_invocation_state = NULL;
+}
+
+/*
+ * fsmutil_free_all_ci_id
+ *
+ * Description:
+ *    This function clears the entire call instance map.
+ *
+ * Parameters:
+ *    None
+ *
+ * Returns:
+ *    None
+ */
+void
+fsmutil_free_all_ci_id (void)
+{
+    uint16_t line;
+
+    for (line = 1; line <= MAX_REG_LINES; line++) {
+        rm_clear_all_elements((resource_manager_t *) ci_map_p[line]);
+    }
+}
+
+/*
+ * fsmutil_free_ci_id
+ *
+ * Description:
+ *    This function clears a specified call instance id from
+ *    the call instance map.
+ *
+ * Note that call instance ids are one based. The resource manager
+ * used to implement the call instance map is zero based so we have
+ * to adjust the call instance id when using the resource manager API.
+ *
+ * Parameters:
+ *    id - the call instance id to be freed
+ *    line - line from where to free the call instance id
+ *
+ * Returns:
+ *    None
+ */
+void
+fsmutil_free_ci_id (uint16_t id, line_t line)
+{
+    static const char fname[] = "fsmutil_free_ci_id";
+
+    if (id < 1 || id > MAX_CALLS) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified id %d is invalid\n", fname, id);
+        return;
+    }
+
+    if (line < 1 || line > MAX_REG_LINES) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified line %d is invalid\n", fname, line);
+        return;
+    }
+
+    rm_clear_element((resource_manager_t *) ci_map_p[line], (int16_t) --id);
+}
+
+#ifdef LOCAL_UI_CALLINSTANCE_ID
+/*This routine is not needed now as the local assignment
+ *of call instance id is no longer supported.
+ */
+/*
+ * fsmutil_get_ci_id
+ *
+ * Description:
+ *    This function locates the next available call instance id in
+ *    the call instance map. This function is utilized when operating in
+ *    the peer to peer mode to assign call instance ids for inbound
+ *    and outbound calls.
+ *
+ * Note that call instance ids are one based. The resource manager
+ * used to implement the call instance map is zero based so we have
+ * to adjust the call instance id when using the resource manager API.
+ *
+ * Parameters:
+ *    line - line from where to retrieve the call instance id
+ *
+ * Returns:
+ *    uint16_t - Non-zero call instance id. 0 if no call instance ids
+ *               are available.
+ */
+uint16_t
+fsmutil_get_ci_id (line_t line)
+{
+    static const char fname[] = "fsmutil_get_ci_id";
+    int16_t         id;
+    uint16_t        return_id = 0;
+
+    if (line < 1 || line > MAX_REG_LINES) {
+        GSM_ERR_MSG("specified line %d is invalid\n", fname, line);
+        return 0;
+    }
+
+    id = rm_get_free_element(ci_map_p[line]);
+    if (id >= 0) {
+        return_id = ++id;
+    }
+    return (return_id);
+}
+#endif
+
+/*
+ * fsmutil_set_ci_id
+ *
+ * Description:
+ *    This function marks a specified call instance id as being in
+ *    use in the call instance map. This function is used when in
+ *    CCM mode to store the call instance id specified by the CCM
+ *    for inbound and outbound calls.
+ *
+ * Note that call instance ids are one based. The resource manager
+ * used to implement the call instance map is zero based so we have
+ * to adjust the call instance id when using the resource manager API.
+ * Parameters:
+ *
+ * Parameters:
+ *    id - The call instance id to set.
+ *    line - Line being used for the call.
+ *
+ * Returns:
+ *    None
+ */
+void
+fsmutil_set_ci_id (uint16_t id, line_t line)
+{
+    static const char fname[] = "fsmutil_set_ci_id";
+
+    if (id < 1 || id > MAX_CALLS) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified id %d is invalid\n", fname, id);
+        return;
+    }
+
+    if (line < 1 || line > MAX_REG_LINES) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified line %d is invalid\n", fname, line);
+        return;
+    }
+
+    rm_set_element(ci_map_p[line], (int16_t) --id);
+}
+
+/*
+ * fsmutil_init_ci_map
+ *
+ * Description:
+ *    Utility function to allocate and init the call instance id map.
+ *
+ * Parameters:
+ *    None
+ *
+ * Returns:
+ *    None
+ */
+void
+fsmutil_init_ci_map (void)
+{
+    static const char fname[] = "fsmutil_init_ci_map";
+    uint16_t line;
+
+    for (line = 1; line <= MAX_REG_LINES; line++) {
+        ci_map_p[line] = rm_create(MAX_CALLS);
+
+        if (!ci_map_p[line]) {
+            GSM_ERR_MSG(GSM_F_PREFIX"failed to allocate call instance id map for line %d",
+                         fname, line);
+        }
+    }
+}
+
+/**
+ * This function will allocate and initialize the shown_calls_call_instnace map.
+ *
+ * @return none
+ */
+void fsmutil_init_shown_calls_ci_map (void)
+{
+    static const char fname[] = "fsmutil_init_shown_calls_ci_map";
+    uint16_t line;
+
+    for (line = 1; line <= MAX_REG_LINES; line++) {
+        shown_calls_ci_map_p[line] = rm_create(MAX_CALLS);
+
+        if (!shown_calls_ci_map_p[line]) {
+            GSM_ERR_MSG(GSM_F_PREFIX"failed to allocate shown calls call instance id map for line %d",
+                         fname, line);
+        }
+    }
+}
+
+/**
+ * This function will set the shown_calls_call_instnace map to zeros.
+ *
+ * @return none
+ */
+void fsmutil_free_all_shown_calls_ci_map (void)
+{
+    uint16_t line;
+
+    for (line = 1; line <= MAX_REG_LINES; line++) {
+        rm_clear_all_elements((resource_manager_t *) shown_calls_ci_map_p[line]);
+    }
+}
+
+/**
+ * This function marks a given call to be hidden.
+ *
+ * @param[in] id - call instance id of the call to be hidden.
+ * @param[in] line - the line being used for the call.
+ *
+ * @return none
+ */
+void fsmutil_clear_shown_calls_ci_element (uint16_t id, line_t line)
+{
+    static const char fname[] = "fsmutil_clear_shown_calls_ci_element";
+
+    if (id < 1 || id > MAX_CALLS) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified id %d is invalid\n", fname, id);
+        return;
+    }
+
+    if (line < 1 || line > MAX_REG_LINES) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified line %d is invalid\n", fname, line);
+        return;
+    }
+
+    rm_clear_element((resource_manager_t *) shown_calls_ci_map_p[line], (int16_t)(id - 1));
+}
+
+/**
+ * This function marks a given call to be shown.
+ *
+ * @param[in] id - call instance id of the call to be shown.
+ * @param[in] line - the line being used for the call.
+ *
+ * @return none
+ */
+void fsmutil_set_shown_calls_ci_element (uint16_t id, line_t line)
+{
+    static const char fname[] = "fsmutil_set_shown_calls_ci_element";
+
+    if (id < 1 || id > MAX_CALLS) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified id %d is invalid\n", fname, id);
+        return;
+    }
+
+    if (line < 1 || line > MAX_REG_LINES) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified line %d is invalid\n", fname, line);
+        return;
+    }
+
+    rm_set_element(shown_calls_ci_map_p[line], (int16_t)(id - 1));
+}
+
+/**
+ * This function checks if a given call is to be shown.
+ *
+ * @param[in] id - call instance id of the call.
+ * @param[in] line - the line being used for the call.
+ *
+ * @return none
+ */
+boolean fsmutil_is_shown_calls_ci_element_set (uint16_t id, line_t line)
+{
+    static const char fname[] = "fsmutil_is_shown_calls_ci_element_set";
+
+    if (id < 1 || id > MAX_CALLS) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified id %d is invalid\n", fname, id);
+        return FALSE;
+    }
+
+    if (line < 1 || line > MAX_REG_LINES) {
+        GSM_ERR_MSG(GSM_F_PREFIX"specified line %d is invalid\n", fname, line);
+        return FALSE;
+    }
+
+    if (rm_is_element_set(shown_calls_ci_map_p[line], (int16_t)(id - 1))) {
+        return (TRUE);
+    }
+
+    return (FALSE);
+}
+
+/*
+ * fsmutil_free_ci_map
+ *
+ * Description:
+ *    Utility function to free the call instance id map.
+ *
+ * Parameters:
+ *    None
+ *
+ * Returns:
+ *    None
+ */
+void
+fsmutil_free_ci_map (void)
+{
+    uint16_t line;
+
+    for (line = 1; line <= MAX_REG_LINES; line++) {
+        rm_destroy((resource_manager_t *) ci_map_p[line]);
+        ci_map_p[line] = NULL;
+    }
+}
+
+/*
+ * fsmutil_show_ci_map
+ *
+ * Description:
+ *    Utility function to display the current state of the call
+ *    instance map.
+ *
+ * Parameters:
+ *    None
+ *
+ * Returns:
+ *    None
+ */
+void
+fsmutil_show_ci_map (void)
+{
+    uint16_t line;
+
+    for (line = 1; line <= MAX_REG_LINES; line++) {
+        rm_show(ci_map_p[line]);
+    }
+}
diff --git a/libs/sipcc/core/gsm/fsmb2bcnf.c b/libs/sipcc/core/gsm/fsmb2bcnf.c
new file mode 100755 (executable)
index 0000000..e3c84f5
--- /dev/null
@@ -0,0 +1,1284 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "fsm.h"
+#include "fim.h"
+#include "lsm.h"
+#include "sm.h"
+#include "ccapi.h"
+#include "phone_debug.h"
+#include "text_strings.h"
+#include "debug.h"
+#include "config.h"
+#include "uiapi.h"
+#include "phntask.h"
+#include "regmgrapi.h"
+#include "subapi.h"
+#include "rcc_int_types.h"
+
+static fsmcnf_ccb_t *fsmb2bcnf_ccbs;
+
+typedef enum {
+    FSMB2BCNF_S_MIN = -1,
+    FSMB2BCNF_S_IDLE,
+    FSMB2BCNF_S_ACTIVE,
+    FSMB2BCNF_S_MAX
+} fsmb2bcnf_states_t;
+
+static const char *fsmb2bcnf_state_names[] = {
+    "IDLE",
+    "ACTIVE"
+};
+
+
+static sm_rcs_t fsmb2bcnf_ev_idle_feature(sm_event_t *event);
+static sm_rcs_t fsmb2bcnf_ev_active_release(sm_event_t *event);
+static sm_rcs_t fsmb2bcnf_ev_active_release_complete(sm_event_t *event);
+static sm_rcs_t fsmb2bcnf_ev_active_feature(sm_event_t *event);
+static sm_rcs_t fsmb2bcnf_ev_active_feature_ack(sm_event_t *event);
+static sm_rcs_t fsmb2bcnf_ev_active_onhook(sm_event_t *event);
+
+static sm_function_t fsmb2bcnf_function_table[FSMB2BCNF_S_MAX][CC_MSG_MAX] =
+{
+/* FSMB2BCNF_S_IDLE ------------------------------------------------------------ */
+    {
+    /* FSMB2BCNF_E_SETUP            */ NULL,
+    /* FSMB2BCNF_E_SETUP_ACK        */ NULL,
+    /* FSMB2BCNF_E_PROCEEDING       */ NULL,
+    /* FSMB2BCNF_E_ALERTING         */ NULL,
+    /* FSMB2BCNF_E_CONNECTED        */ NULL,
+    /* FSMB2BCNF_E_CONNECTED_ACK    */ NULL,
+    /* FSMB2BCNF_E_RELEASE          */ NULL,
+    /* FSMB2BCNF_E_RELEASE_COMPLETE */ NULL,
+    /* FSMB2BCNF_E_FEATURE          */ fsmb2bcnf_ev_idle_feature,
+    /* FSMB2BCNF_E_FEATURE_ACK      */ NULL,
+    /* FSMB2BCNF_E_OFFHOOK          */ NULL,
+    /* FSMB2BCNF_E_ONHOOK           */ NULL,
+    /* FSMB2BCNF_E_LINE             */ NULL,
+    /* FSMB2BCNF_E_DIGIT_BEGIN      */ NULL,
+    /* FSMB2BCNF_E_DIGIT            */ NULL,
+    /* FSMB2BCNF_E_DIALSTRING       */ NULL,
+    /* FSMB2BCNF_E_MWI              */ NULL,
+    /* FSMB2BCNF_E_SESSION_AUDIT    */ NULL
+    },
+
+/* FSMB2BCNF_S_ACTIVE --------------------------------------------------- */
+    {
+    /* FSMB2BCNF_E_SETUP            */ NULL,
+    /* FSMB2BCNF_E_SETUP_ACK        */ NULL,
+    /* FSMB2BCNF_E_PROCEEDING       */ NULL,
+    /* FSMB2BCNF_E_ALERTING         */ fsmb2bcnf_ev_active_feature,
+    /* FSMB2BCNF_E_CONNECTED        */ NULL,
+    /* FSMB2BCNF_E_CONNECTED_ACK    */ NULL,
+    /* FSMB2BCNF_E_RELEASE          */ fsmb2bcnf_ev_active_release,
+    /* FSMB2BCNF_E_RELEASE_COMPLETE */ fsmb2bcnf_ev_active_release_complete,
+    /* FSMB2BCNF_E_FEATURE          */ fsmb2bcnf_ev_active_feature,
+    /* FSMB2BCNF_E_FEATURE_ACK      */ fsmb2bcnf_ev_active_feature_ack,
+    /* FSMB2BCNF_E_OFFHOOK          */ NULL,
+    /* FSMB2BCNF_E_ONHOOK           */ fsmb2bcnf_ev_active_onhook,
+    /* FSMB2BCNF_E_LINE             */ NULL,
+    /* FSMB2BCNF_E_DIGIT_BEGIN      */ NULL,
+    /* FSMB2BCNF_E_DIGIT            */ NULL,
+    /* FSMB2BCNF_E_DIALSTRING       */ NULL,
+    /* FSMB2BCNF_E_MWI              */ NULL,
+    /* FSMB2BCNF_E_SESSION_AUDIT    */ NULL
+    }
+};
+
+static sm_table_t g_fsmb2bcnf_sm_table;
+sm_table_t *pfsmb2bcnf_sm_table = &g_fsmb2bcnf_sm_table;
+
+const char *
+fsmb2bcnf_state_name (int state)
+{
+    if ((state <= FSMB2BCNF_S_MIN) || (state >= FSMB2BCNF_S_MAX)) {
+        return (get_debug_string(GSM_UNDEFINED));
+    }
+
+    return (fsmb2bcnf_state_names[state]);
+}
+
+
+static int
+fsmb2bcnf_get_new_b2bcnf_id (void)
+{
+    static int b2bcnf_id = FSM_NO_ID;
+
+    if (++b2bcnf_id < FSM_NO_ID) {
+        b2bcnf_id = 1;
+    }
+
+    return (b2bcnf_id);
+}
+
+
+static void
+fsmb2bcnf_init_ccb (fsmcnf_ccb_t *ccb)
+{
+    if (ccb != NULL) {
+        ccb->cnf_id      = FSM_NO_ID;
+        ccb->cnf_call_id = CC_NO_CALL_ID;
+        ccb->cns_call_id = CC_NO_CALL_ID;
+        ccb->cnf_line    = CC_NO_LINE;
+        ccb->cns_line    = CC_NO_LINE;
+        ccb->bridged     = FALSE;
+        ccb->active      = FALSE;
+        ccb->cnf_ftr_ack = FALSE;
+        ccb->cnf_orig    = CC_SRC_MIN;
+    }
+}
+
+/**
+ *
+ * Get active trasnfer state machine information (in active state).
+ *
+ * @param none
+ *
+ * @return  fsm_fcb_t if there is a active trasnfer pending
+ *          else NULL
+ *
+ * @pre     (none)
+ */
+
+fsm_fcb_t *fsmb2bcnf_get_active_cnf(void)
+{
+    fsm_fcb_t    *fcb;
+    fsmcnf_ccb_t *b2bccb;
+
+    FSM_FOR_ALL_CBS(b2bccb, fsmb2bcnf_ccbs, FSMCNF_MAX_CCBS) {
+        fcb = fsm_get_fcb_by_call_id_and_type(b2bccb->cnf_call_id,
+                                               FSM_TYPE_B2BCNF);
+        if (fcb && fcb->state == FSMB2BCNF_S_ACTIVE) {
+            return(fcb);
+        }
+    }
+
+    return(NULL);
+}
+
+static fsmcnf_ccb_t *
+fsmb2bcnf_get_ccb_by_b2bcnf_id (int b2bcnf_id)
+{
+    fsmcnf_ccb_t   *ccb;
+    fsmcnf_ccb_t   *ccb_found = NULL;
+
+    FSM_FOR_ALL_CBS(ccb, fsmb2bcnf_ccbs, FSMCNF_MAX_CCBS) {
+        if (ccb->cnf_id == b2bcnf_id) {
+            ccb_found = ccb;
+
+            break;
+        }
+    }
+
+    return (ccb_found);
+}
+
+
+/*
+ *  Function: fsmb2bcnf_get_new_b2bcnf_context
+ *
+ *  Parameters:
+ *      b2bcnf_call_id: call_id for the call initiating the conference
+ *
+ *  Description: This function creates a new conference context by:
+ *               - getting a free ccb
+ *               - creating new b2bcnf_id and cns_call_id
+ *
+ *  Returns: ccb
+ *
+ */
+static fsmcnf_ccb_t *
+fsmb2bcnf_get_new_b2bcnf_context (callid_t b2bcnf_call_id, line_t line)
+{
+    const char fname[] = "fsmb2bcnf_get_new_b2bcnf_context";
+    fsmcnf_ccb_t *ccb;
+
+    ccb = fsmb2bcnf_get_ccb_by_b2bcnf_id(FSM_NO_ID);
+    if (ccb != NULL) {
+        ccb->cnf_id      = fsmb2bcnf_get_new_b2bcnf_id();
+        ccb->cnf_call_id = b2bcnf_call_id;
+        ccb->cnf_line    = line;
+        ccb->cns_line    = line;
+        ccb->cns_call_id = cc_get_new_call_id();
+
+        FSM_DEBUG_SM(get_debug_string(FSMB2BCNF_DBG_PTR), ccb->cnf_id,
+                     ccb->cnf_call_id, ccb->cns_call_id, fname, ccb);
+    } else {
+
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Failed to get new b2bccb.\n", fname);
+    }
+
+    return (ccb);
+}
+
+
+static fsmcnf_ccb_t *
+fsmb2bcnf_get_ccb_by_call_id (callid_t call_id)
+{
+    fsmcnf_ccb_t *ccb;
+    fsmcnf_ccb_t *ccb_found = NULL;
+
+    FSM_FOR_ALL_CBS(ccb, fsmb2bcnf_ccbs, FSMCNF_MAX_CCBS) {
+        if ((ccb->cnf_call_id == call_id) || (ccb->cns_call_id == call_id)) {
+            ccb_found = ccb;
+
+            break;
+        }
+    }
+
+    return (ccb_found);
+}
+
+
+static void
+fsmb2bcnf_update_b2bcnf_context (fsmcnf_ccb_t *ccb, callid_t old_call_id,
+                                 callid_t new_call_id)
+{
+    const char fname[] = "fsmb2bcnf_update_b2bcnf_context";
+
+    if (ccb != NULL) {
+        if (old_call_id == ccb->cnf_call_id) {
+            ccb->cnf_call_id = new_call_id;
+        } else if (old_call_id == ccb->cns_call_id) {
+            ccb->cns_call_id = new_call_id;
+        }
+
+        FSM_DEBUG_SM(get_debug_string(FSMB2BCNF_DBG_PTR), ccb->cnf_id,
+                     ccb->cnf_call_id, ccb->cns_call_id, fname, ccb);
+    }
+}
+
+/*
+ *      Function to get line number of other call associated in
+ *      transfer.
+ *
+ *  @param xcb and call_id.
+ *
+ *  @return void
+ *
+ */
+line_t
+fsmb2bcnf_get_other_line (fsmcnf_ccb_t *ccb, callid_t call_id)
+{
+    line_t other_line = CC_NO_LINE;
+
+    if (ccb != NULL) {
+        if (ccb->cnf_call_id == call_id) {
+            other_line = ccb->cns_line;
+        } else if (ccb->cns_call_id == call_id) {
+            other_line = ccb->cnf_line;
+        }
+    }
+
+    return (other_line);
+}
+
+static callid_t
+fsmb2bcnf_get_other_call_id (fsmcnf_ccb_t *ccb, callid_t call_id)
+{
+    callid_t other_call_id = CC_NO_CALL_ID;
+
+    if (ccb != NULL) {
+        if (ccb->cnf_call_id == call_id) {
+            other_call_id = ccb->cns_call_id;
+        } else if (ccb->cns_call_id == call_id) {
+            other_call_id = ccb->cnf_call_id;
+        }
+    }
+
+    return (other_call_id);
+}
+
+/*
+ *  Function: fsmb2bcnf_remove_fcb
+ *
+ *  Parameters:
+ *      b2bcnf_id:  b2bcnf_id for the conference
+ *      call_id: call_id that identifies the fcb to be removed
+ *
+ *  Description: This function will remove the fcb identified by the given
+ *               call_id from the ccb. And the function will free the ccb
+ *               if both fcbs have been removed.
+ *
+ *  Returns: none
+ *
+ *  Note: This is a helper function for fsmb2bcnf_cleanup. It allows fsmb2bcnf_cleanup
+ *        to cleanup one fcb (one call involved in the conference) independent
+ *        of the other involved fcb.
+ */
+static void
+fsmb2bcnf_remove_fcb (fsm_fcb_t *fcb, callid_t call_id)
+{
+    fsmcnf_ccb_t *ccb = fcb->b2bccb;
+
+    if (ccb != NULL) {
+        fsmb2bcnf_update_b2bcnf_context(ccb, call_id, CC_NO_CALL_ID);
+
+        /*
+         * Free the ccb if both fcb references have been removed.
+         */
+        if ((ccb->cnf_call_id == CC_NO_CALL_ID) &&
+            (ccb->cns_call_id == CC_NO_CALL_ID)) {
+            fsmb2bcnf_init_ccb(ccb);
+        }
+    }
+}
+
+
+static void
+fsmb2bcnf_cleanup (fsm_fcb_t *fcb, int fname, boolean both)
+{
+    fsm_fcb_t      *other_fcb = NULL;
+    callid_t        call_id       = fcb->call_id;
+    callid_t        other_call_id = CC_NO_CALL_ID;
+    line_t          other_line;
+
+    other_call_id = fsmb2bcnf_get_other_call_id(fcb->b2bccb, call_id);
+    other_line    = fsmb2bcnf_get_other_line(fcb->b2bccb, call_id);
+
+    if (other_call_id != CC_NO_CALL_ID) {
+        other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                    FSM_TYPE_B2BCNF);
+    }
+
+    if (fcb->b2bccb && (call_id == fcb->b2bccb->cnf_call_id)) {
+
+        if (other_call_id != CC_NO_CALL_ID) {
+            /*
+             * Not clearing consulation call, so change consultation
+             * call attribute to display connected softkey set.
+             * Do not change softkey set if it is a transfer o
+             */
+            cc_call_attribute(other_call_id, other_line, NORMAL_CALL);
+        }
+
+    }
+    /*
+     * Check if the user wanted to cleanup the whole ccb.
+     * If so, then we will grab the other fcb first and call this function
+     * again with this other fcb. The whole ccb will be freed after this block
+     * of code because both call_ids will be -1, which tells
+     * fsmb2bcnf_remove_fcb to free the ccb.
+     */
+    if (both) {
+        if (other_call_id != CC_NO_CALL_ID) {
+            if (other_fcb != NULL) {
+                fsmb2bcnf_cleanup(other_fcb, fname, FALSE);
+            }
+        }
+    }
+    /*
+     * Remove the reference to this fcb from the ccb.
+     */
+    fsmb2bcnf_remove_fcb(fcb, fcb->call_id);
+
+    /*
+     * Move this fcb to the IDLE state
+    */
+    fsm_change_state(fcb, fname, FSMB2BCNF_S_IDLE);
+
+    /*
+     * Reset the data for this fcb. The fcb is still included in a call
+     * so set the call_id and dcb values accordingly.
+     */
+    fsm_init_fcb(fcb, fcb->call_id, fcb->dcb, FSM_TYPE_B2BCNF);
+}
+
+
+void
+fsmb2bcnf_free_cb (fim_icb_t *icb, callid_t call_id)
+{
+    fsm_fcb_t *fcb = NULL;
+
+    if (call_id != CC_NO_CALL_ID) {
+        fcb = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_B2BCNF);
+
+        if (fcb != NULL) {
+            fsmb2bcnf_cleanup(fcb, __LINE__, FALSE);
+            fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE);
+        }
+    }
+}
+
+
+/*
+ * fsmb2bcnf_check_if_ok_to_setup_conf
+ *
+ * Description:
+ *    Checks if the requested call is ok to setup conference.
+ *
+ * Parameters:
+ *    call_id
+ *
+ * Returns: TRUE - if the call is ok to setup conference
+ *          FALSE - other cases
+ */
+boolean
+fsmb2bcnf_check_if_ok_to_setup_conf (callid_t call_id)
+{
+    fsmdef_dcb_t *dcb;
+
+    if (call_id == CC_NO_CALL_ID) {
+        return (FALSE);
+    }
+
+    dcb = fsm_get_dcb(call_id);
+
+    if(dcb && dcb->policy == CC_POLICY_CHAPERONE
+                   && dcb->is_conf_call == TRUE){
+       return (FALSE);
+    }
+
+    return (TRUE);
+}
+
+
+/*
+ * fsmb2bcnf_b2bcnf_invoke
+ *
+ * Description:
+ *    This function implements the conference feature invocation.
+ *    If the feature is already invoked and waiting for the
+ *    feature ack back from the SIP stack then no action taken.
+ *    Otherwise, the conf feature is invoked and state is SET.
+ *
+ * Parameters:
+ *    fsmdef_dcb_t *dcb - dcb associated with this call
+ *
+ * Returns: None
+ */
+static void
+fsmb2bcnf_cnf_invoke (callid_t call_id, callid_t target_call_id,
+                         line_t line, fsmcnf_ccb_t *ccb)
+{
+    sipspi_msg_t subscribe_msg;
+    ccsip_event_data_t *evt_data;
+
+    /*
+     * post SIPSPI_EV_CC_SUBSCRIBE to SIP stack
+     */
+    evt_data = (ccsip_event_data_t *)
+        cpr_malloc(sizeof(ccsip_event_data_t));
+    if (evt_data == NULL) {
+        return;
+    }
+    memset(evt_data, 0, sizeof(ccsip_event_data_t));
+    evt_data->type = EVENT_DATA_REMOTECC_REQUEST;
+    evt_data->u.remotecc_data.line = 0;
+    evt_data->u.remotecc_data.rcc_request_type = RCC_SOFTKEY_EVT;
+    evt_data->u.remotecc_data.rcc_int.rcc_softkey_event_msg.softkeyevent = RCC_SOFTKEY_CONFERENCE;
+    evt_data->u.remotecc_data.consult_gsm_id = target_call_id;
+    evt_data->u.remotecc_data.gsm_id = call_id;
+
+    memset(&subscribe_msg, 0, sizeof(sipspi_msg_t));
+    subscribe_msg.msg.subscribe.eventPackage = CC_SUBSCRIPTIONS_REMOTECC;
+    subscribe_msg.msg.subscribe.sub_id = CCSIP_SUBS_INVALID_SUB_ID;
+    subscribe_msg.msg.subscribe.auto_resubscribe = TRUE;
+    subscribe_msg.msg.subscribe.request_id = (long)ccb;
+    subscribe_msg.msg.subscribe.duration = 60;
+    subscribe_msg.msg.subscribe.subsNotCallbackTask = CC_SRC_GSM;
+    subscribe_msg.msg.subscribe.subsResCallbackMsgID = SUB_MSG_B2BCNF_SUBSCRIBE_RESP;
+    subscribe_msg.msg.subscribe.subsNotIndCallbackMsgID = SUB_MSG_B2BCNF_NOTIFY;
+    subscribe_msg.msg.subscribe.subsTermCallbackMsgID = SUB_MSG_B2BCNF_TERMINATE;
+    subscribe_msg.msg.subscribe.norefersub = FALSE;
+    subscribe_msg.msg.subscribe.eventData = evt_data;
+    subscribe_msg.msg.subscribe.dn_line = line;
+
+    (void)sub_int_subscribe(&subscribe_msg);
+}
+
+/**
+ *
+ * Cancel b2b conference feature by sending cancel event to SIP stack.
+ * This routine is used in roundtable phone.
+ *
+ * @param line, call_id, target_call_id, cause (implicit or explicit)
+ *
+ * @return  void
+ *
+ * @pre     (none)
+ */
+void
+fsmb2bcnf_feature_cancel (fsmcnf_ccb_t *ccb, line_t line, callid_t call_id,
+                          callid_t target_call_id,
+                          cc_rcc_skey_evt_type_e cause)
+{
+    cc_feature_data_t data;
+    fsm_fcb_t         *fcb_def;
+
+    fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+
+    if ((cause == CC_SK_EVT_TYPE_EXPLI) &&
+        (fcb_def != NULL) && ((fcb_def->dcb->selected == FALSE) &&
+            ((fcb_def->state == FSMDEF_S_OUTGOING_ALERTING) ||
+            ((fcb_def->state == FSMDEF_S_CONNECTED) &&
+            (fcb_def->dcb->spoof_ringout_requested == TRUE) &&
+            (fcb_def->dcb->spoof_ringout_applied == TRUE))))) {
+
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, call_id,
+                           line, CC_FEATURE_END_CALL, NULL);
+    }
+
+    fcb_def = fsm_get_fcb_by_call_id_and_type(target_call_id, FSM_TYPE_DEF);
+
+    if ((cause == CC_SK_EVT_TYPE_EXPLI) &&
+        (fcb_def != NULL) && ((fcb_def->dcb->selected == FALSE) &&
+            ((fcb_def->state == FSMDEF_S_OUTGOING_ALERTING) ||
+            ((fcb_def->state == FSMDEF_S_CONNECTED) &&
+            (fcb_def->dcb->spoof_ringout_requested == TRUE) &&
+            (fcb_def->dcb->spoof_ringout_applied == TRUE))))) {
+
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, target_call_id,
+                           line, CC_FEATURE_END_CALL, NULL);
+    }
+
+    data.cancel.target_call_id = target_call_id;
+    data.cancel.call_id = call_id;
+    data.cancel.cause = cause;
+
+    cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, call_id,
+                           line, CC_FEATURE_CANCEL, &data);
+}
+
+/*******************************************************************
+ * event functions
+ */
+
+
+static sm_rcs_t
+fsmb2bcnf_ev_idle_feature (sm_event_t *event)
+{
+    const char        *fname    = "fsmb2bcnf_ev_idle_feature";
+    fsm_fcb_t         *fcb      = (fsm_fcb_t *) event->data;
+    cc_feature_t      *msg      = (cc_feature_t *) event->msg;
+    callid_t           call_id  = msg->call_id;
+    line_t             line     = msg->line;
+    cc_srcs_t          src_id   = msg->src_id;
+    cc_features_t      ftr_id   = msg->feature_id;
+    cc_feature_data_t *ftr_data = &(msg->data);
+    fsmdef_dcb_t      *dcb      = fcb->dcb;
+    callid_t           cns_call_id;
+    sm_rcs_t           sm_rc    = SM_RC_CONT;
+    fsmcnf_ccb_t      *ccb;
+    int                free_lines;
+    cc_feature_data_t  data;
+    fsm_fcb_t         *other_fcb, *cns_fcb;
+    fsm_fcb_t         *fcb_def;
+    callid_t           other_call_id;
+    line_t             newcall_line = 0;
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_RCC:
+    case CC_SRC_UI:
+        switch (ftr_id) {
+        case CC_FEATURE_B2BCONF:
+            /* Connect the existing call to active trasnfer state
+             * machine. If the UI generates the event with target
+             * call_id in the data then terminate the existing consulatative
+             * call and link that to another call.
+            */
+            if (ftr_data && msg->data_valid &&
+                (ftr_data->b2bconf.target_call_id != CC_NO_CALL_ID)
+                && (cns_fcb = fsm_get_fcb_by_call_id_and_type(ftr_data->b2bconf.target_call_id,
+                            FSM_TYPE_B2BCNF)) != NULL) {
+                /*
+                 * Get a new ccb and new b2bcnf id - This is the handle that will
+                 * identify the b2bcnf.
+                 */
+                ccb = fsmb2bcnf_get_new_b2bcnf_context(call_id, line);
+
+                if (ccb==NULL || ccb->cnf_id == FSM_NO_ID) {
+                    return(SM_RC_END);
+                }
+
+                /* Conference origination id, required later. This indicates conf is
+                 * because of UI or because of CTI
+                 */
+                ccb->cnf_orig = src_id;
+
+                ccb->cns_call_id = ftr_data->b2bconf.target_call_id;
+                fcb->b2bccb = ccb;
+                cns_fcb->b2bccb = ccb;
+
+                /* Find line information for target call.
+                 */
+                fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+                if (fcb_def != NULL && fcb_def->dcb) {
+
+                    ccb->cns_line = fcb_def->dcb->line;
+
+                } else {
+
+                    return(SM_RC_END);
+                }
+
+                fsm_change_state(fcb, __LINE__, FSMB2BCNF_S_ACTIVE);
+
+                fsm_change_state(cns_fcb, __LINE__, FSMB2BCNF_S_ACTIVE);
+
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, ccb->cns_call_id,
+                                   ccb->cns_line, CC_FEATURE_B2BCONF, NULL);
+                return(SM_RC_END);
+
+            }
+
+
+            /*
+             * This call is the conference and we are initiating a local
+             * conference. So:
+             * 1. Make sure we have a free line to open a new call plane to
+             *    collect digits (and place call) for the consultation call,
+             * 2. Create a new conference context,
+             * 3. Place this call on hold,
+             * 4. Send a newcall feature back to the GSM so that the
+             *    consultation call can be initiated.
+             */
+
+            /*
+             * Check for any other active features which may block
+             * the conference.
+             */
+
+            /*
+             * The call must be in the connected state to initiate a conference
+             */
+            fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+            if ((fcb_def != NULL) && (fcb_def->state != FSMDEF_S_CONNECTED)) {
+                break;
+            }
+
+            /*
+             * Make sure we have a free line to start the consultation call.
+             */
+            //CSCsz38962 don't use expline for b2bcnf call
+            //free_lines = lsm_get_instances_available_cnt(line, TRUE);
+            free_lines = lsm_get_instances_available_cnt(line, FALSE);
+            if (free_lines <= 0) {
+                /*
+                 * No free lines - let the user know and end this request.
+                 */
+                fsm_display_no_free_lines();
+
+                break;
+            }
+
+            newcall_line = lsm_get_newcall_line(line);
+            if (newcall_line == NO_LINES_AVAILABLE) {
+                /*
+                 * Error Pass Limit- let the user know and end this request.
+                 */
+                lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT);
+
+                break;
+            }
+
+            /*
+             * Get a new ccb and new b2bcnf id - This is the handle that will
+             * identify the b2bcnf.
+             */
+            ccb = fsmb2bcnf_get_new_b2bcnf_context(call_id, line);
+
+            if (ccb==NULL || ccb->cnf_id == FSM_NO_ID) {
+                break;
+            }
+
+            ccb->cnf_orig = src_id;
+            fcb->b2bccb = ccb;
+            ccb->cns_line = newcall_line;
+
+            /*
+             * This call needs to go on hold so we can start the consultation
+             * call. Indicate feature indication should be send by setting
+             * call info type to hold and feature reason to conference.
+             */
+            memset(&data, 0, sizeof(data));
+            data.hold.call_info.type = CC_FEAT_HOLD;
+            data.hold.call_info.data.hold_resume_reason = CC_REASON_CONF;
+            data.hold.msg_body.num_parts = 0;
+            data.hold.call_info.data.call_info_feat_data.protect = TRUE;
+
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
+                           CC_FEATURE_HOLD, &data);
+
+            /*
+             * Initiate the consultation call.
+             */
+            data.newcall.cause = CC_CAUSE_CONF;
+            cns_call_id = ccb->cns_call_id;
+            sstrncpy(data.newcall.global_call_id,
+                     ftr_data->b2bconf.global_call_id, CC_GCID_LEN);
+            data.newcall.prim_call_id = ccb->cnf_call_id;
+            data.newcall.hold_resume_reason = CC_REASON_CONF;
+
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_call_id, newcall_line,
+                           CC_FEATURE_NEW_CALL, &data);
+
+            FSM_DEBUG_SM(get_debug_string(FSMB2BCNF_DBG_CNF_INITIATED),
+                         ccb->cnf_id, call_id, cns_call_id, __LINE__);
+
+            fsm_change_state(fcb, __LINE__, FSMB2BCNF_S_ACTIVE);
+
+            sm_rc = SM_RC_END;
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            sm_rc = SM_RC_DEF_CONT;
+            break;
+        }                       /* switch (ftr_id) */
+        break;
+
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_NOTIFY:
+
+            /* Since this message is specifically for conference, msg is
+             * consumed here and not forwarded
+             */
+            if ((msg->data.notify.subscription == CC_SUBSCRIPTIONS_REMOTECC)
+                && (msg->data.notify.data.rcc.feature == CC_FEATURE_B2BCONF)) {
+                sm_rc = SM_RC_END;
+            }
+            break;
+        case CC_FEATURE_NEW_CALL:
+            /*
+             * If this is the consultation call involved in a conference,
+             * then set the rest of the data required to make the conference
+             * happen. The data is the b2bcnf_id in the fcb. The data is set now
+             * because we did not have the fcb when the conference was
+             * initiated. The fcb is created when a new_call event is
+             * received by the FIM, not when a conference event is received.
+             *
+             * Or this could be the call that originated the conference and
+             * the person he was talking to (the conference target) has
+             * decided to conference the trasnferor to another party.
+             */
+
+            /*
+             * Ignore this event if this call is not involved in a conference.
+             */
+            ccb = fsmb2bcnf_get_ccb_by_call_id(call_id);
+            if (ccb == NULL) {
+                break;
+            }
+            fcb->b2bccb = ccb;
+
+            /*
+             * Determine what state this b2bcnf should be in (b2bcnfing or b2bcnfed).
+             * If the cnfrn key has only been hit once, then this call will
+             * be in the cnfing state. If it has been hit the second time,
+             * then this call should go to the cnfed state. The latter
+             * case only happens when the calls are conferenced and one
+             * of the remote ends has decided to transfer one leg of the cnf.
+             * And it just so happens that the other call involved in the cnf
+             * should match this call, so we can just use it's state to
+             * assign the state to this call.
+             */
+            other_call_id = fsmb2bcnf_get_other_call_id(ccb, call_id);
+            other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_B2BCNF);
+
+            if (other_fcb == NULL) {
+                GSM_DEBUG_ERROR(GSM_F_PREFIX"FCP not found \n", fname);
+            } else {
+               fsm_change_state(fcb, __LINE__, other_fcb->state);
+            }
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            sm_rc = SM_RC_DEF_CONT;
+            break;
+        }                       /* switch (ftr_id) */
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+        sm_rc = SM_RC_DEF_CONT;
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+
+static sm_rcs_t
+fsmb2bcnf_ev_active_release (sm_event_t *event)
+{
+
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    fsmcnf_ccb_t     *ccb     = fcb->b2bccb;
+
+    /* For round table phone wait for NOTIFY response, so do not
+     * clear the conf state machine
+     */
+    if (ccb->active == FALSE) {
+        fsmb2bcnf_feature_cancel(ccb, ccb->cnf_line, ccb->cnf_call_id,
+                                        ccb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+        fsmb2bcnf_cleanup((fsm_fcb_t *) event->data, __LINE__, TRUE);
+    }
+
+    return (SM_RC_CONT);
+}
+
+static sm_rcs_t
+fsmb2bcnf_ev_active_release_complete (sm_event_t *event)
+{
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    fsmcnf_ccb_t     *ccb     = fcb->b2bccb;
+
+    if (ccb->active == FALSE) {
+
+
+        fsmb2bcnf_cleanup((fsm_fcb_t *) event->data, __LINE__, TRUE);
+    }
+
+    return (SM_RC_CONT);
+}
+
+static sm_rcs_t
+fsmb2bcnf_ev_active_feature (sm_event_t *event)
+{
+    static const char fname[] = "fsmb2bcnf_ev_active_feature";
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_feature_t     *msg     = (cc_feature_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    fsmdef_dcb_t     *dcb     = fcb->dcb;
+    fsmcnf_ccb_t     *ccb     = fcb->b2bccb;
+    cc_srcs_t         src_id  = msg->src_id;
+    cc_features_t     ftr_id  = msg->feature_id;
+    cc_feature_data_t *feat_data   = &(msg->data);
+    sm_rcs_t          sm_rc   = SM_RC_CONT;
+    callid_t          other_call_id;
+    fsmdef_dcb_t     *other_dcb;
+    fsm_fcb_t        *other_fcb;
+    cc_action_data_t  action_data;
+    fsm_fcb_t        *cnf_fcb = NULL;
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    memset(&action_data, 0, sizeof(cc_action_data_t));
+
+    switch (src_id) {
+    case CC_SRC_UI:
+    case CC_SRC_RCC:
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_CANCEL:
+            sm_rc = SM_RC_END;
+            fsmb2bcnf_feature_cancel(ccb, ccb->cnf_line, ccb->cnf_call_id,
+                                ccb->cns_call_id,
+                                CC_SK_EVT_TYPE_EXPLI);
+            fsmb2bcnf_cleanup(fcb, __LINE__, TRUE);
+            break;
+
+        case CC_FEATURE_HOLD:
+            /* Do not send out protect parameter for CTI generated conference
+             * and send protect=true for swap or hold call
+            */
+            if ((msg->data_valid) &&
+                (feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_SWAP ||
+                feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_CONF ||
+                feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_INTERNAL))
+            {
+                feat_data->hold.call_info.data.call_info_feat_data.protect = TRUE;
+            } else if ((msg->data_valid) &&
+                (feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_RCC)) {
+                //Do nothing remote-cc will terminate the feature layer.
+
+            } else {
+                DEF_DEBUG(DEB_F_PREFIX"Invoke hold call_id = %d t_call_id=%d\n",
+                        DEB_F_PREFIX_ARGS(GSM, fname), ccb->cnf_call_id, ccb->cns_call_id);
+                //Actual hold to this call, so break the feature layer.
+                ui_terminate_feature(ccb->cnf_line, ccb->cnf_call_id, ccb->cns_call_id);
+                fsmb2bcnf_feature_cancel(ccb, ccb->cnf_line, ccb->cnf_call_id,
+                        ccb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                fsmb2bcnf_cleanup(fcb, __LINE__, TRUE);
+            }
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+
+        case CC_FEATURE_B2BCONF:
+            /* Connect the existing call to active trasnfer state
+             * machine. If the UI generates the event with target
+             * call_id in the data then terminate the existing consulatative
+             * call and link that to another call.
+            */
+            DEF_DEBUG(DEB_F_PREFIX"ACTIVE CNF call_id = %d, t_id = %d, cns_id=%d\n",
+                DEB_F_PREFIX_ARGS(GSM, fname), ccb->cnf_call_id,
+                    feat_data->b2bconf.target_call_id, ccb->cns_call_id);
+
+            if (feat_data && msg->data_valid &&
+                (feat_data->b2bconf.target_call_id != CC_NO_CALL_ID)) {
+                /* End existing consult call and then link another
+                * call with the trasfer. This is the case where User can
+                * select active call instead of consultative call
+                */
+                cnf_fcb = fsm_get_fcb_by_call_id_and_type(ccb->cns_call_id,
+                                                    FSM_TYPE_DEF);
+
+                /* If the call_id is different then active call has been picked
+                 */
+
+                if (ccb->cns_call_id != feat_data->b2bconf.target_call_id) {
+
+                    cnf_fcb = fsm_get_fcb_by_call_id_and_type(ccb->cns_call_id,
+                            FSM_TYPE_B2BCNF);
+
+                    if (cnf_fcb != NULL) {
+                        DEF_DEBUG(DEB_F_PREFIX"INVOKE ACTIVE CNF call_id = %d, t_id=%d\n",
+                            DEB_F_PREFIX_ARGS(GSM, fname), ccb->cnf_call_id,
+                        feat_data->b2bconf.target_call_id);
+
+                        cnf_fcb->b2bccb = ccb;
+                        fsm_change_state(cnf_fcb, __LINE__, FSMB2BCNF_S_ACTIVE);
+
+
+                        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, ccb->cns_call_id,
+                                   ccb->cnf_line, CC_FEATURE_B2BCONF, NULL);
+                    }
+
+                    return(SM_RC_END);
+                }
+            }
+            /*
+             * This is the second conference event for a local
+             * attended conference with consultation.
+             *
+             * The user is attempting to complete the conference, so
+             * resume the other leg of the conference call.
+             */
+            ccb->active = TRUE;
+
+            if (dcb) {
+                dcb->active_feature = CC_FEATURE_B2BCONF;
+            }
+
+            other_call_id = fsmb2bcnf_get_other_call_id(ccb, call_id);
+            other_dcb = fsm_get_dcb(other_call_id);
+
+            //Update confinvoked value through JPlatUi method.
+            ui_update_conf_invoked(other_dcb->line, other_call_id, TRUE);
+
+            fsmb2bcnf_cnf_invoke(fsmb2bcnf_get_other_call_id(ccb, call_id),
+                call_id, other_dcb->line, ccb);
+
+            other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_B2BCNF);
+
+            fsm_change_state(other_fcb, __LINE__, FSMB2BCNF_S_ACTIVE);
+
+            fsm_change_state(fcb, __LINE__, FSMB2BCNF_S_ACTIVE);
+
+            sm_rc = SM_RC_END;
+
+            break;
+
+        case CC_FEATURE_END_CALL:
+            /* Release ccbs related to conference and allow event to pass through
+             * fsmdef
+             */
+            fsmb2bcnf_feature_cancel(ccb, ccb->cnf_line, ccb->cnf_call_id,
+                        ccb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+            fsmb2bcnf_cleanup(fcb, __LINE__, TRUE);
+
+            break;
+
+
+        case CC_FEATURE_RESUME:
+            /* to achieve SCCP phone behaviour, if the 1st call is resumed
+             * then conference should be terminated.
+             */
+            if (ccb->cnf_orig == CC_SRC_RCC) {
+                if (ccb->cnf_call_id == call_id) {
+                    fsmb2bcnf_feature_cancel(ccb, ccb->cnf_line, ccb->cnf_call_id,
+                        ccb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                    fsmb2bcnf_cleanup(fcb, __LINE__, TRUE);
+                    cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                                  ((cc_state_data_t *) (&(dcb->caller_id))));
+                }
+            }
+            break;
+
+        case CC_FEATURE_NOTIFY:
+
+            if ((msg->data.notify.subscription == CC_SUBSCRIPTIONS_REMOTECC)
+                && (msg->data.notify.data.rcc.feature == CC_FEATURE_B2BCONF)) {
+
+                if (msg->data.notify.cause_code != RCC_SUCCESS) {
+                    fsmb2bcnf_feature_cancel(fcb->b2bccb, fcb->b2bccb->cnf_line,
+                                    fcb->b2bccb->cnf_call_id,
+                                    fcb->b2bccb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                }
+                /*
+                 * Conference key press has been accepted, so now terminate the
+                 * conference data structures. This call is now same as any other
+                 * regular call. All the future events are handled by fsmdef.
+                 */
+                fsmb2bcnf_cleanup(fcb, __LINE__, TRUE);
+                sm_rc = SM_RC_END;
+            }
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+        break;
+
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_CALL_PRESERVATION:
+             DEF_DEBUG(DEB_F_PREFIX"Invoke hold call_id = %d t_call_id=%d\n",
+                            DEB_F_PREFIX_ARGS(GSM, fname), ccb->cnf_call_id, ccb->cns_call_id);
+             //Actual hold to this call, so break the feature layer.
+             ui_terminate_feature(ccb->cnf_line, ccb->cnf_call_id, ccb->cns_call_id);
+             fsmb2bcnf_feature_cancel(ccb, ccb->cnf_line, ccb->cnf_call_id,
+                        ccb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+             fsmb2bcnf_cleanup(fcb, __LINE__, TRUE);
+             break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+static sm_rcs_t
+fsmb2bcnf_ev_active_feature_ack (sm_event_t *event)
+{
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_feature_ack_t *msg     = (cc_feature_ack_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    cc_srcs_t         src_id  = msg->src_id;
+    cc_features_t     ftr_id  = msg->feature_id;
+    sm_rcs_t          sm_rc   = SM_RC_CONT;
+    cc_action_data_t  data;
+    callid_t          other_call_id;
+    callid_t          other_ui_id;
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    memset(&data, 0, sizeof(cc_action_data_t));
+
+    switch (src_id) {
+    case CC_SRC_GSM:
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_B2BCONF:
+            /* Handle 2nd conference key press completion NOTIFY */
+            if (msg->cause == CC_CAUSE_ERROR) {
+                other_call_id =
+                    fsmb2bcnf_get_other_call_id(fcb->b2bccb, call_id);
+
+                other_ui_id = lsm_get_ui_id(other_call_id);
+                ui_set_call_status(platform_get_phrase_index_str(CONF_CANNOT_COMPLETE),
+                                   msg->line, other_ui_id);
+                fsmb2bcnf_feature_cancel(fcb->b2bccb, fcb->b2bccb->cnf_line, fcb->b2bccb->cnf_call_id,
+                                    fcb->b2bccb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                fsmb2bcnf_cleanup(fcb, __LINE__, TRUE);
+                break;
+            }
+
+            sm_rc = SM_RC_END;
+            break;
+
+        case CC_FEATURE_NOTIFY:
+
+            if ((msg->data.notify.subscription == CC_SUBSCRIPTIONS_REMOTECC) &&
+                (msg->data.notify.data.rcc.feature == CC_FEATURE_B2BCONF)) {
+
+                /*
+                 * Conference key press has been accepted, so now terminate the
+                 * conference data structures. This call is now same as any other
+                 * regular call. All the future events are handled by fsmdef.
+                 */
+                fsmb2bcnf_cleanup(fcb, __LINE__, TRUE);
+                sm_rc = SM_RC_END;
+            }
+
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+void
+fsmb2bcnf_get_sub_call_id_from_ccb(fsmcnf_ccb_t *ccb, callid_t *cnf_call_id,
+                callid_t *cns_call_id)
+{
+    static const char fname[] = "fsmb2bcnf_get_sub_call_id_from_ccb";
+
+    DEF_DEBUG(DEB_F_PREFIX"call_id = %d t_call_id=%d\n",
+                        DEB_F_PREFIX_ARGS(GSM, fname), ccb->cnf_call_id, ccb->cns_call_id);
+
+    *cnf_call_id = ccb->cnf_call_id;
+    *cns_call_id = ccb->cns_call_id;
+}
+
+static sm_rcs_t
+fsmb2bcnf_ev_active_onhook (sm_event_t *event)
+{
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    fsmcnf_ccb_t     *ccb     = fcb->b2bccb;
+    cc_onhook_t      *msg     = (cc_onhook_t *) event->msg;
+
+    /* For RT phone active call list can be invoked during
+     * conf and that genertes onhook event for existing
+     * consult call. Conf state machine is not terminated
+     */
+    if (msg->active_list == CC_REASON_ACTIVECALL_LIST) {
+
+        ccb->cns_line = CC_NO_LINE;
+        ccb->cns_call_id = CC_NO_CALL_ID;
+
+    } else {
+        fsmb2bcnf_feature_cancel(ccb, ccb->cnf_line, ccb->cnf_call_id,
+                                    ccb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+        fsmb2bcnf_cleanup((fsm_fcb_t *) event->data, __LINE__, TRUE);
+    }
+
+    return (SM_RC_CONT);
+}
+
+cc_int32_t
+fsmb2bcnf_show_cmd (cc_int32_t argc, const char *argv[])
+{
+    fsmcnf_ccb_t *ccb;
+    int           i = 0;
+
+    /*
+     * check if need help
+     */
+    if ((argc == 2) && (argv[1][0] == '?')) {
+        debugif_printf("show fsmb2bcnf\n");
+        return (0);
+    }
+
+    debugif_printf("\n-------------------------- FSMB2BCNF ccbs --------------------------");
+    debugif_printf("\ni   b2bcnf_id  ccb         cnf_call_id  cns_call_id  active  bridged");
+    debugif_printf("\n--------------------------------------------------------------------"
+         "\n");
+
+    FSM_FOR_ALL_CBS(ccb, fsmb2bcnf_ccbs, FSMCNF_MAX_CCBS) {
+        debugif_printf("%-2d  %-6d  0x%08p  %-11d  %-11d  %-6d  %-7d\n",
+                       i++, ccb->cnf_id, ccb, ccb->cnf_call_id,
+                       ccb->cns_call_id, ccb->active, ccb->bridged);
+    }
+
+    return (0);
+}
+
+
+void
+fsmb2bcnf_init (void)
+{
+    fsmcnf_ccb_t *ccb;
+    static const char *fname = "fsmb2bcnf_init";
+
+
+    /*
+     * Initialize the ccbs.
+     */
+    fsmb2bcnf_ccbs = (fsmcnf_ccb_t *)
+        cpr_malloc(sizeof(fsmcnf_ccb_t) * FSMCNF_MAX_CCBS);
+
+    if (fsmb2bcnf_ccbs == NULL) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Failed to allocate memory \
+                forb2bcnf ccbs.\n", fname);
+        return;
+    }
+
+    FSM_FOR_ALL_CBS(ccb, fsmb2bcnf_ccbs, FSMCNF_MAX_CCBS) {
+        fsmb2bcnf_init_ccb(ccb);
+    }
+
+    /*
+     * Initialize the state/event table.
+     */
+    g_fsmb2bcnf_sm_table.min_state = FSMB2BCNF_S_MIN;
+    g_fsmb2bcnf_sm_table.max_state = FSMB2BCNF_S_MAX;
+    g_fsmb2bcnf_sm_table.min_event = CC_MSG_MIN;
+    g_fsmb2bcnf_sm_table.max_event = CC_MSG_MAX;
+    g_fsmb2bcnf_sm_table.table     = (&(fsmb2bcnf_function_table[0][0]));
+}
+
+
+callid_t
+fsmb2bcnf_get_primary_call_id (callid_t call_id)
+{
+    fsmcnf_ccb_t *ccb;
+
+    ccb = fsmb2bcnf_get_ccb_by_call_id(call_id);
+
+    if (ccb && (ccb->cns_call_id == call_id)) {
+        return (fsmb2bcnf_get_other_call_id(ccb, call_id));
+    } else {
+        return (CC_NO_CALL_ID);
+    }
+}
+
+callid_t
+fsmb2bcnf_get_consult_call_id (callid_t call_id)
+{
+    fsmcnf_ccb_t *ccb;
+
+    ccb = fsmb2bcnf_get_ccb_by_call_id(call_id);
+
+    if (ccb && ccb->cnf_call_id == call_id) {
+        return (fsmb2bcnf_get_other_call_id(ccb, call_id));
+    } else {
+        return (CC_NO_CALL_ID);
+    }
+}
+
+int
+fsmutil_is_b2bcnf_consult_call (callid_t call_id)
+{
+    return fsmutil_is_cnf_consult_leg(call_id, fsmb2bcnf_ccbs, FSMCNF_MAX_CCBS);
+}
+
+boolean
+fsmb2bcnf_is_rcc_orig_b2bcnf (callid_t call_id)
+{
+    fsmcnf_ccb_t *ccb;
+
+    ccb = fsmb2bcnf_get_ccb_by_call_id(call_id);
+    if (ccb && ccb->cnf_orig == CC_SRC_RCC) {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+void
+fsmb2bcnf_shutdown (void)
+{
+    cpr_free(fsmb2bcnf_ccbs);
+    fsmb2bcnf_ccbs = NULL;
+}
diff --git a/libs/sipcc/core/gsm/fsmcac.c b/libs/sipcc/core/gsm/fsmcac.c
new file mode 100755 (executable)
index 0000000..21c32ef
--- /dev/null
@@ -0,0 +1,707 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "phntask.h"
+#include "fsm.h"
+#include "fim.h"
+#include "lsm.h"
+#include "sm.h"
+#include "gsm.h"
+#include "ccapi.h"
+#include "phone_debug.h"
+#include "debug.h"
+#include "text_strings.h"
+#include "sip_interface_regmgr.h"
+#include "resource_manager.h"
+#include "singly_link_list.h"
+#include "platform_api.h"
+
+#define CAC_FAILURE_TIMEOUT 5
+cc_int32_t g_cacDebug = 0;
+
+/* CAC key */
+typedef struct {
+    callid_t  call_id;
+} cac_key_t;
+
+
+typedef enum {
+    FSM_CAC_IDLE = 0,
+    FSM_CAC_REQ_PENDING = 1,
+    FSM_CAC_REQ_RESP = 2
+} fsm_cac_state_e;
+
+/* CAC structure to hold the data
+ */
+typedef struct cac_data_t {
+    void                *msg_ptr;
+    callid_t            call_id;
+    void                *cac_fail_timer;
+    fsm_cac_state_e     cac_state;
+    uint32_t            sessions;
+} cac_data_t;
+
+static sll_handle_t s_cac_list = NULL;
+
+
+
+/*
+ *      Function responsible for searching the list waiting for
+ *  bandwidth allocation.
+ *
+ *  @param cac_data_t *key_p - pointer to the key.
+ *  @param cac_data_t *cac_data - cac data.
+ *
+ *  @return void
+ *
+ */
+static sll_match_e
+fsm_cac_match_call_id (cac_data_t *key_p, cac_data_t *cac_data)
+{
+    if (cac_data->call_id == key_p->call_id) {
+
+        return SLL_MATCH_FOUND;
+    }
+
+    return SLL_MATCH_NOT_FOUND;
+
+}
+
+/*
+ *      Function responsible to create new data information
+ *  for cac.
+ *
+ *  @param none.
+ *
+ *  @return cac_data_t *
+ *
+ */
+static cac_data_t *
+fsm_get_new_cac_data (void)
+{
+    static const char *fname="fsm_get_new_cac_data";
+    cac_data_t *cac_mem;
+
+    cac_mem = (cac_data_t *) cpr_malloc(sizeof(cac_data_t));
+
+    if (cac_mem == NULL) {
+        CAC_ERROR(CAC_F_PREFIX"No memory for CAC data.\n",
+                DEB_F_PREFIX_ARGS("CAC", fname));
+        return (NULL);
+    }
+
+    memset(cac_mem, 0, sizeof(cac_data_t));
+    return (cac_mem);
+}
+
+/**
+ *
+ * Release all cac related data. This includes timer, cac_data
+ * and message buffers
+ *
+ * @param cac_data       cac data structure
+ *
+ * @return  none.
+ *
+ * @pre     (cac_data not_eq NULL)
+ */
+
+static void
+fsm_clear_cac_data (cac_data_t *cac_data)
+{
+
+    if (cac_data->cac_fail_timer) {
+        (void) cprCancelTimer(cac_data->cac_fail_timer);
+
+        (void) cprDestroyTimer(cac_data->cac_fail_timer);
+    }
+
+    (void) sll_remove(s_cac_list, cac_data);
+
+    fim_free_event(cac_data->msg_ptr);
+
+    /* Release buffer too */
+    cpr_free(cac_data->msg_ptr);
+
+    cpr_free(cac_data);
+
+}
+
+/**
+ *
+ * Notifies the SIP stack and UI that CAC has failed.
+ *
+ * @param cac_data       cac data structure
+ *
+ * @return  none.
+ *
+ * @pre     (cac_data not_eq NULL)
+ */
+
+static void fsm_cac_notify_failure (cac_data_t *cac_data)
+{
+    const char fname[] = "fsm_cac_notify_failure";
+    cc_setup_t     *msg = (cc_setup_t *) cac_data->msg_ptr;
+    cc_msgs_t       msg_id   = msg->msg_id;
+    callid_t        call_id  = msg->call_id;
+    line_t          line     = msg->line;
+    int             event_id = msg_id;
+    cc_srcs_t       src_id  = msg->src_id;
+
+    /* Notify UI about the failure */
+    lsm_ui_display_notify_str_index(STR_INDEX_NO_BAND_WIDTH);
+
+    /* Send response from network side regarding the failure */
+    if (event_id == CC_MSG_SETUP &&
+            src_id == CC_SRC_SIP) {
+        DEF_DEBUG(DEB_F_PREFIX"Send CAC failure to SIP %d.\n",
+                    DEB_F_PREFIX_ARGS("CAC", fname), cac_data->call_id);
+        cc_int_release(CC_SRC_GSM, CC_SRC_SIP, call_id, line,
+                       CC_CAUSE_CONGESTION, NULL, NULL);
+    } else {
+        /* If the cac failed, GSM is not spinning yet, so just send the
+         * information to UI in this case. Other case, where GSM receives event
+         * will send the information from GSM.
+         * If the UI is not cleaned up, session infomation is not cleared.
+         */
+        ui_call_state(evOnHook, line, call_id, CC_CAUSE_CONGESTION);
+    }
+
+}
+
+/**
+ *
+ * Initialize the cac timer. This timer is responsible for cleanup if the
+ * cac response is not received from lower layer.
+ *
+ * @param cac_data       cac data structure
+ *        timeout        specify the time out in sec
+ *
+ * @return  true if the timer is created scuccessfully.
+ *          false if the timer is not created.
+ *
+ * @pre     (cac_data not_eq NULL)
+ */
+
+static boolean
+fsm_init_cac_failure_timer(cac_data_t *cac_data, uint32_t timeout)
+{
+    const char fname[] = "fsm_init_cac_failure_timer";
+
+    CAC_DEBUG(DEB_F_PREFIX"cac_data call_id=%x\n",
+              DEB_F_PREFIX_ARGS("CAC", fname),
+              cac_data->call_id);
+
+    cac_data->cac_fail_timer =
+        cprCreateTimer("CAC failure timer", GSM_CAC_FAILURE_TIMER, TIMER_EXPIRATION,
+                       gsm_msg_queue);
+
+    if (cac_data->cac_fail_timer == NULL) {
+        CAC_ERROR(CAC_F_PREFIX"CAC Timer allocation failed.\n",
+                                    DEB_F_PREFIX_ARGS("CAC", fname));
+        return(FALSE);
+    }
+
+    (void) cprStartTimer(cac_data->cac_fail_timer, timeout * 1000,
+                         (void *)(long)cac_data->call_id);
+
+    return(TRUE);
+}
+
+/**
+ *
+ * Serches through cac link list and returns cac_data
+ * based on call_id. This search is a singly link list search.
+ *
+ * @param call_id       call_id of the call
+ *
+ * @return  cac_data if found in the list
+ *          NULL     if there is no cac_data
+ *
+ * @pre     (call_id not_eq CC_NO_CALL_ID)
+ */
+
+static cac_data_t *
+fsm_cac_get_data_by_call_id (callid_t call_id)
+{
+    const char fname[] = "fsm_cac_get_data_by_call_id";
+    cac_data_t *cac_data;
+
+    cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
+
+    while (cac_data != NULL) {
+
+        if (cac_data->call_id == call_id) {
+            CAC_DEBUG(DEB_F_PREFIX"cac_data found call_id=%x\n",
+              DEB_F_PREFIX_ARGS("CAC", fname),
+              cac_data->call_id);
+            return(cac_data);
+        }
+
+        cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
+
+    }
+
+    CAC_DEBUG(DEB_F_PREFIX"cac_data NOT found.\n",
+        DEB_F_PREFIX_ARGS("CAC", fname));
+    return(NULL);
+}
+
+/**
+ *
+ * Initialize cac module by enabling debugs and creating a cac list.
+ *
+ * @param void
+ *
+ * @return  void
+ *
+ * @pre     (NULL)
+ */
+void fsm_cac_init (void)
+{
+    const char fname[] = "fsm_cac_init";
+
+
+    /* allocate and initialize cac list */
+    s_cac_list = sll_create((sll_match_e(*)(void *, void *))
+                            fsm_cac_match_call_id);
+
+    if (s_cac_list == NULL) {
+        CAC_ERROR(CAC_F_PREFIX"CAC list creation failed.\n",
+                                    DEB_F_PREFIX_ARGS("CAC", fname));
+
+    }
+}
+
+/**
+ *
+ * clears all the entries in the cac list
+ *
+ * @param void
+ *
+ * @return  void
+ *
+ * @pre     (NULL)
+ */
+void fsm_cac_clear_list (void)
+{
+    const char fname[] = "fsm_cac_clear_list";
+    cac_data_t *cac_data;
+    cac_data_t *prev_cac_data;
+
+    DEF_DEBUG(DEB_F_PREFIX"Clear all pending CAC dat.\n",
+                DEB_F_PREFIX_ARGS("CAC", fname));
+
+    cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
+
+    while (cac_data != NULL) {
+
+        prev_cac_data = cac_data;
+        cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
+
+        fsm_cac_notify_failure(prev_cac_data);
+        fsm_clear_cac_data(prev_cac_data);
+    }
+
+}
+
+/**
+ *
+ * Shutdown cac module, clears all the pending cac requests
+ *
+ * @param void
+ *
+ * @return  void
+ *
+ * @pre     (NULL)
+ */
+void fsm_cac_shutdown (void)
+{
+
+    fsm_cac_clear_list();
+
+    sll_destroy(s_cac_list);
+
+    s_cac_list = NULL;
+}
+
+/**
+ *
+ * Check if there are pending CAC requests
+ *
+ * @param none
+ *
+ * @return  cac_data returns first pending request.
+ *
+ * @pre     (NULL)
+ */
+static cac_data_t *
+fsm_cac_check_if_pending_req (void)
+{
+    cac_data_t *cac_data;
+
+    cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
+
+    while (cac_data != NULL) {
+
+        if (cac_data->cac_state == FSM_CAC_REQ_PENDING ||
+                cac_data->cac_state == FSM_CAC_IDLE) {
+
+            return(cac_data);
+        }
+
+        cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
+
+    }
+
+    return(NULL);
+}
+
+/**
+ *
+ * Check if there are pending CAC requests
+ *
+ * @param none
+ *
+ * @return  cac_data returns first pending request.
+ *
+ * @pre     (NULL)
+ */
+static cc_causes_t
+fsm_cac_process_bw_allocation (cac_data_t *cac_data)
+{
+    const char fname[] = "fsm_cac_process_bw_allocation";
+
+    if (lsm_allocate_call_bandwidth(cac_data->call_id, cac_data->sessions) ==
+            CC_CAUSE_CONGESTION) {
+
+        DEF_DEBUG(DEB_F_PREFIX"CAC Allocation failed.\n",
+                DEB_F_PREFIX_ARGS("CAC", fname));
+
+        fsm_cac_notify_failure(cac_data);
+
+        fsm_clear_cac_data(cac_data);
+
+        return(CC_CAUSE_CONGESTION);
+    }
+
+    cac_data->cac_state = FSM_CAC_REQ_PENDING;
+
+    return(CC_CAUSE_OK);
+}
+
+/**
+ *
+ * Check if there are pending CAC requests
+ *
+ * @param call_id request a cac for this call_id
+ *       sessions  number of sessions in the request
+ *        msg    ccapi msg, that is held to process
+ *              till cac response is received.
+ *
+ * @return  CC_CAUSE_BW_OK if the bandwidth is received.
+ *          CC_CAUSE_Ok Call returned successfully, not sure about BW yet
+ *          CC_CAUSE_ERROR: Call returned with failure.
+ *
+ * @pre     (NULL)
+ */
+cc_causes_t
+fsm_cac_call_bandwidth_req (callid_t call_id, uint32_t sessions,
+                            void *msg)
+{
+    const char fname[] = "fsm_cac_call_bandwidth_req";
+    cac_data_t *cac_data, *cac_pend_data;
+
+    /* If wlan not connected return OK */
+    cac_data = fsm_get_new_cac_data();
+
+    if (cac_data == NULL) {
+
+        return(CC_CAUSE_CONGESTION);
+    }
+
+    cac_data->msg_ptr = msg;
+    cac_data->call_id = call_id;
+    cac_data->cac_state = FSM_CAC_IDLE;
+    cac_data->sessions = sessions;
+
+    fsm_init_cac_failure_timer(cac_data, CAC_FAILURE_TIMEOUT);
+
+    /* Make sure there is no pending requests before submitting
+     * another one
+     */
+    if ((cac_pend_data = fsm_cac_check_if_pending_req()) == NULL) {
+
+        /*
+        * Make sure sufficient bandwidth available to make a outgoing call. This
+        * should be done before allocating other resources.
+        */
+        DEF_DEBUG(DEB_F_PREFIX"CAC request for %d sessions %d.\n",
+                DEB_F_PREFIX_ARGS("CAC", fname), call_id, sessions);
+
+        if (fsm_cac_process_bw_allocation(cac_data) == CC_CAUSE_CONGESTION) {
+
+            return(CC_CAUSE_CONGESTION);
+        }
+
+        cac_data->cac_state = FSM_CAC_REQ_PENDING;
+
+    } else if (cac_pend_data->cac_state == FSM_CAC_IDLE) {
+
+        if (fsm_cac_process_bw_allocation(cac_pend_data) ==
+                    CC_CAUSE_CONGESTION) {
+
+            /* Clear all remaining data */
+            fsm_cac_clear_list();
+
+            return(CC_CAUSE_CONGESTION);
+        }
+
+    }
+
+    (void) sll_append(s_cac_list, cac_data);
+
+    return(CC_CAUSE_OK);
+
+}
+
+/**
+ *
+ * This is called by gsm to cleanup the cac data. If there are any
+ * pending CAC requests and far end cancels the call, the pending
+ * request has to be canceled.
+ *
+ * @param call_id - call_id of the request
+ *
+ * @return  none.
+ *
+ * @pre     (NULL)
+ */
+void fsm_cac_call_release_cleanup (callid_t call_id)
+{
+    cac_data_t *cac_data;
+
+    cac_data = fsm_cac_get_data_by_call_id(call_id);
+
+    if (cac_data) {
+
+        sll_remove(s_cac_list, cac_data);
+
+        fsm_clear_cac_data(cac_data);
+    }
+
+}
+
+
+/**
+ *
+ * Called when the bandwidth response with available bw is received. This
+ * also process held ccapi messages through fim event chain
+ *
+ * @param none
+ *
+ * @return  CC_CAUSE_NO_RESOURCE No bandwidth
+ *          CC_CAUSE_OK if ok
+ *
+ *
+ * @pre     (NULL)
+ */
+
+cc_causes_t
+fsm_cac_process_bw_avail_resp (void)
+{
+    const char      fname[] = "fsm_cac_process_bw_avail_resp";
+    cac_data_t      *cac_data = NULL;
+    cac_data_t      *next_cac_data = NULL;
+
+
+    cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
+
+    if (cac_data != NULL) {
+
+        switch (cac_data->cac_state) {
+        default:
+        case FSM_CAC_IDLE:
+            DEF_DEBUG(DEB_F_PREFIX"No Pending CAC request.\n",
+                DEB_F_PREFIX_ARGS("CAC", fname));
+            /*
+            * Make sure sufficient bandwidth available to make a outgoing call. This
+            * should be done before allocating other resources.
+            */
+            if (fsm_cac_process_bw_allocation(cac_data) == CC_CAUSE_CONGESTION) {
+
+                sll_remove(s_cac_list, cac_data);
+
+                return(CC_CAUSE_NO_RESOURCE);
+            }
+
+
+            break;
+        case FSM_CAC_REQ_PENDING:
+
+            next_cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
+            sll_remove(s_cac_list, cac_data);
+
+            /* Request for the next bandwidth */
+            DEF_DEBUG(DEB_F_PREFIX"Process pending responses %d.\n",
+                DEB_F_PREFIX_ARGS("CAC", fname), cac_data->call_id);
+
+            /* Let GSM process completed request */
+            fim_process_event(cac_data->msg_ptr, TRUE);
+
+            fsm_clear_cac_data(cac_data);
+
+            if (next_cac_data != NULL) {
+                /*
+                 * Make sure sufficient bandwidth available to make a outgoing call. This
+                 * should be done before allocating other resources.
+                 */
+                DEF_DEBUG(DEB_F_PREFIX"Requesting next allocation %d.\n",
+                    DEB_F_PREFIX_ARGS("CAC", fname), next_cac_data->call_id);
+
+                if (fsm_cac_process_bw_allocation(next_cac_data) ==
+                                CC_CAUSE_CONGESTION) {
+
+                    /* If the next data was in idle state and the request fialed
+                     * then clean up the remaining list
+                     */
+                    if (next_cac_data->cac_state == FSM_CAC_IDLE) {
+                        /* Clear all remaining data */
+                        fsm_cac_clear_list();
+                    } else {
+
+                        sll_remove(s_cac_list, next_cac_data);
+                    }
+
+                    return(CC_CAUSE_NO_RESOURCE);
+                }
+
+            }
+
+            break;
+        }
+
+    }
+
+    return(CC_CAUSE_NO_RESOURCE);
+
+}
+
+/**
+ *
+ * Called when the bandwidth response with failed bw is received. This
+ * also process held ccapi messages through fim event chain
+ *
+ * @param none
+ *
+ * @return  CC_CAUSE_NO_RESOURCE No bandwidth
+ *          CC_CAUSE_OK if ok
+ *
+ *
+ * @pre     (NULL)
+ */
+cc_causes_t
+fsm_cac_process_bw_failed_resp (void)
+{
+    const char      fname[] = "fsm_cac_process_bw_avail_resp";
+    cac_data_t      *cac_data = NULL;
+    cac_data_t      *next_cac_data = NULL;
+
+
+    cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
+
+    if (cac_data != NULL) {
+
+        switch (cac_data->cac_state) {
+        default:
+        case FSM_CAC_IDLE:
+            DEF_DEBUG(DEB_F_PREFIX"No Pending request.\n",
+                DEB_F_PREFIX_ARGS("CAC", fname));
+            /*
+            * Make sure sufficient bandwidth available to make a outgoing call. This
+            * should be done before allocating other resources.
+            */
+            if (fsm_cac_process_bw_allocation(cac_data) == CC_CAUSE_CONGESTION) {
+
+                sll_remove(s_cac_list, cac_data);
+
+                return(CC_CAUSE_NO_RESOURCE);
+            }
+
+            break;
+        case FSM_CAC_REQ_PENDING:
+
+            next_cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
+
+            sll_remove(s_cac_list, cac_data);
+
+            /* Request for the next bandwidth */
+            DEF_DEBUG(DEB_F_PREFIX"Process pending responses even after failure.\n",
+                DEB_F_PREFIX_ARGS("CAC", fname));
+
+            /* Let GSM process completed request */
+            fsm_cac_notify_failure(cac_data);
+
+            fsm_clear_cac_data(cac_data);
+
+            if (next_cac_data != NULL) {
+
+                /*
+                 * Make sure sufficient bandwidth available to make a outgoing call. This
+                 * should be done before allocating other resources.
+                 */
+                if (fsm_cac_process_bw_allocation(next_cac_data) == CC_CAUSE_CONGESTION) {
+
+                    /* If the next data was in idle state and the request fialed
+                     * then clean up the remaining list
+                     */
+                    if (next_cac_data->cac_state == FSM_CAC_IDLE) {
+                        /* Clear all remaining data */
+                        fsm_cac_clear_list();
+                    } else {
+
+                        sll_remove(s_cac_list, next_cac_data);
+                    }
+
+                    return(CC_CAUSE_NO_RESOURCE);
+                }
+
+            }
+
+            break;
+        }
+
+    }
+
+    return(CC_CAUSE_NO_RESOURCE);
+}
+
+/**
+ *
+ * Process time-out event. This cause cac data to send failure notifications.
+ *
+ * @param   void *tmr_data - timer data
+ *
+ * @return  none
+ *
+ *
+ * @pre     (NULL)
+ */
+void
+fsm_cac_process_bw_fail_timer (void *tmr_data)
+{
+    const char      fname[] = "fsm_cac_process_bw_fail_timer";
+
+    DEF_DEBUG(DEB_F_PREFIX"CAC request timedout %d.\n",
+                    DEB_F_PREFIX_ARGS("CAC", fname), (callid_t)(long)tmr_data);
+
+    /* Time-out causes same set of processing as bw failure
+     */
+    fsm_cac_process_bw_failed_resp();
+
+}
+
diff --git a/libs/sipcc/core/gsm/fsmcnf.c b/libs/sipcc/core/gsm/fsmcnf.c
new file mode 100755 (executable)
index 0000000..7f92bfc
--- /dev/null
@@ -0,0 +1,1749 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_stdio.h"
+#include "fsm.h"
+#include "fim.h"
+#include "lsm.h"
+#include "sm.h"
+#include "ccapi.h"
+#include "phone_debug.h"
+#include "text_strings.h"
+#include "config.h"
+#include "debug.h"
+#include "gsm_sdp.h"
+#include "regmgrapi.h"
+#include "platform_api.h"
+
+static fsmcnf_ccb_t *fsmcnf_ccbs;
+
+static int softkey_mask_list[MAX_SOFT_KEYS];
+
+#define FSMCNF_CONNECTED_SET "CONNECTED"
+
+typedef enum {
+    FSMCNF_S_MIN = -1,
+    FSMCNF_S_IDLE,
+    FSMCNF_S_CNFING,
+    FSMCNF_S_CNFED,
+    FSMCNF_S_MAX
+} fsmcnf_states_t;
+
+static const char *fsmcnf_state_names[] = {
+    "IDLE",
+    "CNFING",
+    "CNFED"
+};
+
+
+static sm_rcs_t fsmcnf_ev_idle_setup(sm_event_t *event);
+static sm_rcs_t fsmcnf_ev_idle_feature(sm_event_t *event);
+static sm_rcs_t fsmcnf_ev_cnfing_release(sm_event_t *event);
+static sm_rcs_t fsmcnf_ev_cnfing_feature(sm_event_t *event);
+static sm_rcs_t fsmcnf_ev_cnfing_onhook(sm_event_t *event);
+static sm_rcs_t fsmcnf_ev_cnfed_release(sm_event_t *event);
+static sm_rcs_t fsmcnf_ev_cnfed_feature(sm_event_t *event);
+static sm_rcs_t fsmcnf_ev_cnfed_feature_ack(sm_event_t *event);
+static sm_rcs_t fsmcnf_ev_cnfed_onhook(sm_event_t *event);
+
+static sm_function_t fsmcnf_function_table[FSMCNF_S_MAX][CC_MSG_MAX] =
+{
+/* FSMCNF_S_IDLE ------------------------------------------------------------ */
+    {
+    /* FSMCNF_E_SETUP            */ fsmcnf_ev_idle_setup,
+    /* FSMCNF_E_SETUP_ACK        */ NULL,
+    /* FSMCNF_E_PROCEEDING       */ NULL,
+    /* FSMCNF_E_ALERTING         */ NULL,
+    /* FSMCNF_E_CONNECTED        */ NULL,
+    /* FSMCNF_E_CONNECTED_ACK    */ NULL,
+    /* FSMCNF_E_RELEASE          */ NULL,
+    /* FSMCNF_E_RELEASE_COMPLETE */ NULL,
+    /* FSMCNF_E_FEATURE          */ fsmcnf_ev_idle_feature,
+    /* FSMCNF_E_FEATURE_ACK      */ NULL,
+    /* FSMCNF_E_OFFHOOK          */ NULL,
+    /* FSMCNF_E_ONHOOK           */ NULL,
+    /* FSMCNF_E_LINE             */ NULL,
+    /* FSMCNF_E_DIGIT_BEGIN      */ NULL,
+    /* FSMCNF_E_DIGIT            */ NULL,
+    /* FSMCNF_E_DIALSTRING       */ NULL,
+    /* FSMCNF_E_MWI              */ NULL,
+    /* FSMCNF_E_SESSION_AUDIT    */ NULL
+    },
+
+/* FSMCNF_S_CNFING --------------------------------------------------- */
+    {
+    /* FSMCNF_E_SETUP            */ NULL,
+    /* FSMCNF_E_SETUP_ACK        */ NULL,
+    /* FSMCNF_E_PROCEEDING       */ NULL,
+    /* FSMCNF_E_ALERTING         */ NULL,
+    /* FSMCNF_E_CONNECTED        */ NULL,
+    /* FSMCNF_E_CONNECTED_ACK    */ NULL,
+    /* FSMCNF_E_RELEASE          */ fsmcnf_ev_cnfing_release,
+    /* FSMCNF_E_RELEASE_COMPLETE */ NULL,
+    /* FSMCNF_E_FEATURE          */ fsmcnf_ev_cnfing_feature,
+    /* FSMCNF_E_FEATURE_ACK      */ NULL,
+    /* FSMCNF_E_OFFHOOK          */ NULL,
+    /* FSMCNF_E_ONHOOK           */ fsmcnf_ev_cnfing_onhook,
+    /* FSMCNF_E_LINE             */ NULL,
+    /* FSMCNF_E_DIGIT_BEGIN      */ NULL,
+    /* FSMCNF_E_DIGIT            */ NULL,
+    /* FSMCNF_E_DIALSTRING       */ NULL,
+    /* FSMCNF_E_MWI              */ NULL,
+    /* FSMCNF_E_SESSION_AUDIT    */ NULL
+    },
+
+/* FSMCNF_S_CNFED ---------------------------------------------------- */
+    {
+    /* FSMCNF_E_SETUP            */ NULL,
+    /* FSMCNF_E_SETUP_ACK        */ NULL,
+    /* FSMCNF_E_PROCEEDING       */ NULL,
+    /* FSMCNF_E_ALERTING         */ NULL,
+    /* FSMCNF_E_CONNECTED        */ NULL,
+    /* FSMCNF_E_CONNECTED_ACK    */ NULL,
+    /* FSMCNF_E_RELEASE          */ fsmcnf_ev_cnfed_release,
+    /* FSMCNF_E_RELEASE_COMPLETE */ NULL,
+    /* FSMCNF_E_FEATURE          */ fsmcnf_ev_cnfed_feature,
+    /* FSMCNF_E_FEATURE_ACK      */ fsmcnf_ev_cnfed_feature_ack,
+    /* FSMCNF_E_OFFHOOK          */ NULL,
+    /* FSMCNF_E_ONHOOK           */ fsmcnf_ev_cnfed_onhook,
+    /* FSMCNF_E_LINE             */ NULL,
+    /* FSMCNF_E_DIGIT_BEGIN      */ NULL,
+    /* FSMCNF_E_DIGIT            */ NULL,
+    /* FSMCNF_E_DIALSTRING       */ NULL,
+    /* FSMCNF_E_MWI              */ NULL,
+    /* FSMCNF_E_SESSION_AUDIT    */ NULL
+    }
+};
+
+static sm_table_t fsmcnf_sm_table;
+sm_table_t *pfsmcnf_sm_table = &fsmcnf_sm_table;
+
+
+const char *
+fsmcnf_state_name (int state)
+{
+    if ((state <= FSMCNF_S_MIN) || (state >= FSMCNF_S_MAX)) {
+        return (get_debug_string(GSM_UNDEFINED));
+    }
+
+    return (fsmcnf_state_names[state]);
+}
+
+
+static int
+fsmcnf_get_new_cnf_id (void)
+{
+    static int cnf_id = 0;
+
+    if (++cnf_id < 0) {
+        cnf_id = 1;
+    }
+
+    return (cnf_id);
+}
+
+
+static void
+fsmcnf_init_ccb (fsmcnf_ccb_t *ccb)
+{
+    if (ccb != NULL) {
+        ccb->cnf_id      = FSM_NO_ID;
+        ccb->cnf_call_id = CC_NO_CALL_ID;
+        ccb->cns_call_id = CC_NO_CALL_ID;
+        ccb->cnf_line    = CC_NO_LINE;
+        ccb->cns_line    = CC_NO_LINE;
+        ccb->bridged     = FALSE;
+        ccb->active      = FALSE;
+        ccb->flags       = 0;
+        ccb->cnf_ftr_ack = FALSE;
+    }
+}
+
+
+static fsmcnf_ccb_t *
+fsmcnf_get_ccb_by_cnf_id (int cnf_id)
+{
+    fsmcnf_ccb_t *ccb;
+    fsmcnf_ccb_t *ccb_found = NULL;
+
+    FSM_FOR_ALL_CBS(ccb, fsmcnf_ccbs, FSMCNF_MAX_CCBS) {
+        if (ccb->cnf_id == cnf_id) {
+            ccb_found = ccb;
+
+            break;
+        }
+    }
+
+    return (ccb_found);
+}
+
+
+/*
+ *  Function: fsmcnf_get_new_cnf_context
+ *
+ *  Parameters:
+ *      cnf_call_id: call_id for the call initiating the conference
+ *
+ *  Description: This function creates a new conference context by:
+ *               - getting a free ccb
+ *               - creating new cnf_id and cns_call_id
+ *
+ *  Returns: ccb
+ *
+ */
+static fsmcnf_ccb_t *
+fsmcnf_get_new_cnf_context (callid_t cnf_call_id)
+{
+    static const char fname[] = "fsmcnf_get_new_cnf_context";
+    fsmcnf_ccb_t *ccb;
+
+    ccb = fsmcnf_get_ccb_by_cnf_id(FSM_NO_ID);
+    if (ccb != NULL) {
+        ccb->cnf_id      = fsmcnf_get_new_cnf_id();
+        ccb->cnf_call_id = cnf_call_id;
+        ccb->cns_call_id = cc_get_new_call_id();
+
+        FSM_DEBUG_SM(get_debug_string(FSMCNF_DBG_PTR), ccb->cnf_id,
+                     ccb->cnf_call_id, ccb->cns_call_id, fname, ccb);
+    } else {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Failed to get new ccb.\n", fname);
+    }
+
+    return (ccb);
+}
+
+
+fsmcnf_ccb_t *
+fsmcnf_get_ccb_by_call_id (callid_t call_id)
+{
+    fsmcnf_ccb_t *ccb;
+    fsmcnf_ccb_t *ccb_found = NULL;
+
+    FSM_FOR_ALL_CBS(ccb, fsmcnf_ccbs, FSMCNF_MAX_CCBS) {
+        if ((ccb->cnf_call_id == call_id) || (ccb->cns_call_id == call_id)) {
+            ccb_found = ccb;
+
+            break;
+        }
+    }
+
+    return (ccb_found);
+}
+
+
+static void
+fsmcnf_update_cnf_context (fsmcnf_ccb_t *ccb, callid_t old_call_id,
+                           callid_t new_call_id)
+{
+    static const char fname[] = "fsmcnf_update_cnf_context";
+
+    if (ccb != NULL) {
+        if (old_call_id == ccb->cnf_call_id) {
+            ccb->cnf_call_id = new_call_id;
+        } else if (old_call_id == ccb->cns_call_id) {
+            ccb->cns_call_id = new_call_id;
+        }
+
+        FSM_DEBUG_SM(get_debug_string(FSMCNF_DBG_PTR), ccb->cnf_id,
+                     ccb->cnf_call_id, ccb->cns_call_id, fname, ccb);
+    }
+}
+
+
+callid_t
+fsmcnf_get_other_call_id (fsmcnf_ccb_t *ccb, callid_t call_id)
+{
+    callid_t other_call_id = CC_NO_CALL_ID;
+
+    if (ccb != NULL) {
+        if (ccb->cnf_call_id == call_id) {
+            other_call_id = ccb->cns_call_id;
+        } else if (ccb->cns_call_id == call_id) {
+            other_call_id = ccb->cnf_call_id;
+        }
+    }
+
+    return (other_call_id);
+}
+
+static void
+fsmcnf_cnf_xfer (fsmcnf_ccb_t *ccb)
+{
+    fsmdef_dcb_t     *dcb;
+    cc_feature_data_t ftr_data;
+
+    dcb = fsm_get_dcb(ccb->cnf_call_id);
+
+    /*
+     * Pretending attended transfer
+     */
+    ftr_data.xfer.cause          = CC_CAUSE_XFER_CNF;
+    ftr_data.xfer.target_call_id = ccb->cns_call_id;
+    cc_int_feature(CC_SRC_UI, CC_SRC_GSM, dcb->call_id,
+                   dcb->line, CC_FEATURE_XFER, &(ftr_data));
+}
+
+
+/*
+ *  Function: fsmcnf_remove_fcb
+ *
+ *  Parameters:
+ *      cnf_id:  cnf_id for the conference
+ *      call_id: call_id that identifies the fcb to be removed
+ *
+ *  Description: This function will remove the fcb identified by the given
+ *               call_id from the ccb. And the function will free the ccb
+ *               if both fcbs have been removed.
+ *
+ *  Returns: none
+ *
+ *  Note: This is a helper function for fsmcnf_cleanup. It allows fsmcnf_cleanup
+ *        to cleanup one fcb (one call involved in the conference) independent
+ *        of the other involved fcb.
+ */
+static void
+fsmcnf_remove_fcb (fsm_fcb_t *fcb, callid_t call_id)
+{
+    fsmcnf_ccb_t *ccb = fcb->ccb;
+
+    if (ccb != NULL) {
+        fsmcnf_update_cnf_context(ccb, call_id, CC_NO_CALL_ID);
+
+        /*
+         * Free the ccb if both fcb references have been removed.
+         */
+        if ((ccb->cnf_call_id == CC_NO_CALL_ID) &&
+            (ccb->cns_call_id == CC_NO_CALL_ID)) {
+            fsmcnf_init_ccb(ccb);
+        }
+    }
+}
+
+
+static void
+fsmcnf_cleanup (fsm_fcb_t *fcb, int fname, boolean both)
+{
+    fsmcnf_ccb_t   *ccb;
+    fsm_fcb_t      *other_fcb, *fcb_def;
+    callid_t        call_id       = fcb->call_id;
+    callid_t        other_call_id = CC_NO_CALL_ID;
+
+    /* stop the channel mixing if we are currently doing it */
+    ccb = fsmcnf_get_ccb_by_call_id(call_id);
+    other_call_id = fsmcnf_get_other_call_id(fcb->ccb, call_id);
+    /* Set session to be primary */
+    fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+
+    if (fcb->ccb && (call_id == fcb->ccb->cnf_call_id)) {
+
+        if (other_call_id != CC_NO_CALL_ID) {
+            /*
+             * Not clearing consulation call, so change consultation
+             * call attribute to display connected softkey set.
+             * Do not change softkey set if it is a transfer o
+             */
+            if (ccb == NULL) {
+                GSM_DEBUG_ERROR(GSM_F_PREFIX"Failed to get CCB.\n", fname);
+            } else {
+                cc_call_attribute(other_call_id, ccb->cnf_line, NORMAL_CALL);
+            }
+        }
+
+    }
+
+    if (fcb_def && fcb_def->dcb)  {
+        fcb_def->dcb->session = PRIMARY;
+    }
+    /*
+     * Check if the user wanted to cleanup the whole ccb.
+     * If so, then we will grab the other fcb first and call this function
+     * again with this other fcb. The whole ccb will be freed after this block
+     * of code because both call_ids will be -1, which tells
+     * fsmcnf_remove_fcb to free the ccb.
+     */
+    if (both) {
+        other_call_id = fsmcnf_get_other_call_id(fcb->ccb, call_id);
+        if (other_call_id != CC_NO_CALL_ID) {
+            other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_CNF);
+            if (other_fcb != NULL) {
+                fsmcnf_cleanup(other_fcb, fname, FALSE);
+            }
+        }
+    }
+
+    /*
+     * Remove the reference to this fcb from the ccb.
+     */
+    fsmcnf_remove_fcb(fcb, fcb->call_id);
+
+    /*
+     * Move this fcb to the IDLE state
+     */
+    fsm_change_state(fcb, fname, FSMCNF_S_IDLE);
+
+    /*
+     * Reset the data for this fcb. The fcb is still included in a call
+     * so set the call_id and dcb values accordingly.
+     */
+    fsm_init_fcb(fcb, fcb->call_id, fcb->dcb, FSM_TYPE_CNF);
+}
+
+
+void
+fsmcnf_free_cb (fim_icb_t *icb, callid_t call_id)
+{
+    fsm_fcb_t *fcb = NULL;
+
+    if (call_id != CC_NO_CALL_ID) {
+        fcb = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_CNF);
+
+        if (fcb != NULL) {
+            fsmcnf_cleanup(fcb, __LINE__, FALSE);
+            fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE);
+        }
+    }
+}
+
+
+/**
+ *
+ * Cancel conference feature by sending cancel event to SIP stack.
+ * This routine is used in roundtable phone.
+ *
+ * Copied and pasted from fsmb2bcnf_feature_cancel().
+ * See also fsmxfr_feature_cancel().
+ *
+ * @param line, call_id, target_call_id, cause (implicit or explicit)
+ *
+ * @return  void
+ *
+ * @pre     (none)
+ */
+void
+fsmcnf_feature_cancel (fsmcnf_ccb_t *ccb, line_t line, callid_t call_id,
+                          callid_t target_call_id)
+{
+    cc_feature_data_t data;
+    fsm_fcb_t         *fcb_def;
+
+    fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+
+    // 'cause' will always be CC_SK_EVT_TYPE_EXPLI for now since it's only
+    // called by fsmcnf_ev_cnfing_feature in the case of CC_FEAURE_CANCEL.
+    // Thus 'cause' is ignored (for now) until the whole SM is refactored.
+    if (/*(cause == CC_SK_EVT_TYPE_EXPLI) && */
+        (fcb_def != NULL) && ((fcb_def->dcb->selected == FALSE) &&
+            ((fcb_def->state == FSMDEF_S_OUTGOING_ALERTING) ||
+            ((fcb_def->state == FSMDEF_S_CONNECTED) &&
+            (fcb_def->dcb->spoof_ringout_requested == TRUE) &&
+            (fcb_def->dcb->spoof_ringout_applied == TRUE))))) {
+
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, call_id,
+                           line, CC_FEATURE_END_CALL, NULL);
+    }
+
+    fcb_def = fsm_get_fcb_by_call_id_and_type(target_call_id, FSM_TYPE_DEF);
+
+    if (/* (cause == CC_SK_EVT_TYPE_EXPLI) && */
+        (fcb_def != NULL) && ((fcb_def->dcb->selected == FALSE) &&
+            ((fcb_def->state == FSMDEF_S_OUTGOING_ALERTING) ||
+            ((fcb_def->state == FSMDEF_S_CONNECTED) &&
+            (fcb_def->dcb->spoof_ringout_requested == TRUE) &&
+            (fcb_def->dcb->spoof_ringout_applied == TRUE))))) {
+
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, target_call_id,
+                           line, CC_FEATURE_END_CALL, NULL);
+    }
+
+    data.cancel.target_call_id = target_call_id;
+    data.cancel.call_id = call_id;
+    data.cancel.cause = CC_SK_EVT_TYPE_EXPLI;
+
+    cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, call_id,
+                           line, CC_FEATURE_CANCEL, &data);
+}
+
+/*******************************************************************
+ * event functions
+ */
+
+
+static sm_rcs_t
+fsmcnf_ev_idle_setup (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb     = (fsm_fcb_t *) event->data;
+    cc_setup_t   *msg     = (cc_setup_t *) event->msg;
+    callid_t     call_id  = msg->call_id;
+    fsmcnf_ccb_t *ccb;
+
+    if (!msg->replaces) {
+        return (SM_RC_DEF_CONT);
+    }
+
+    /*
+     * Check to see if this new setup call is a new call that replaces
+     * one of the conferenced call i.e. the this new call replacing the
+     * existing leg of a conferenced by XFER feature from SIP. The
+     * call id of this setup should match call id of a conference.
+     */
+    ccb = fsmcnf_get_ccb_by_call_id(call_id);
+    if (ccb == NULL) {
+        return (SM_RC_DEF_CONT);
+    }
+
+    /* This new call is part of a conference */
+    fcb->ccb = ccb;           /* attach ccb to the new call chain */
+    fsm_change_state(fcb, __LINE__, FSMCNF_S_CNFING);
+
+    return (SM_RC_CONT);
+}
+
+
+static sm_rcs_t
+fsmcnf_ev_idle_feature (sm_event_t *event)
+{
+    static const char *fname  = "fsmcnf_ev_idle_feature";
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_feature_t     *msg     = (cc_feature_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    line_t            line    = msg->line;
+    cc_srcs_t         src_id  = msg->src_id;
+    cc_features_t     ftr_id  = msg->feature_id;
+    fsmdef_dcb_t     *dcb     = fcb->dcb;
+    callid_t          cns_call_id;
+    sm_rcs_t          sm_rc   = SM_RC_CONT;
+    fsmcnf_ccb_t     *ccb;
+    int               free_lines;
+    cc_feature_data_t data;
+    cc_action_data_t  action_data;
+    fsmdef_dcb_t     *other_dcb;
+    fsm_fcb_t        *other_fcb;
+    fsm_fcb_t        *fcb_def, *join_fcb_cnf;
+    cc_feature_data_t ftr_data = msg->data;
+    cc_feature_data_t *feat_data = &(msg->data);
+    fsm_fcb_t        *cns_fcb;
+    callid_t          other_call_id;
+    fsmxfr_xcb_t     *xcb;
+    cc_causes_t       cause;
+
+    memset(&data, 0, sizeof(cc_feature_data_t));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_UI:
+        switch (ftr_id) {
+        case CC_FEATURE_CONF:
+
+            /* Connect the existing call to active conference state
+             * machine. If the UI generates the event with target
+             * call_id in the data then terminate the existing consulatative
+             * call and link that to another call.
+            */
+            if (feat_data && msg->data_valid &&
+                (feat_data->cnf.target_call_id != CC_NO_CALL_ID)
+                && (cns_fcb = fsm_get_fcb_by_call_id_and_type(feat_data->cnf.target_call_id,
+                            FSM_TYPE_CNF)) != NULL) {
+                /*
+                 * Get a new ccb and new b2bcnf id - This is the handle that will
+                 * identify the b2bcnf.
+                 */
+                ccb = fsmcnf_get_new_cnf_context(feat_data->cnf.target_call_id);
+
+                if (ccb == NULL || ccb->cnf_id == FSM_NO_ID) {
+                    return(SM_RC_END);
+                }
+
+                ccb->cns_call_id = call_id;
+                fcb->ccb = ccb;
+                cns_fcb->ccb = ccb;
+                ccb->cnf_line = line;
+                ccb->cns_line = line;
+
+                fsm_change_state(fcb, __LINE__, FSMCNF_S_CNFING);
+
+                fsm_change_state(cns_fcb, __LINE__, FSMCNF_S_CNFING);
+
+                cc_int_feature(CC_SRC_UI, CC_SRC_GSM, ccb->cns_call_id,
+                                   cns_fcb->dcb->line, CC_FEATURE_CONF, NULL);
+                return(SM_RC_END);
+
+            }
+
+            /*
+             * This call is the conference and we are initiating a local
+             * conference. So:
+             * 1. Make sure we have a free line to open a new call plane to
+             *    collect digits (and place call) for the consultation call,
+             * 2. Create a new conference context,
+             * 3. Place this call on hold,
+             * 4. Send a newcall feature back to the GSM so that the
+             *    consultation call can be initiated.
+             */
+
+            /*
+             * Check for any other active features which may block
+             * the conference.
+             */
+
+            /*
+             * The call must be in the connected state to initiate a conference.
+             */
+            fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+            if ((fcb_def != NULL) && (fcb_def->state != FSMDEF_S_CONNECTED)) {
+                break;
+            }
+
+            /*
+             * Make sure we have a free line to start the consultation call.
+             */
+            //CSCsz38962 don't use expline for local conf call
+            //free_lines = lsm_get_instances_available_cnt(line, TRUE);
+            free_lines = lsm_get_instances_available_cnt(line, FALSE);
+            if (free_lines <= 0) {
+                /*
+                 * No free lines - let the user know and end this request.
+                 */
+                fsm_display_no_free_lines();
+
+                break;
+            }
+
+            /*
+             * Get a new ccb and new cnf id - This is the handle that will
+             * identify the cnf.
+             */
+            ccb = fsmcnf_get_new_cnf_context(call_id);
+            if (ccb == NULL || ccb->cnf_id == 0) {
+                break;
+            }
+            fcb->ccb = ccb;
+            ccb->cnf_line = line;
+            ccb->cns_line = line;
+
+            /*
+             * This call needs to go on hold so we can start the consultation
+             * call. Indicate feature indication should be send by setting
+             * call info type to hold and feature reason to conference.
+             */
+            data.hold.call_info.type = CC_FEAT_HOLD;
+            data.hold.call_info.data.hold_resume_reason = CC_REASON_CONF;
+            data.hold.msg_body.num_parts = 0;
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
+                           CC_FEATURE_HOLD, &data);
+
+            /*
+             * Initiate the consultation call.
+             */
+            data.newcall.cause = CC_CAUSE_CONF;
+            cns_call_id = ccb->cns_call_id;
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_call_id, line,
+                           CC_FEATURE_NEW_CALL, &data);
+
+            FSM_DEBUG_SM(get_debug_string(FSMCNF_DBG_CNF_INITIATED),
+                         ccb->cnf_id, call_id, cns_call_id, __LINE__);
+
+            fsm_change_state(fcb, __LINE__, FSMCNF_S_CNFING);
+
+            sm_rc = SM_RC_END;
+
+            break;
+
+        case CC_FEATURE_JOIN:
+            /*
+             * The call must be in the connected state to initiate a conference
+             */
+            fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+            if ((fcb_def != NULL) && (fcb_def->state != FSMDEF_S_CONNECTED)) {
+                break;
+            }
+
+            /*
+             * Make sure that we have another active call on this line.
+             */
+            other_dcb = fsmdef_get_other_dcb_by_line(call_id, dcb->line);
+            if (other_dcb == NULL) {
+                break;
+            }
+
+            /* get other calls FCB */
+            other_fcb = fsm_get_fcb_by_call_id_and_type(other_dcb->call_id,
+                                                        FSM_TYPE_DEF);
+            if (other_fcb == NULL) {
+                break;
+            }
+
+            if (other_fcb->state == FSMDEF_S_HOLDING) {
+                /*
+                 * Get a new ccb and new cnf id - This is the handle that will
+                 * identify the cnf.
+                 */
+                ccb = fsmcnf_get_new_cnf_context(call_id);
+                if (ccb == NULL) {
+                    break;
+                }
+                fcb->ccb = ccb;
+                fsm_change_state(fcb, __LINE__, FSMCNF_S_CNFED);
+
+
+                other_fcb = fsm_get_fcb_by_call_id_and_type(other_dcb->call_id,
+                                                            FSM_TYPE_CNF);
+                if (other_fcb == NULL) {
+                    fsmcnf_cleanup(fcb, __LINE__, TRUE);
+                    break;
+                }
+                other_fcb->ccb = ccb;
+                fsm_change_state(other_fcb, __LINE__, FSMCNF_S_CNFED);
+
+                ccb->cnf_call_id = dcb->call_id;
+                ccb->cns_call_id = other_dcb->call_id;
+                ccb->bridged = TRUE;
+
+                /* Build SDP for sending out */
+                cause = gsmsdp_encode_sdp_and_update_version(other_dcb,
+                                                             &data.resume.msg_body);
+                if (cause != CC_CAUSE_OK) {
+                    FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+                    sm_rc = SM_RC_END;
+                    break;
+                }
+                data.resume.cause = CC_CAUSE_CONF;
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_dcb->call_id,
+                               other_dcb->line, CC_FEATURE_RESUME, &data);
+
+                /*
+                 * Update the UI for this call.
+                 */
+                action_data.update_ui.action = CC_UPDATE_CONF_ACTIVE;
+                (void)cc_call_action(other_dcb->call_id, other_dcb->line,
+                               CC_ACTION_UPDATE_UI, &action_data);
+            } else {
+                fsm_display_feature_unavailable();
+            }
+
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            sm_rc = SM_RC_DEF_CONT;
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_NEW_CALL:
+
+            /*
+             * If this is the consultation call involved in a conference,
+             * then set the rest of the data required to make the conference
+             * happen. The data is the cnf_id in the fcb. The data is set now
+             * because we did not have the fcb when the conference was
+             * initiated. The fcb is created when a new_call event is
+             * received by the FIM, not when a conference event is received.
+             *
+             * Or this could be the call that originated the conference and
+             * the person he was talking to (the conference target) has
+             * decided to conference the trasnferor to another party.
+             */
+
+            /*
+             * Ignore this event if this call is not involved in a conference.
+             */
+            ccb = fsmcnf_get_ccb_by_call_id(call_id);
+            if (ccb == NULL) {
+                break;
+            }
+            fcb->ccb = ccb;
+
+            /*
+             * Determine what state this cnf should be in (cnfing or cnfed).
+             * If the cnfrn key has only been hit once, then this call will
+             * be in the cnfing state. If it has been hit the second time,
+             * then this call should go to the cnfed state. The latter
+             * case only happens when the calls are conferenced and one
+             * of the remote ends has decided to transfer one leg of the cnf.
+             * And it just so happens that the other call involved in the cnf
+             * should match this call, so we can just use it's state to
+             * assign the state to this call.
+             */
+            other_call_id = fsmcnf_get_other_call_id(ccb, call_id);
+            other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_CNF);
+
+            if(other_fcb == NULL) {
+                GSM_DEBUG_ERROR(GSM_F_PREFIX"Failed to get FCB.\n", fname);
+            } else {
+                fsm_change_state(fcb, __LINE__, other_fcb->state);
+            }
+
+            break;
+
+        case CC_FEATURE_JOIN:
+            if (fsm_is_joining_call(ftr_data)) {
+                /*
+                 * The join target call must be in the
+                 * connected state to initiate a conference.
+                 */
+                fcb_def = fsm_get_fcb_by_call_id_and_type(call_id,
+                                                          FSM_TYPE_DEF);
+                if ((fcb_def == NULL) ||
+                    ((fcb_def->state != FSMDEF_S_CONNECTED) &&
+                     (fcb_def->state != FSMDEF_S_RESUME_PENDING))) {
+                    FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Join target \
+                            call is not at connected state\n",
+                            DEB_L_C_F_PREFIX_ARGS(FSM, line, call_id, fname));
+                    break;
+                }
+
+                /* Get the joining call fcb */
+                join_fcb_cnf = fsm_get_fcb_by_call_id_and_type(
+                                    ftr_data.newcall.join.join_call_id,
+                                    FSM_TYPE_CNF);
+
+                /* Create a conference context for the join target call */
+                ccb = fsmcnf_get_new_cnf_context(call_id);
+                if (ccb == NULL || join_fcb_cnf == NULL) {
+                    FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Could not \
+                            find the conference context\n",
+                            DEB_L_C_F_PREFIX_ARGS(FSM, line, call_id, fname));
+                    break;
+                }
+
+                fcb->ccb = ccb;
+                join_fcb_cnf->ccb = ccb;
+                ccb->cnf_call_id = fcb_def->dcb->call_id;
+                /* Joining call is the consultative call of the conference */
+                ccb->cns_call_id = join_fcb_cnf->dcb->call_id;
+
+                join_fcb_cnf->dcb->group_id = fcb_def->dcb->group_id;
+                fsm_change_state(fcb, __LINE__, FSMCNF_S_CNFED);
+                fsm_change_state(join_fcb_cnf, __LINE__, FSMCNF_S_CNFED);
+
+                softkey_mask_list[0] = skConfrn;
+                ui_select_feature_key_set(line, call_id,
+                                          FSMCNF_CONNECTED_SET,
+                                          softkey_mask_list, 1);
+
+                ccb->flags |= JOINED;
+
+                ccb->bridged = FALSE;
+            }
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            sm_rc = SM_RC_DEF_CONT;
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_NOTIFY:
+            if ((msg->data_valid == TRUE) &&
+                (msg->data.notify.subscription == CC_SUBSCRIPTIONS_XFER) &&
+                (msg->data.notify.method == CC_XFER_METHOD_REFER)) {
+                xcb = fsmxfr_get_xcb_by_call_id(call_id);
+                if ((xcb != NULL) && (call_id == xcb->cns_call_id)) {
+                    /*
+                     * One leg of the conference has decided to transfer us.
+                     * We need to
+                     * 1. remove the transferred call from the conference
+                     *    context and associate the new call
+                     *    with the context,
+                     * 2. cleanup this transferred fcb.
+                     */
+                    ccb = fsmcnf_get_ccb_by_call_id(xcb->xfr_call_id);
+                    if (ccb == NULL) {
+                        break;
+                    }
+
+                    other_fcb =
+                        fsm_get_fcb_by_call_id_and_type(xcb->xfr_call_id,
+                                                        FSM_TYPE_CNF);
+                    if (other_fcb == NULL) {
+                        break;
+                    }
+
+                    fcb->ccb = ccb;
+
+                    fsmcnf_update_cnf_context(ccb, xcb->xfr_call_id,
+                                              xcb->cns_call_id);
+
+                    fsmcnf_cleanup(other_fcb, __LINE__, FALSE);
+
+                    other_call_id = fsmcnf_get_other_call_id(ccb, call_id);
+                    other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                                FSM_TYPE_CNF);
+
+                    fsm_change_state(fcb, __LINE__, other_fcb->state);
+
+                    if (other_fcb->state == FSMCNF_S_CNFED) {
+                        ccb->bridged = TRUE;
+
+                        /*
+                         * Resume the other leg of the conference that was
+                         * not transferred.
+                         */
+                        other_dcb = fsm_get_dcb(other_call_id);
+
+                        /* Build SDP for sending out */
+                        cause = gsmsdp_encode_sdp_and_update_version(other_dcb,
+                                                                     &data.resume.msg_body);
+                        if (cause != CC_CAUSE_OK) {
+                            GSM_DEBUG_ERROR(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+                            fsmcnf_cleanup(fcb, __LINE__, TRUE);
+                            sm_rc = SM_RC_END;
+                            break;
+                        }
+                        data.resume.cause = CC_CAUSE_CONF;
+                        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM,
+                                       other_dcb->call_id, other_dcb->line,
+                                       CC_FEATURE_RESUME, &data);
+                    }
+
+                    /*
+                     * Update the UI for the just transferred leg.
+                     */
+                    action_data.update_ui.action = CC_UPDATE_CONF_ACTIVE;
+                    (void)cc_call_action(fcb->call_id, dcb->line,
+                                   CC_ACTION_UPDATE_UI, &action_data);
+                }
+            }
+
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            sm_rc = SM_RC_DEF_CONT;
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+        sm_rc = SM_RC_DEF_CONT;
+
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+
+static void
+fsmcnf_update_release (sm_event_t *event)
+{
+    fsm_fcb_t       *fcb = (fsm_fcb_t *) event->data;
+    callid_t         other_call_id;
+    cc_action_data_t action_data;
+    fsm_fcb_t       *other_fcb;
+
+    /*
+     * Update the UI for the other call.
+     */
+    other_call_id = fsmcnf_get_other_call_id(fcb->ccb, fcb->call_id);
+    if (other_call_id != CC_NO_CALL_ID) {
+        action_data.update_ui.action = CC_UPDATE_CONF_RELEASE;
+        (void)cc_call_action(other_call_id, fcb->dcb->line, CC_ACTION_UPDATE_UI,
+                       &action_data);
+
+        /* Far end released its original call. Set the attribute of
+         * other call so it can display connected softkey set.
+         *
+         * Check only for the consultation leg since only that leg
+         * will need its attribute reset - the other leg does not
+         * have an atribute set.
+         */
+        if (fcb->ccb && (fcb->call_id == fcb->ccb->cnf_call_id)) {
+            other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_CNF);
+            if (other_fcb != NULL) {
+                fsm_fcb_t       *b2bcnf_fcb, *xfr_fcb;
+                b2bcnf_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_B2BCNF);
+                xfr_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_XFR);
+                if ((b2bcnf_fcb != NULL && b2bcnf_fcb->b2bccb == NULL)  &&
+                        (xfr_fcb != NULL && xfr_fcb->xcb == NULL)) {
+                    cc_call_attribute(other_call_id, other_fcb->dcb->line, NORMAL_CALL);
+                }
+            }
+        }
+    }
+
+    fsmcnf_cleanup(fcb, __LINE__, TRUE);
+}
+
+
+static sm_rcs_t
+fsmcnf_ev_cnfing_release (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb     = (fsm_fcb_t *) event->data;
+    cc_release_t *msg     = (cc_release_t *) event->msg;
+    callid_t      call_id = msg->call_id;
+    fsmcnf_ccb_t *ccb     = fcb->ccb;
+    fsmxfr_xcb_t *xcb;
+    fsm_fcb_t    *other_fcb;
+
+    xcb = fsmxfr_get_xcb_by_call_id(call_id);
+    if (xcb != NULL) {
+        /*
+         * One leg of the conference has decided to transfer us.
+         * We need to
+         * 1. remove this call from the conference context and
+         *    associate the new (transferred) call with the context,
+         * 2. cleanup this fcb.
+         */
+        fsmcnf_update_cnf_context(ccb, call_id, xcb->cns_call_id);
+
+        fsmcnf_cleanup(fcb, __LINE__, FALSE);
+
+        other_fcb = fsm_get_fcb_by_call_id_and_type(xcb->cns_call_id,
+                                                    FSM_TYPE_CNF);
+
+        if (other_fcb != NULL) {
+            other_fcb->ccb = ccb;
+            fsm_change_state(other_fcb, __LINE__, FSMCNF_S_CNFING);
+        }
+
+        return (SM_RC_CONT);
+    }
+
+    fsmcnf_update_release(event);
+
+    return (SM_RC_CONT);
+}
+
+
+static sm_rcs_t
+fsmcnf_ev_cnfing_feature (sm_event_t *event)
+{
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_feature_t     *msg     = (cc_feature_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    fsmdef_dcb_t     *dcb     = fcb->dcb;
+    fsmcnf_ccb_t     *ccb     = fcb->ccb;
+    cc_srcs_t         src_id  = msg->src_id;
+    cc_features_t     ftr_id  = msg->feature_id;
+    static const char fname[] = "fsmcnf_ev_cnfing_feature";
+    cc_feature_data_t *feat_data   = &(msg->data);
+    sm_rcs_t          sm_rc   = SM_RC_CONT;
+    callid_t          other_call_id;
+    fsmdef_dcb_t     *other_dcb;
+    fsm_fcb_t        *other_fcb;
+    cc_action_data_t  action_data;
+    cc_feature_data_t data;
+    cc_causes_t       cause;
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_UI:
+        switch (ftr_id) {
+        case CC_FEATURE_CANCEL:
+            sm_rc = SM_RC_END;
+            fsmcnf_feature_cancel(ccb, ccb->cnf_line, ccb->cnf_call_id,
+                                  ccb->cns_call_id /*,
+                                  CC_SK_EVT_TYPE_EXPLI */);
+            fsmcnf_cleanup(fcb, __LINE__, TRUE);
+            break;
+
+        case CC_FEATURE_ANSWER:
+            other_call_id = fsmcnf_get_other_call_id(ccb, call_id);
+            if (other_call_id == CC_NO_CALL_ID) {
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                return (sm_rc);
+            }
+            other_dcb = fsm_get_dcb(other_call_id);
+            if (other_dcb == NULL) {
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                return (sm_rc);
+            }
+            /*
+             * The answer to the a setup with replaced we have received.
+             * The setup with replaced is automatically answered
+             * by xfer state machine as UI source (simulate user pressing
+             * the answer key).
+             *
+             * Set the group ID to the new call so that the voice can be
+             * mixed with the new call.
+             */
+            dcb->group_id = other_dcb->group_id;
+            fsm_change_state(fcb, __LINE__, FSMCNF_S_CNFED);
+            return (sm_rc);
+
+        default:
+            break;
+        }
+        /* Fall through */
+
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_CONF:
+            /*
+             * This is the second conference event for a local
+             * attended conference with consultation.
+             *
+             * The user is attempting to complete the conference, so
+             * resume the other leg of the conference call.
+             */
+            ccb->bridged = TRUE;
+            ccb->active  = TRUE;
+            ccb->flags |= LCL_CNF;
+
+            other_call_id = fsmcnf_get_other_call_id(ccb, call_id);
+            other_dcb = fsm_get_dcb(other_call_id);
+
+            /*
+             * Since we are resuming the other leg, allocate the src_sdp
+             * because src_sdp was freed and set to null when this other-leg
+             * was put on hold.
+             */
+            gsmsdp_update_local_sdp_media_capability(other_dcb, TRUE, FALSE);
+
+            /* Build SDP for sending out */
+            cause = gsmsdp_encode_sdp_and_update_version(other_dcb,
+                                                         &data.resume.msg_body);
+            if (cause != CC_CAUSE_OK) {
+                GSM_DEBUG_ERROR(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+                sm_rc = SM_RC_END;
+                break;
+            }
+            data.resume.cause = CC_CAUSE_CONF;
+
+            other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_DEF);
+            if (other_fcb && other_fcb->state == FSMDEF_S_HOLDING) {
+
+                other_dcb->session = LOCAL_CONF;
+
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_dcb->call_id,
+                           other_dcb->line, CC_FEATURE_RESUME, &data);
+
+            } else {
+                /* Other call must be on hold */
+
+                dcb->session = LOCAL_CONF;
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, call_id,
+                           ccb->cnf_line, CC_FEATURE_RESUME, &data);
+
+
+            }
+
+            /*
+             * Update the UI for this call.
+             */
+            action_data.update_ui.action = CC_UPDATE_CONF_ACTIVE;
+            (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_UPDATE_UI,
+                           &action_data);
+
+            other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_CNF);
+            fsm_change_state(other_fcb, __LINE__, FSMCNF_S_CNFED);
+            fsm_change_state(fcb, __LINE__, FSMCNF_S_CNFED);
+
+            sm_rc = SM_RC_END;
+
+            break;
+
+        case CC_FEATURE_END_CALL:
+            fsmcnf_update_release(event);
+
+            break;
+
+        case CC_FEATURE_HOLD:
+            if ((msg->data_valid) &&
+                (feat_data->hold.call_info.data.hold_resume_reason != CC_REASON_SWAP &&
+                feat_data->hold.call_info.data.hold_resume_reason != CC_REASON_CONF &&
+                feat_data->hold.call_info.data.hold_resume_reason != CC_REASON_INTERNAL)) {
+                sm_rc = SM_RC_END;
+                DEF_DEBUG(DEB_F_PREFIX"Invoke hold call_id = %d t_call_id=%d\n",
+                            DEB_F_PREFIX_ARGS(GSM, fname), ccb->cnf_call_id, ccb->cns_call_id);
+                //Actual hold to this call, so break the feature layer.
+                ui_terminate_feature(dcb->line, ccb->cnf_call_id, ccb->cns_call_id);
+
+                fsmcnf_feature_cancel(ccb, ccb->cnf_line, ccb->cnf_call_id,
+                                      ccb->cns_call_id /*,
+                                      CC_SK_EVT_TYPE_EXPLI */);
+                fsmcnf_cleanup(fcb, __LINE__, TRUE);
+            }
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+
+static sm_rcs_t
+fsmcnf_ev_cnfing_onhook (sm_event_t *event)
+{
+    fsmcnf_update_release(event);
+
+    return (SM_RC_CONT);
+}
+
+
+static sm_rcs_t
+fsmcnf_ev_cnfed_release (sm_event_t *event)
+{
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_release_t     *msg     = (cc_release_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    fsmdef_dcb_t     *dcb     = fcb->dcb;
+    fsmcnf_ccb_t     *ccb     = fcb->ccb;
+    callid_t          other_call_id;
+    fsmdef_dcb_t     *other_dcb;
+    cc_feature_data_t data;
+    cc_action_data_t  action_data;
+    fsmxfr_xcb_t     *xcb;
+    fsm_fcb_t        *other_fcb;
+    cc_causes_t       cause;
+
+    /* Conference is not active any more, clear that flag
+    */
+    ccb->active  = FALSE;
+
+    if( ccb->flags & JOINED ){
+        other_call_id = fsmcnf_get_other_call_id(ccb, call_id);
+        if(other_call_id != CC_NO_CALL_ID ){
+            fsm_fcb_t       *b2bcnf_fcb, *xfr_fcb;
+            b2bcnf_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_B2BCNF);
+            xfr_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_XFR);
+            if ((b2bcnf_fcb != NULL && b2bcnf_fcb->b2bccb == NULL)  &&
+                        (xfr_fcb != NULL && xfr_fcb->xcb == NULL)) {
+                cc_call_attribute(other_call_id, dcb->line, NORMAL_CALL);
+            }
+        }
+    }
+    xcb = fsmxfr_get_xcb_by_call_id(call_id);
+    if ((xcb != NULL) && (!(ccb->flags & JOINED))) {
+        /*
+         * One leg of the conference has decided to transfer us.
+         * We need to:
+         * 1. remove this call from the conference context and
+         *    associate the new (transferred) call with the context,
+         * 2. Resume the other leg of the conference that was not transferred.
+         *    More than likely this leg was put on hold when the transfer
+         *    was intitated if this was a REFER transfer.
+         * 3.
+         */
+        fsmcnf_update_cnf_context(ccb, call_id, xcb->cns_call_id);
+
+        fsmcnf_cleanup(fcb, __LINE__, FALSE);
+
+        /* NOTE:
+         * Normally, we would reset the ccb->bridged flag to indicate
+         * that the bridge is not active. We do not want to do that in
+         * this case because we want the conference to bridge as soon as
+         * the target we are transferred to answers.
+         */
+
+        ccb->bridged = TRUE;
+
+        /*
+         * Resume the other leg of the conference that was not transferred.
+         */
+        other_call_id = fsmcnf_get_other_call_id(ccb, xcb->cns_call_id);
+        other_dcb = fsm_get_dcb(other_call_id);
+
+        /* Build SDP for sending out */
+        cause = gsmsdp_encode_sdp_and_update_version(other_dcb,
+                                                     &data.resume.msg_body);
+        if (cause != CC_CAUSE_OK) {
+            GSM_DEBUG_ERROR(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+            return (SM_RC_END);
+        }
+        data.resume.cause = CC_CAUSE_CONF;
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_dcb->call_id,
+                       other_dcb->line, CC_FEATURE_RESUME, &data);
+
+        /*
+         * Update the UI for the just transferred leg.
+         */
+        other_fcb = fsm_get_fcb_by_call_id_and_type(xcb->cns_call_id,
+                                                    FSM_TYPE_CNF);
+
+        if (other_fcb != NULL) {
+            other_fcb->ccb = ccb;
+
+            fsm_change_state(other_fcb, __LINE__, FSMCNF_S_CNFED);
+
+            action_data.update_ui.action = CC_UPDATE_CONF_ACTIVE;
+            cc_call_action(other_fcb->call_id, dcb->line, CC_ACTION_UPDATE_UI,
+                           &action_data);
+            return (SM_RC_CONT);
+        }
+    }
+
+    fsmcnf_update_release(event);
+
+    return (SM_RC_CONT);
+}
+
+
+static void
+fsmcnf_other_feature (fsm_fcb_t *fcb, cc_features_t ftr_id)
+{
+    callid_t other_call_id;
+
+    other_call_id = fsmcnf_get_other_call_id(fcb->ccb, fcb->call_id);
+    if (other_call_id != CC_NO_CALL_ID) {
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_call_id,
+                       fcb->dcb->line, ftr_id, NULL);
+    }
+}
+
+
+static sm_rcs_t
+fsmcnf_ev_cnfed_feature (sm_event_t *event)
+{
+    fsm_fcb_t      *fcb     = (fsm_fcb_t *) event->data;
+    cc_feature_t   *msg     = (cc_feature_t *) event->msg;
+    callid_t        call_id = msg->call_id;
+    cc_srcs_t       src_id  = msg->src_id;
+    cc_features_t   ftr_id  = msg->feature_id;
+    sm_rcs_t        sm_rc   = SM_RC_CONT;
+    fsmcnf_ccb_t   *ccb     = fcb->ccb;
+    fsmxfr_xcb_t   *xcb;
+    int             join = 1;
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_UI:
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_CANCEL:
+            sm_rc = SM_RC_END;
+            fsmcnf_cleanup(fcb, __LINE__, TRUE);
+            break;
+
+        case CC_FEATURE_END_CALL:
+            if ((ccb->flags & JOINED) && (ccb->bridged == FALSE)) {
+                /* Something went wrong during the barge/monitor call setup, clean up */
+                fsmcnf_cleanup(fcb, __LINE__, TRUE);
+                return (sm_rc);
+            }
+            /*
+             * If bridge of conference call ends the call, other 2
+             * users should still be able to talk to each other.
+             * So we simulate attended transfer when bridge tries
+             * to end the call.
+             */
+            config_get_value(CFGID_CNF_JOIN_ENABLE, &join, sizeof(join));
+            if (((ccb->bridged == TRUE) && (join) && !(ccb->flags & JOINED)) ||
+                ((ccb->bridged == TRUE) && (ccb->flags & XFER))) {
+
+                fsmcnf_cnf_xfer(ccb);
+                sm_rc = SM_RC_END;
+            } else {
+                /*
+                 * The user is attempting to clear the call, therefore
+                 * we need to also clear the other leg of this
+                 * conference if it is active.
+                 */
+                fsmcnf_other_feature(fcb, ftr_id);
+            }
+
+            fsmcnf_cleanup(fcb, __LINE__, TRUE);
+            break;
+
+        case CC_FEATURE_HOLD:
+            /* Don't process the Hold if we are still waiting
+             * on an ack from a previous Resume.
+             */
+            if ((ccb->cnf_ftr_ack) && (src_id == CC_SRC_UI)) {
+                return (SM_RC_END);
+            }
+
+            /*
+             * The user is attempting to hold the call, therefore we need
+             * to also hold the other leg of this conference. Only update
+             * the other leg if the conference is bridged and not involved
+             * in a transfer.
+             */
+            if (ccb->bridged == TRUE) {
+
+                if ((ccb->flags & XFER) && (src_id == CC_SRC_GSM)) {
+                    fsmcnf_cleanup(fcb, __LINE__, TRUE);
+                    return (sm_rc);
+                }
+                xcb = fsmxfr_get_xcb_by_call_id(call_id);
+                if ((xcb == NULL) || (!(ccb->flags & XFER))) {
+                    fsmcnf_other_feature(fcb, ftr_id);
+                    ccb->cnf_ftr_ack = TRUE;
+                    ccb->bridged = FALSE;
+                }
+            }
+            break;
+
+        case CC_FEATURE_RESUME:
+            /* Don't process the Resume if we are still waiting
+             * on an ack from a previous Hold.
+             */
+            if ((ccb->cnf_ftr_ack) && (src_id == CC_SRC_UI)) {
+                return (SM_RC_END);
+            }
+
+            /*
+             * The user is attempting to resume the call, therefore we need
+             * to also resume the other leg of this conference. Only do this
+             * if the conference is not bridged.
+             */
+            if (ccb->bridged == FALSE) {
+                fsmcnf_other_feature(fcb, ftr_id);
+                ccb->cnf_ftr_ack = TRUE;
+                ccb->bridged = TRUE;
+            }
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+        break;
+
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_CALLINFO:
+            if ((ccb->flags & JOINED) &&
+                (call_id == ccb->cns_call_id)) {
+                /*
+                 * Call is already joined into, eat this call
+                 * info event that came for the virtual bubble
+                 */
+                return (SM_RC_END);
+            }
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+
+        case CC_FEATURE_XFER:
+            if (msg->data_valid == FALSE) {
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                break;
+            }
+
+            switch (msg->data.xfer.method) {
+            case CC_XFER_METHOD_BYE:
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                break;
+
+            case CC_XFER_METHOD_REFER:
+                if ((msg->data.xfer.cause == CC_CAUSE_XFER_REMOTE) &&
+                    (msg->data.xfer.target_call_id != CC_NO_CALL_ID)) {
+                    /*
+                     * This operation is replacing this call leg with the
+                     * new leg. This call is a target of a transfer.
+                     * Replace the call id of this call with the new
+                     * replacing call id.
+                     */
+                    fsmcnf_update_cnf_context(ccb, call_id,
+                                              msg->data.xfer.target_call_id);
+                    /* Drop this call from conferenced */
+                    fsmcnf_cleanup(fcb, __LINE__, FALSE);
+                } else {
+                    fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                }
+                break;
+
+            default:
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                break;
+            }
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+
+static sm_rcs_t
+fsmcnf_ev_cnfed_feature_ack (sm_event_t *event)
+{
+    fsm_fcb_t        *fcb    = (fsm_fcb_t *) event->data;
+    cc_feature_ack_t *msg    = (cc_feature_ack_t *) event->msg;
+    fsmdef_dcb_t     *dcb    = fcb->dcb;
+    fsmcnf_ccb_t     *ccb    = fcb->ccb;
+    cc_srcs_t         src_id = msg->src_id;
+    cc_features_t     ftr_id = msg->feature_id;
+    sm_rcs_t          sm_rc  = SM_RC_CONT;
+    cc_feature_data_t ftr_data;
+    cc_causes_t       cause;
+    char              tmp_str[STATUS_LINE_MAX_LEN];
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_HOLD:
+            /*
+             * HOLD request is pending. Let this event drop through
+             * to the default sm for handling.
+             */
+            if (msg->cause == CC_CAUSE_REQUEST_PENDING) {
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            } else {
+                ccb->cnf_ftr_ack = FALSE;
+            }
+            break;
+
+        case CC_FEATURE_RESUME:
+            /*
+             * Resume request is pending. Let this event drop through
+             * to the default sm for handling.
+             */
+            if (msg->cause == CC_CAUSE_REQUEST_PENDING) {
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                break;
+            }
+
+            ccb->cnf_ftr_ack = FALSE;
+
+            /*
+             * Check the status of the cause code
+             */
+            if (msg->cause != CC_CAUSE_NORMAL) {
+                if ((platGetPhraseText(STR_INDEX_CNFR_FAIL_NOCODEC,
+                                             (char *) tmp_str,
+                                             STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) {
+                    lsm_ui_display_notify(tmp_str, NO_FREE_LINES_TIMEOUT);
+                }
+
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, ccb->cns_call_id,
+                               fcb->dcb->line, CC_FEATURE_END_CALL, NULL);
+
+                dcb = fsmdef_get_dcb_by_call_id(ccb->cnf_call_id);
+                if (dcb != NULL) {
+                    /* Build SDP for sending out */
+                    cause = gsmsdp_encode_sdp_and_update_version(dcb,
+                                                                 &ftr_data.resume.msg_body);
+                    if (cause != CC_CAUSE_OK) {
+                        GSM_DEBUG_ERROR(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+                        return (SM_RC_END);
+                    }
+                    ftr_data.resume.cause = CC_CAUSE_OK;
+                    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                                   dcb->line, CC_FEATURE_RESUME, &ftr_data);
+                }
+
+                fsmcnf_cleanup(fcb, __LINE__, TRUE);
+            }
+
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+
+static sm_rcs_t
+fsmcnf_ev_cnfed_onhook (sm_event_t *event)
+{
+    fsm_fcb_t      *fcb   = (fsm_fcb_t *) event->data;
+    sm_rcs_t        sm_rc = SM_RC_CONT;
+    fsmcnf_ccb_t   *ccb   = fcb->ccb;
+    int             join  = 1;
+    fsmdef_dcb_t    *other_dcb;
+    boolean     conf_id_valid = FALSE;
+
+    /* If call_id of the ONHOOK received is of cnf_call_id,
+     * flag to dcb of cns_call_id to know of the event,
+     * and vice versa. We do not want to process more than
+     * one onhook for calls in a conf call. Note that this is
+     * needed because when in Speaker mode, onhook event is
+     * sent to each call_id in active state.
+     */
+    if (fcb->call_id == ccb->cnf_call_id) {
+        other_dcb = fsm_get_dcb(ccb->cns_call_id);
+    } else {
+        other_dcb = fsm_get_dcb(ccb->cnf_call_id);
+    }
+    other_dcb->onhook_received = TRUE;
+
+    // CSCtc04202, when conf_id_valid is FALSE,which mean at least one call is dropped
+    //  Need release the whole conference session.
+    conf_id_valid =  fsmcnd_conf_call_id_valid(ccb);
+    config_get_value(CFGID_CNF_JOIN_ENABLE, &join, sizeof(join));
+    if (((ccb->bridged == TRUE) && (join) && !(ccb->flags & JOINED) && (conf_id_valid)) ||
+        ((ccb->bridged == TRUE) && (ccb->flags & XFER) && (conf_id_valid))) {
+        /*
+         * If bridge of conference call ends the call other 2
+         * users should still be able to talk to each other
+         * So we simulate attended transfer when bridge tries
+         * to end the call
+         */
+        fsmcnf_cnf_xfer(ccb);
+        sm_rc = SM_RC_END;
+    } else {
+        /*
+         * The user is attempting to clear the call, therefore
+         * we need to also clear the other leg of this
+         * conference if it is active.
+         */
+        fsmcnf_other_feature(fcb, CC_FEATURE_END_CALL);
+    }
+
+    fsmcnf_cleanup(fcb, __LINE__, TRUE);
+    return (sm_rc);
+}
+
+
+cc_int32_t
+fsmcnf_show_cmd (cc_int32_t argc, const char *argv[])
+{
+    fsmcnf_ccb_t *ccb;
+    int           i = 0;
+
+    /*
+     * check if need help
+     */
+    if ((argc == 2) && (argv[1][0] == '?')) {
+        debugif_printf("show fsmcnf\n");
+        return (0);
+    }
+
+    debugif_printf("\n-------------------------- FSMCNF ccbs --------------------------");
+    debugif_printf("\ni   cnf_id  ccb         cnf_call_id  cns_call_id  active  bridged");
+    debugif_printf("\n-----------------------------------------------------------------"
+         "\n");
+
+    FSM_FOR_ALL_CBS(ccb, fsmcnf_ccbs, FSMCNF_MAX_CCBS) {
+        debugif_printf("%-2d  %-6d  0x%8p  %-11d  %-11d  %-6d  %-7d\n",
+                       i++, ccb->cnf_id, ccb, ccb->cnf_call_id,
+                       ccb->cns_call_id, ccb->active, ccb->bridged);
+    }
+
+    return (0);
+}
+
+
+void
+fsmcnf_init (void)
+{
+    fsmcnf_ccb_t *ccb;
+    static const char *fname = "fsmcnf_init";
+
+
+    /*
+     * Initialize the ccbs.
+     */
+    fsmcnf_ccbs = (fsmcnf_ccb_t *)
+        cpr_calloc(FSMCNF_MAX_CCBS, sizeof(fsmcnf_ccb_t));
+
+    if (fsmcnf_ccbs == NULL) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Failed to allocate memory for " \
+                            "cnf ccbs.\n", fname);
+        return;
+    }
+
+    FSM_FOR_ALL_CBS(ccb, fsmcnf_ccbs, FSMCNF_MAX_CCBS) {
+        fsmcnf_init_ccb(ccb);
+    }
+
+    /*
+     * Initialize the state/event table.
+     */
+    fsmcnf_sm_table.min_state = FSMCNF_S_MIN;
+    fsmcnf_sm_table.max_state = FSMCNF_S_MAX;
+    fsmcnf_sm_table.min_event = CC_MSG_MIN;
+    fsmcnf_sm_table.max_event = CC_MSG_MAX;
+    fsmcnf_sm_table.table     = (&(fsmcnf_function_table[0][0]));
+}
+
+int
+cc_is_cnf_call (callid_t call_id)
+{
+    if (call_id == CC_NO_CALL_ID) {
+        return FALSE;
+    }
+    return fsmutil_is_cnf_leg(call_id, fsmcnf_ccbs, FSMCNF_MAX_CCBS);
+}
+
+void
+fsmcnf_shutdown (void)
+{
+    cpr_free(fsmcnf_ccbs);
+    fsmcnf_ccbs = NULL;
+}
+
+int
+fsmutil_is_cnf_consult_call (callid_t call_id)
+{
+    return fsmutil_is_cnf_consult_leg(call_id, fsmcnf_ccbs, FSMCNF_MAX_CCBS);
+}
+
+boolean
+fsmcnd_conf_call_id_valid(fsmcnf_ccb_t   *ccb){
+
+        static const char fname[] = "fsmcnd_conf_call_id_valid";
+       if( ccb != NULL){
+                FSM_DEBUG_SM(get_debug_string(FSMCNF_DBG_PTR), ccb->cnf_id,
+                    ccb->cnf_call_id, ccb->cns_call_id, fname, ccb);
+               if((ccb->cnf_call_id != CC_NO_CALL_ID)  && (ccb->cns_call_id != CC_NO_CALL_ID) ){
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
diff --git a/libs/sipcc/core/gsm/fsmdef.c b/libs/sipcc/core/gsm/fsmdef.c
new file mode 100755 (executable)
index 0000000..cec9eb5
--- /dev/null
@@ -0,0 +1,8667 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <limits.h>
+#include "CCProvider.h"
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "cpr_rand.h"
+#include "cpr_timers.h"
+#include "cpr_errno.h"
+#include "phone.h"
+#include "lsm.h"
+#include "fsm.h"
+#include "sm.h"
+#include "ccapi.h"
+#include "ccsip_cc.h"
+#include "phone_debug.h"
+#include "fim.h"
+#include "config.h"
+#include "sdp.h"
+#include "ccsip_sdp.h" // Temporary include
+#include "rtp_defs.h"
+#include "debug.h"
+#include "gsm_sdp.h"
+#include "vcm.h"
+#include "uiapi.h"
+#include "gsm.h"
+#include "phntask.h"
+#include "prot_configmgr.h"
+#include "sip_interface_regmgr.h"
+#include "dialplanint.h"
+#include "subapi.h"
+#include "text_strings.h"
+#include "platform_api.h"
+#include "peer_connection_types.h"
+#include "prlog.h"
+#include "sessionHash.h"
+
+extern void update_kpmlconfig(int kpmlVal);
+extern boolean g_disable_mass_reg_debug_print;
+void escalateDeescalate();
+
+#define FSMDEF_NO_NUMBER    (NULL)
+#define DIGIT_POUND         ('#')
+#define FSMDEF_MAX_DCBS     (LSM_MAX_CALLS)
+#define FSMDEF_CC_CALLER_ID ((cc_state_data_t *)(&(dcb->caller_id)))
+#define RINGBACK_DELAY      90
+
+// Minimum and maximum hold reversion timer in seconds
+#define MIN_HOLD_REVERSION_INTERVAL_TIMER 10
+#define MAX_HOLD_REVERSION_INTERVAL_TIMER 1200
+
+fsmdef_dcb_t *fsmdef_dcbs;
+
+static const char *fsmdef_state_names[] = {
+    "IDLE",
+    "COLLECTING_INFO",
+    "CALL_SENT",
+    "OUTGOING_PROCEEDING",
+    "KPML_COLLECTING_INFO",
+    "OUTGOING_ALERTING",
+    "INCOMING_ALERTING",
+    "CONNECTING",
+    "JOINING",
+    "CONNECTED",
+    "CONNECTED MEDIA PEND",
+    "RELEASING",
+    "HOLD_PENDING",
+    "HOLDING",
+    "RESUME_PENDING",
+    "PRESERVED"
+};
+
+
+static sm_rcs_t fsmdef_ev_createoffer(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_createanswer(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_setlocaldesc(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_setremotedesc(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_setpeerconnection(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_localdesc(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_remotedesc(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_addstream(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_removestream(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_addcandidate(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_default(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_default_feature_ack(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_idle_setup(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_idle_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_idle_offhook(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_idle_dialstring(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_onhook(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_collectinginfo_release(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_collectinginfo_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_offhook(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_digit_begin(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_dialstring(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_proceeding(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_callsent_release(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_callsent_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_out_alerting(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_inalerting_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_inalerting_offhook(sm_event_t *event);
+static sm_rcs_t fsmdef_handle_inalerting_offhook_answer(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_connected(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_connected_line(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_connected_ack(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_connecting_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_connected_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_connected_media_pend_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_connected_media_pend_feature_ack(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_release(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_release_complete(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_releasing_release(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_releasing_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_releasing_onhook(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_hold_pending_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_hold_pending_feature_ack(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_holding_release(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_holding_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_holding_feature_ack(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_holding_onhook(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_holding_offhook(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_session_audit(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_resume_pending_feature(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_resume_pending_feature_ack(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_preserved_feature(sm_event_t *event);
+static void fsmdef_ev_join(cc_feature_data_t *data);
+
+static sm_rcs_t fsmdef_cfwd_clear_ccm(fsm_fcb_t *fcb);
+static sm_rcs_t fsmdef_process_dialstring_for_callfwd(sm_event_t *event);
+static sm_rcs_t fsmdef_process_cfwd_softkey_event(sm_event_t *event);
+static sm_rcs_t fsmdef_cfwd_clear_ccm(fsm_fcb_t *fcb);
+static sm_rcs_t fsmdef_ev_joining_connected_ack(sm_event_t *event);
+static sm_rcs_t fsmdef_ev_joining_offhook(sm_event_t *event);
+
+static void fsmdef_b2bjoin_invoke(fsmdef_dcb_t *dcb,
+                                  cc_feature_data_t *join_data);
+static void fsmdef_select_invoke(fsmdef_dcb_t *dcb,
+                                 cc_feature_data_t *select_data);
+static void fsmdef_handle_join_pending(fsmdef_dcb_t *dcb);
+static void fsmdef_append_dialstring_to_feature_uri(fsmdef_dcb_t *dcb,
+                                                    const char *dialstring);
+static boolean fsmdef_is_feature_uri_configured(cc_features_t ftr_id);
+static void fsmdef_set_call_info_cc_call_state(fsmdef_dcb_t *dcb,
+                                               cc_states_t state,
+                                               cc_causes_t cause);
+static boolean fsmdef_extract_join_target(sm_event_t *event);
+static void fsmdef_ev_notify_feature(cc_feature_t *msg, fsmdef_dcb_t *dcb);
+static void fsmdef_notify_hook_event(fsm_fcb_t *fcb, cc_msgs_t msg,
+                                     char *global_call_id,
+                                     callid_t prim_call_id,
+                                     cc_hold_resume_reason_e consult_reason,
+                                     monitor_mode_t monitor_mode,
+                                     cfwdall_mode_t cfwdall_mode);
+static void fsmdef_update_callinfo_security_status(fsmdef_dcb_t *dcb,
+                                     cc_feature_data_call_info_t *call_info);
+static void fsmdef_update_calltype (fsm_fcb_t *fcb, cc_feature_t *msg);
+
+
+/*
+ * TODO <emannion> Update events for correct JSEP transitions
+ *                 Instead of providing events for all states
+ */
+static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
+{
+/* FSMDEF_S_IDLE ------------------------------------------------------------ */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_idle_setup,       // New incoming
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_release_complete,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_idle_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_idle_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_default,
+    /* CC_MSG_LINE             */ fsmdef_ev_idle_offhook,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_idle_dialstring,  // new outgoing
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_COLLECT_INFO ---------------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_collectinginfo_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_collectinginfo_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_default,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_digit_begin,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_dialstring,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_CALL_SENT ------------------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_proceeding,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_out_alerting,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_connected,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_callsent_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_callsent_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_OUTGOING_PROCEEDING --------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_out_alerting,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_connected,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_callsent_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_callsent_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_KPML_COLLECT_INFO ----------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_out_alerting,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_connected,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_callsent_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_collectinginfo_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_default,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_digit_begin,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_OUTGOING_ALERTING ----------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_out_alerting,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_connected,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_callsent_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_callsent_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_INCOMING_ALERTING ----------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_inalerting_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_inalerting_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_inalerting_offhook,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_CONNECTING ------------------------------------------------------ */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_connected_ack,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_connecting_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_connected_line,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_JOINING --------------------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_joining_connected_ack,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_connecting_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_joining_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_connected_line,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_CONNECTED ------------------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_connected_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_connected_line,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_CONNECTED_MEDIA_PEND  ------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_connected_media_pend_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_connected_media_pend_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_connected_line,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_RELEASING ------------------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_releasing_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_release_complete,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_releasing_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_default,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_releasing_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_connected_line,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_HOLD_PENDING ---------------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_holding_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_hold_pending_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_hold_pending_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_default,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_HOLDING --------------------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_holding_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_holding_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_holding_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_holding_offhook,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_holding_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_RESUME_PENDING -------------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_resume_pending_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_resume_pending_feature_ack,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_default,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
+/* FSMDEF_S_PRESERVED  ------------------------------------------------------ */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_release,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_preserved_feature,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_default,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_session_audit,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
+    /* CC_MSG_LOCALDESC        */ fsmdef_ev_localdesc,
+    /* CC_MSG_REMOTEDESC       */ fsmdef_ev_remotedesc,
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    }
+};
+
+static sm_table_t fsmdef_sm_table;
+sm_table_t *pfsmdef_sm_table = &fsmdef_sm_table;
+
+/*--------------------------------------------------------------------------
+ * Global data
+ *--------------------------------------------------------------------------
+ */
+uint16_t g_numofselected_calls = 0;
+boolean  g_b2bjoin_pending = FALSE;
+callid_t g_b2bjoin_callid = CC_NO_CALL_ID;
+
+static sdp_direction_e s_default_video_dir = SDP_DIRECTION_SENDRECV;
+static sdp_direction_e s_session_video_dir = SDP_MAX_QOS_DIRECTIONS;
+
+
+void set_default_video_pref(int pref) {
+   s_default_video_dir = pref;
+}
+
+void set_next_sess_video_pref(int pref) {
+   s_session_video_dir = pref;
+}
+
+const char *
+fsmdef_state_name (int state)
+{
+    if ((state <= FSMDEF_S_MIN) || (state >= FSMDEF_S_MAX)) {
+        return (get_debug_string(GSM_UNDEFINED));
+    }
+
+    return (fsmdef_state_names[state]);
+}
+
+/*
+ * fsmdef_get_dcb_by_call_id
+ *
+ * return the dcb referenced by the given call_id
+ */
+fsmdef_dcb_t *
+fsmdef_get_dcb_by_call_id (callid_t call_id)
+{
+    static const char fname[] = "fsmdef_get_dcb_by_call_id";
+    fsmdef_dcb_t   *dcb;
+    fsmdef_dcb_t   *dcb_found = NULL;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (dcb->call_id == call_id) {
+            dcb_found = dcb;
+            break;
+        }
+    }
+
+    if (dcb_found) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_PTR),
+                     dcb->call_id, dcb->line, fname, dcb_found);
+    }
+
+    return (dcb_found);
+}
+
+
+/*
+ * fsmdef_check_if_chaperone_call_exist
+ *
+ * return the dcb referenced by the given call_id
+ */
+boolean
+fsmdef_check_if_chaperone_call_exist (void)
+{
+    static const char fname[] = "fsmdef_check_if_chaperone_call_exist";
+    fsmdef_dcb_t   *dcb;
+    boolean result = FALSE;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+       if(dcb->policy == CC_POLICY_CHAPERONE){
+               result = TRUE;
+               break;
+       }
+    }
+
+    if (result) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_PTR),
+                     dcb->call_id, dcb->line, fname, dcb);
+    }
+
+    return result;
+}
+
+
+void fsmdef_get_rtp_stat (fsmdef_dcb_t *dcb , cc_kfact_t *kfactor)
+{
+    static const char fname[] ="fsmdef_get_rtp_stat";
+
+    int      call_stats_flag;
+    fsmdef_media_t *media;
+    media = gsmsdp_find_audio_media(dcb);
+
+    if (!media) {
+        GSM_ERR_MSG(GSM_F_PREFIX"dcb media pointer invalid\n", fname);
+        return;
+    }
+
+    memset(kfactor, 0, sizeof(cc_kfact_t));
+    config_get_value(CFGID_CALL_STATS, &call_stats_flag, sizeof(call_stats_flag));
+
+    if (call_stats_flag) {
+        vcmGetRtpStats(media->cap_index, dcb->group_id,
+                          media->refid,
+                          lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), &(kfactor->rxstats[0]), &(kfactor->txstats[0]));
+    }
+}
+
+/**
+ * The function sets or clears the local hold status to the given media or
+ * for all of the media.
+ *
+ * @param[in]dcb          - pointer to fsmdef_dcb_t.
+ * @param[in]media        - specify which media to use. The value of
+ *                          NULL indicates for all media.
+ * @param[in]set          - set local hold or clear local hold.
+ *
+ * @return  none
+ *
+ * @pre     (dcb not_eq NULL)
+ */
+static void
+fsmdef_update_media_hold_status (fsmdef_dcb_t *dcb, fsmdef_media_t *media,
+                                 boolean set)
+{
+    fsmdef_media_t *start_media, *end_media;
+
+    if (media == NULL) {
+        /* NULL value of the given media indicates for all media */
+        start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb);
+        end_media   = NULL; /* NULL means till the end of the list */
+    } else {
+        /* given media, uses the provided media */
+        start_media = media;
+        end_media   = media;
+    }
+
+    GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb) {
+        if (GSMSDP_MEDIA_ENABLED(media)) {
+            if (set) {
+                FSM_SET_FLAGS(media->hold, FSM_HOLD_LCL);
+            } else {
+                FSM_RESET_FLAGS(media->hold, FSM_HOLD_LCL);
+            }
+        }
+    }
+}
+
+/**
+ * The function checks to see whether all media streams are
+ * in locally held or not.
+ *
+ * @param[in]dcb          - pointer to fsmdef_dcb_t.
+ *
+ * @return  TRUE  - all media streams are in local hold.
+ *          FALSE - not all media streams are in local hold.
+ *
+ * @pre     (dcb not_eq NULL)
+ */
+static boolean
+fsmdef_all_media_are_local_hold (fsmdef_dcb_t *dcb)
+{
+    fsmdef_media_t *media;
+    /*
+     * Check the local hold status of each media to see if the
+     * media is already locally held or not.
+     */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            continue;
+        }
+        if (!FSM_CHK_FLAGS(media->hold, FSM_HOLD_LCL)) {
+            /* found one media that is not on hold */
+            return (FALSE);
+        }
+    }
+    /* all media streams are local held */
+    return (TRUE);
+}
+
+/**
+ * The function gets the numbmer of media in local hold.
+ *
+ * @param[in]dcb          - pointer to fsmdef_dcb_t.
+ *
+ * @return  uint16_t for the number of media in local hold.
+ *
+ * @pre     (dcb not_eq NULL)
+ */
+static unsigned int
+fsmdef_num_media_in_local_hold (fsmdef_dcb_t *dcb)
+{
+    fsmdef_media_t *media;
+    unsigned int num_local_hold = 0;
+
+    /* Check the local hold status of the media(s) */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            continue;
+        }
+        if (FSM_CHK_FLAGS(media->hold, FSM_HOLD_LCL)) {
+            num_local_hold++;
+        }
+    }
+    return (num_local_hold);
+}
+
+/**
+ * The function is a convenient function to set each local hold in
+ * SDP for each media if it is marked as locally held.
+ *
+ * @param[in]dcb          - pointer to fsmdef_dcb_t.
+ *
+ * @return  None
+ *
+ * @pre     (dcb not_eq NULL)
+ */
+static void
+fsmdef_set_per_media_local_hold_sdp (fsmdef_dcb_t *dcb)
+{
+    fsmdef_media_t *media;
+
+    GSMSDP_FOR_ALL_MEDIA(media, dcb) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            continue;
+        }
+        if (FSM_CHK_FLAGS(media->hold, FSM_HOLD_LCL)) {
+            /* set local hold to this media entry */
+            gsmsdp_set_local_hold_sdp(dcb, media);
+        }
+    }
+}
+
+void
+fsmdef_init_dcb (fsmdef_dcb_t *dcb, callid_t call_id,
+                 fsmdef_call_types_t call_type,
+                 string_t called_number, line_t line, fsm_fcb_t *fcb)
+{
+    string_t calling_name;
+    int      blocking;
+    char     name[MAX_LINE_NAME_SIZE];
+
+    dcb->call_id = call_id;
+    dcb->line = line;
+
+    dcb->spoof_ringout_requested = FALSE;
+    dcb->spoof_ringout_applied = FALSE;
+
+    dcb->log_disp = CC_CALL_LOG_DISP_UNKNWN;
+
+    fsmutil_init_groupid(dcb, call_id, call_type);
+
+    /*
+     * Fill in as much of the caller_id data as possible.
+     * Different data is available based on the call_type.
+     */
+    switch (call_type) {
+    case FSMDEF_CALL_TYPE_OUTGOING:
+        config_get_value(CFGID_CALLERID_BLOCKING, &blocking, sizeof(blocking));
+        if (line != 0) {
+            sip_config_get_display_name(line, name, sizeof(name));
+        }
+        if (blocking & 1 || line == 0) {
+            calling_name = SIP_HEADER_ANONYMOUS_STR;
+        } else {
+            calling_name = name;
+        }
+        dcb->caller_id.calling_name = strlib_update(dcb->caller_id.calling_name,
+                                                    calling_name);
+        dcb->caller_id.calling_number =
+            strlib_update(dcb->caller_id.calling_number, name);
+
+        /*
+         * called_xxx data will be set when the fsmdef receives the dialstring.
+         */
+        dcb->caller_id.called_name   = strlib_empty();
+        dcb->caller_id.called_number = strlib_empty();
+        dcb->caller_id.orig_rpid_number = strlib_empty();
+
+        dcb->inbound = FALSE;
+
+        break;
+
+    case FSMDEF_CALL_TYPE_INCOMING:
+    case FSMDEF_CALL_TYPE_FORWARD:
+        /*
+         * calling_xxx data will be set when the fsmdef receives the setup.
+         */
+        dcb->caller_id.calling_name   = strlib_empty();
+        dcb->caller_id.calling_number = strlib_empty();
+
+        dcb->caller_id.last_redirect_name = strlib_empty();
+        dcb->caller_id.last_redirect_number = strlib_empty();
+        dcb->caller_id.orig_called_name = strlib_empty();
+        dcb->caller_id.orig_called_number = strlib_empty();
+        dcb->caller_id.orig_rpid_number = strlib_empty();
+
+        sip_config_get_display_name(line, name, sizeof(name));
+        dcb->caller_id.called_name =
+            strlib_update(dcb->caller_id.called_name, name);
+        dcb->caller_id.called_number =
+            strlib_update(dcb->caller_id.called_number, called_number);
+
+        dcb->inbound = TRUE;
+
+        break;
+
+    case FSMDEF_CALL_TYPE_NONE:
+        dcb->caller_id.calling_name   = strlib_empty();
+        dcb->caller_id.calling_number = strlib_empty();
+        dcb->caller_id.called_name    = strlib_empty();
+        dcb->caller_id.called_number  = strlib_empty();
+        dcb->caller_id.alt_calling_number = strlib_empty();
+        dcb->caller_id.last_redirect_name   = strlib_empty();
+        dcb->caller_id.last_redirect_number = strlib_empty();
+        dcb->caller_id.orig_called_name     = strlib_empty();
+        dcb->caller_id.orig_called_number   = strlib_empty();
+        dcb->caller_id.orig_rpid_number = strlib_empty();
+        dcb->inbound = FALSE;
+
+        break;
+    default:
+        break;
+    }                           /* switch (call_type) { */
+
+    dcb->caller_id.display_calling_number = TRUE;
+    dcb->caller_id.display_called_number = TRUE;
+
+    /* Initially, assume ui update is required for a new call. */
+    dcb->ui_update_required = TRUE;
+    dcb->placed_call_update_required = TRUE;
+
+    dcb->is_conf_call = FALSE; /*Initially, set to conf call false*/
+
+    dcb->digit_cnt = 0;
+
+    dcb->call_type = call_type;
+    dcb->orientation = CC_ORIENTATION_NONE;
+
+    dcb->caller_id.call_instance_id = 0;
+
+    dcb->msgs_sent = FSMDEF_MSG_NONE;
+    dcb->msgs_rcvd = FSMDEF_MSG_NONE;
+
+    dcb->send_release = FALSE;
+
+    dcb->inband = FALSE;
+    dcb->inband_received = FALSE;
+    dcb->outofband = 0;
+
+    dcb->remote_sdp_present = FALSE;
+    dcb->remote_sdp_in_ack = FALSE;
+
+    dcb->sdp = NULL;
+    dcb->src_sdp_version = 0;
+
+    dcb->dial_mode = DIAL_MODE_NUMERIC;
+
+    dcb->hold_reason = CC_REASON_NONE;
+
+    dcb->pd_updated = FALSE;
+
+    dcb->alerting_tone = VCM_NO_TONE;
+    dcb->tone_direction = VCM_PLAY_TONE_TO_EAR;
+
+    dcb->alert_info = ALERTING_NONE;
+
+    dcb->dialplan_tone = FALSE;
+
+    dcb->active_tone = VCM_NO_TONE;
+
+    dcb->monrec_tone_action = FSMDEF_MRTONE_NO_ACTION;
+    dcb->monitor_tone_direction = VCM_PLAY_TONE_TO_EAR;
+    dcb->recorder_tone_direction = VCM_PLAY_TONE_TO_EAR;
+
+    dcb->play_tone_action = FSMDEF_PLAYTONE_NO_ACTION;
+
+    dcb->fcb = fcb;
+
+    dcb->early_error_release = FALSE;
+
+    dcb->active_feature = CC_FEATURE_NONE;
+
+    /* Release transient timers if any have been allocated */
+    if (dcb->err_onhook_tmr) {
+        (void) cprDestroyTimer(dcb->err_onhook_tmr);
+        dcb->err_onhook_tmr = NULL;
+    }
+    if (dcb->req_pending_tmr) {
+        (void) cprDestroyTimer(dcb->req_pending_tmr);
+        dcb->req_pending_tmr = NULL;
+    }
+
+    FSM_SET_SECURITY_STATUS(dcb, CC_SECURITY_UNKNOWN);
+    FSM_SET_POLICY(dcb, CC_POLICY_UNKNOWN);
+    dcb->session = PRIMARY;
+
+    dcb->dsp_out_of_resources = FALSE;
+
+    if (dcb->selected) {
+        g_numofselected_calls--;
+    }
+
+    dcb->selected = FALSE;
+
+    dcb->select_pending = FALSE;
+    dcb->call_not_counted_in_mnc_bt = FALSE;
+    if (g_disable_mass_reg_debug_print == FALSE) {
+        FSM_DEBUG_SM(DEB_L_C_F_PREFIX"call_not_counted_in_mnc_bt = FALSE\n",
+            DEB_L_C_F_PREFIX_ARGS(FSM, line, call_id, "fsmdef_init_dcb"));
+    }
+
+    /* clear all bit flags */
+    dcb->flags = 0;
+    dcb->onhook_received = FALSE;
+
+    dcb->cur_video_avail = SDP_DIRECTION_INACTIVE;
+
+    if ( s_session_video_dir != SDP_MAX_QOS_DIRECTIONS &&
+         call_type == FSMDEF_CALL_TYPE_OUTGOING ) {
+        dcb->video_pref = s_session_video_dir;
+        s_session_video_dir = SDP_MAX_QOS_DIRECTIONS;
+    } else {
+        dcb->video_pref = s_default_video_dir;
+    }
+
+    gsmsdp_init_media_list(dcb);
+
+    dcb->join_call_id = CC_NO_CALL_ID;
+    dcb->callref = 0;
+
+    dcb->ice_ufrag = NULL;
+    dcb->ice_pwd = NULL;
+    dcb->ice_default_candidate_addr[0] = '\0';
+
+    dcb->digest_alg[0] = '\0';
+    dcb->digest[0] = '\0';
+}
+
+
+static void
+fsmdef_free_dcb (fsmdef_dcb_t *dcb)
+{
+    if (dcb == NULL) {
+        return;
+    }
+
+    strlib_free(dcb->caller_id.calling_name);
+    strlib_free(dcb->caller_id.calling_number);
+    strlib_free(dcb->caller_id.alt_calling_number);
+    strlib_free(dcb->caller_id.called_name);
+    strlib_free(dcb->caller_id.called_number);
+
+    strlib_free(dcb->caller_id.last_redirect_name);
+    strlib_free(dcb->caller_id.last_redirect_number);
+    strlib_free(dcb->caller_id.orig_called_name);
+    strlib_free(dcb->caller_id.orig_called_number);
+    strlib_free(dcb->caller_id.orig_rpid_number);
+
+    /* Cancel any existing error onhook timer */
+    if (dcb->err_onhook_tmr) {
+        (void) cprCancelTimer(dcb->err_onhook_tmr);
+        (void) cprDestroyTimer(dcb->err_onhook_tmr);
+        dcb->err_onhook_tmr = NULL;
+    }
+
+    /* Cancel any existing request pending timer */
+    if (dcb->req_pending_tmr) {
+        (void) cprCancelTimer(dcb->req_pending_tmr);
+        (void) cprDestroyTimer(dcb->req_pending_tmr);
+        dcb->req_pending_tmr = NULL;
+    }
+
+    /* Cancel any existing ringback delay timer */
+    if (dcb->ringback_delay_tmr) {
+        (void) cprCancelTimer(dcb->ringback_delay_tmr);
+    }
+
+    // Free the call instance id
+    if (dcb->caller_id.call_instance_id != 0) {
+        fsmutil_free_ci_id(dcb->caller_id.call_instance_id, dcb->line);
+    }
+
+    /* clean media list */
+    gsmsdp_clean_media_list(dcb);
+
+    gsmsdp_free(dcb);
+
+    fsmdef_init_dcb(dcb, CC_NO_CALL_ID, FSMDEF_CALL_TYPE_NONE, NULL,
+                    LSM_NO_LINE, NULL);
+
+    /*
+     * Cache random numbers for SRTP keys
+     */
+    gsmsdp_cache_crypto_keys();
+
+}
+
+void
+fsmdef_free_cb (fim_icb_t *icb, callid_t call_id)
+{
+    fsm_fcb_t      *fcb = NULL;
+    fsmdef_dcb_t   *dcb = NULL;
+
+    if (call_id != CC_NO_CALL_ID) {
+        dcb = fsmdef_get_dcb_by_call_id(call_id);
+        if (dcb != NULL) {
+            fcb = dcb->fcb;
+            fsmdef_init_dcb(dcb, CC_NO_CALL_ID, FSMDEF_CALL_TYPE_NONE,
+                            NULL, LSM_NO_LINE, NULL);
+            /* fsmdef_init_dcb(...,NULL) will always set the fcb ptr to NULL,
+               so if fsmdef_free_cb were called on that we'd have fcb==NULL here */
+            if (fcb != NULL) {
+              fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE);
+            }
+        } else {
+
+            fcb = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+            if (fcb != NULL) {
+                fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE);
+            }
+        }
+    }
+}
+
+
+/*
+ *  ROUTINE:     fsmdef_get_new_dcb
+ *
+ *  DESCRIPTION: return a new dcb initialized with the given data
+ *
+ *  PARAMETERS:
+ *      call_id:    call_id
+ *
+ *  RETURNS:
+ *      dcb: the new dcb
+ *
+ *  NOTES:       None
+ */
+fsmdef_dcb_t *
+fsmdef_get_new_dcb (callid_t call_id)
+{
+    static const char fname[] = "fsmdef_get_new_dcb";
+    fsmdef_dcb_t *dcb = NULL;
+
+    /*
+     * Get a free dcb.
+     */
+    if ((dcb = fsmdef_get_dcb_by_call_id(CC_NO_CALL_ID)) == NULL) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), call_id, 0, fname,
+                     "no dcbs available");
+
+        return (NULL);
+    }
+
+    dcb->call_id = call_id;
+
+    FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_PTR),
+                 dcb->call_id, dcb->line, fname, dcb);
+
+    return (dcb);
+}
+
+
+/**
+ *
+ * Returns dcb related to connected call.
+ *
+ * @param none
+ *
+ * @return  dcb of connected call.
+ *
+ * @pre     none
+ */
+
+fsmdef_dcb_t *
+fsmdef_get_connected_call (void)
+{
+    fsmdef_dcb_t *dcb;
+    fsm_fcb_t    *fcb;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (dcb->call_id != CC_NO_CALL_ID) {
+            fcb = dcb->fcb;
+            if ((fcb != NULL) && (fcb->state == FSMDEF_S_RESUME_PENDING ||
+                                fcb->state == FSMDEF_S_CONNECTED ||
+                                fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND)) {
+
+                return (dcb);
+            }
+        }
+    }
+
+    return (NULL);
+}
+
+/**
+ *
+ * Returns dcb related to alerting out call.
+ *
+ * @param none
+ *
+ * @return  dcb of outgoing alerting call.
+ *
+ * @pre     none
+ */
+static fsmdef_dcb_t *
+fsmdef_get_alertingout_call (void)
+{
+    fsmdef_dcb_t *dcb;
+    fsm_fcb_t    *fcb;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (dcb->call_id != CC_NO_CALL_ID) {
+            fcb = dcb->fcb;
+            if ((fcb != NULL) && (fcb->state == FSMDEF_S_OUTGOING_ALERTING)) {
+                return (dcb);
+            }
+        }
+    }
+
+    return (NULL);
+}
+
+/*
+ * fsmdef_get_active_call_cnt
+ *
+ * Return the count of active calls aside from the
+ * callid passed in.
+ */
+int
+fsmdef_get_active_call_cnt (callid_t callId)
+{
+    fsmdef_dcb_t *dcb;
+    int           cnt = 0;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if ((dcb->call_id != CC_NO_CALL_ID) && (dcb->call_id != callId)) {
+            cnt++;
+        }
+    }
+
+    return (cnt);
+}
+
+
+/*
+ * return the dcbs and count of calls that are ringing and
+ * are in error state not including the given call_id
+ *
+ * @param pointer to dcbs array
+ * @param call_id that should be ignored from search
+ *
+ * @return  int number of ringing or error state calls
+ *
+ * @pre     none
+*/
+static int
+fsmdef_get_ringing_n_error_call_dcbs (fsmdef_dcb_t **dcbs, callid_t ignore_call_id)
+{
+    fsmdef_dcb_t *dcb;
+    int           cnt = 0;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (dcb->spoof_ringout_applied ||
+            ((dcb->call_id != CC_NO_CALL_ID) &&
+             (dcb->call_id != ignore_call_id) &&
+             (dcb->fcb && ((dcb->fcb->state <= FSMDEF_S_CONNECTING) ||
+             (dcb->fcb->state == FSMDEF_S_RELEASING))))) {
+            dcbs[cnt++] = dcb;
+        }
+    }
+
+    return (cnt);
+}
+
+
+int
+fsmdef_get_dcbs_in_held_state (fsmdef_dcb_t **dcbs, callid_t ignore_call_id)
+{
+    fsmdef_dcb_t *dcb;
+    int           cnt = 0;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if ((dcb->call_id != CC_NO_CALL_ID) &&
+            (dcb->call_id != ignore_call_id) &&
+            (dcb->fcb && (dcb->fcb->state == FSMDEF_S_HOLDING ||
+             dcb->fcb->state == FSMDEF_S_HOLD_PENDING))) {
+            dcbs[cnt++] = dcb;
+        }
+    }
+
+    return (cnt);
+}
+
+void fsmdef_call_cc_state_dialing (fsmdef_dcb_t *dcb, boolean suppress)
+{
+  cc_state_data_dialing_t data;
+
+  if ( dcb->caller_id.called_number[0] == '\0' ) {
+     data.play_dt = TRUE;
+  } else {
+     data.play_dt = FALSE;
+  }
+
+  data.suppress_stutter = suppress;
+
+  cc_call_state(dcb->call_id, dcb->line, CC_STATE_DIALING,
+                          (cc_state_data_t *)(&data));
+}
+
+/*
+ * fsmdef_get_other_dcb_by_line
+ *
+ * return the dcb of the call that is active on a given line,
+ * not including the given call_id
+ */
+fsmdef_dcb_t *
+fsmdef_get_other_dcb_by_line (callid_t call_id, line_t line)
+{
+    fsmdef_dcb_t *dcb;
+    fsmdef_dcb_t *dcb_found = NULL;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if ((dcb->call_id != CC_NO_CALL_ID) &&
+            (dcb->line == line) && (dcb->call_id != call_id)) {
+            dcb_found = dcb;
+        }
+    }
+
+    return (dcb_found);
+}
+
+/*
+ * fsmdef_are_there_selected_calls_onotherline
+ *
+ * @param line - line number
+ *
+ * loop thru the dcbs and check if there are selected calls
+ * on a line other than this one
+ *
+ * @return TRUE, there are
+ *         FALSE there are not
+ */
+boolean fsmdef_are_there_selected_calls_onotherline (line_t line)
+{
+    fsmdef_dcb_t *dcb;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (dcb->selected) {
+            if (dcb->line != line) {
+                return (TRUE);
+            }
+        }
+    }
+    return (FALSE);
+}
+
+/*
+ * fsmdef_are_join_calls_on_same_line
+ *
+ * @param line - line number
+ *
+ * loop thru the dcbs and check if the line passed is the
+ * same as where the initial join started
+ *
+ * @return TRUE, they are on the same line
+ *         FALSE not on the same line
+ */
+boolean fsmdef_are_join_calls_on_same_line (line_t line)
+{
+    fsmdef_dcb_t *dcb;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (dcb->call_id == g_b2bjoin_callid) {
+            if (dcb->line != line) {
+                return (FALSE);
+            } else {
+                return (TRUE);
+            }
+        }
+    }
+    return (FALSE);
+}
+
+/**
+ *
+ * Handles media capability update feature event from platform when
+ * media stream capability changes.
+ *
+ * @param[in]msg - pointer to the cc_feature_t.
+ *
+ * @return  None.
+ *
+ * @pre     (msg not_eq NULL)
+ */
+void
+fsmdef_update_media_cap_feature_event (cc_feature_t *msg)
+{
+    static const char fname[] = "fsmdef_update_media_cap_feature_event";
+    fsmdef_dcb_t      *dcb;
+    fsm_fcb_t         *fcb;
+
+    FSM_DEBUG_SM(DEB_L_C_F_PREFIX"\n", DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, msg->call_id, fname));
+
+    /*
+     * Find the connected call to send the media capability update
+     * event to. There can be more than one call chains in
+     * connected state for an example, a call that participates in
+     * local conference, barged, monitored. All calls that
+     * are active are sent the update event. Each call leg will handle
+     * the event.
+     *
+     */
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (dcb->call_id != CC_NO_CALL_ID) {
+            fcb = dcb->fcb;
+            if ((fcb != NULL) && (fcb->state == FSMDEF_S_RESUME_PENDING ||
+                                fcb->state == FSMDEF_S_CONNECTED)) {
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                               dcb->line, CC_FEATURE_UPD_MEDIA_CAP, NULL);
+
+            }
+        }
+    }
+}
+
+/**
+ *
+ * Function to find and hold any connected call.
+ *
+ * @param call_id        call id of the call
+ * @param wait     flag to indicate if the caller has to wait ton invoke the event
+ * @param src_id   source id of the caller where to post hold or endcall event.
+ *
+ * @return  none
+ *
+ * @pre     (wait == FALSE)
+ */
+
+static void
+fsmdef_find_and_hold_connected_call (callid_t call_id, boolean *wait,
+                                     cc_srcs_t src_id)
+{
+    fsmdef_dcb_t     *con_dcb;
+    fsmcnf_ccb_t     *ccb;
+    fsmcnf_ccb_t     *con_ccb;
+    fsmxfr_xcb_t     *xcb;
+    cc_feature_data_t data;
+    callid_t          other_call_id;
+    callid_t          other_call_id2;
+
+    *wait = FALSE;
+
+    /*
+     * Place the connected call, if there is one, on hold,
+     * but there are some restrictions to ignore the hold request:
+     * 1. the connected call must be different than the one requesting the hold,
+     * 2. the connected call is involved with this call in a conference and
+     *    the conference is active.
+     * NOTE: for case 2, why do we not send a hold for each of the calls
+     *       involved in the conference? Because, the fsmcnf will generate the
+     *       second hold for the other leg of the conference when it receives
+     *       this hold.
+     * 3. the connected call is in a conference with another call that is
+     *    involved with a transfer. This is the case when two calls are in a
+     *    conference and a bridge decides to transfer one leg of the bridge
+     *    using REFER. I.E., cnf between A-B and A-C. B initiates transfer to D.
+     *    B holds A-B, sends REFER to A, A holds A-B, initiates A-D, D answers,
+     *    A hangs up A-B and connects conference. So, when A initiates A-D,
+     *    we do not want to hold the active bridge between A-C.
+     */
+    con_dcb = fsmdef_get_connected_call();
+    if ((con_dcb != NULL) && ((con_dcb->call_id != call_id) ||
+                              (con_dcb->spoof_ringout_applied == FALSE))) {
+        ccb     = fsmcnf_get_ccb_by_call_id(call_id);
+        con_ccb = fsmcnf_get_ccb_by_call_id(con_dcb->call_id);
+
+
+        if ((ccb == NULL) || (con_ccb == NULL) ||
+            ((ccb == con_ccb) && (ccb->active != TRUE)) || (ccb != con_ccb)) {
+            other_call_id = fsmcnf_get_other_call_id(con_ccb, con_dcb->call_id);
+            xcb = fsmxfr_get_xcb_by_call_id(other_call_id);
+
+            other_call_id2 = fsmxfr_get_other_call_id(xcb, other_call_id);
+
+            if (call_id != other_call_id2) {
+                *wait = TRUE;
+
+                data.hold.call_info.type = CC_FEAT_HOLD;
+                data.hold.call_info.data.hold_resume_reason =
+                    CC_REASON_INTERNAL;
+                data.hold.msg_body.num_parts = 0;
+                data.hold.call_info.data.call_info_feat_data.swap = FALSE;
+                data.hold.call_info.data.call_info_feat_data.protect = FALSE;
+                cc_int_feature(src_id, CC_SRC_GSM, con_dcb->call_id,
+                               con_dcb->line, CC_FEATURE_HOLD, &data);
+            }
+        }
+    }
+}
+
+/*
+ * Function  post a event to end the call which are in ringing
+ * reorder or busy and connecting state. This would indicate call function to
+ * wait on the existing event.
+ *
+ * @param call_id that should be ignored from search
+ * @param pointer to boolean to indicate if the caller has to wait
+ *
+ * @return  none
+ *
+ * @pre     none
+*/
+static void
+fsmdef_find_and_handle_ring_connecting_releasing_calls (callid_t call_id, boolean *wait)
+{
+    int               i;
+    int               act_dcb_cnt;
+    fsmdef_dcb_t     *act_dcb;
+    fsmdef_dcb_t     *act_dcbs[LSM_MAX_CALLS];
+    cc_feature_data_t data;
+
+    *wait = FALSE;
+
+    data.endcall.cause         = CC_CAUSE_NORMAL;
+    data.endcall.dialstring[0] = '\0';
+
+    act_dcb_cnt = fsmdef_get_ringing_n_error_call_dcbs(act_dcbs, call_id);
+    for (i = 0; i < act_dcb_cnt; i++) {
+        act_dcb = act_dcbs[i];
+        /*
+         * Clear all the outgoing ringing lines (if there are any).
+         */
+        if (act_dcb->call_type == FSMDEF_CALL_TYPE_OUTGOING ||
+            act_dcb->spoof_ringout_applied) {
+            *wait = TRUE;
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, act_dcb->call_id,
+                           act_dcb->line, CC_FEATURE_END_CALL, &data);
+
+        }
+        else if (act_dcb->fcb->state == FSMDEF_S_CONNECTING) {
+            /* If the call is in connecting state, then wait till SIP
+             * response to get the call in connected state.
+             * The call can be put on hold only when call is connected.
+             */
+            *wait = TRUE;
+        }
+    }
+}
+
+void
+fsmdef_end_call (fsmdef_dcb_t *dcb, cc_causes_t cause)
+{
+    cc_feature_data_t data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+    data.endcall.cause         = cause;
+    data.endcall.dialstring[0] = '\0';
+
+    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
+                   CC_FEATURE_END_CALL, &data);
+}
+
+/*
+ * fsmdef_clear_preserved_calls
+ *
+ * Release any calls in the preserved state
+ */
+static void
+fsmdef_clear_preserved_calls (boolean *wait)
+{
+    fsmdef_dcb_t *dcb;
+
+    *wait = FALSE;
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if ((dcb->call_id != CC_NO_CALL_ID) &&
+            (dcb->fcb->state == FSMDEF_S_PRESERVED)) {
+            *wait = TRUE;
+            fsmdef_end_call(dcb, CC_CAUSE_NORMAL);
+        }
+    }
+}
+
+/**
+ *
+ * Checks to see if actions are required for existing calls before the new
+ * call can be activated.
+ *
+ * 1. Place the currently active connected call on hold.
+ * 2. Clear any ringing calls.
+ * 3. Release any call in the preserved state.
+ *
+ * @param is_newcall  To indicate if this is a new call
+ * @param src_id     source of the caller
+ * @param call_id    call-id
+ * @param line       line number
+ * @param feature    feature which is waiting on hodling call.
+ * @param feature data
+ *
+ * @return  TRUE if actions require delay before the new call may begin
+ *          FALSE if no actions are required an the new call may begin
+ *
+ * @pre     (wait, wait2, wait3 all FALSE)
+ */
+static boolean
+fsmdef_wait_to_start_new_call (boolean is_newcall, cc_srcs_t src_id, callid_t call_id,
+                               line_t line, cc_features_t feature,
+                               cc_feature_data_t *data)
+{
+    boolean wait  = FALSE;
+    boolean wait2 = FALSE;
+    boolean wait3 = FALSE;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsmdef_find_and_hold_connected_call(call_id, &wait, src_id);
+
+    fsmdef_find_and_handle_ring_connecting_releasing_calls(call_id, &wait2);
+
+    fsmdef_clear_preserved_calls(&wait3);
+
+    /*
+     * Requeue the message because we need to wait for call actions to complete
+     */
+    if ((wait) || (wait2) || (wait3)) {
+        cc_int_feature(src_id, CC_SRC_GSM, call_id, line, feature, data);
+    }
+
+    return (wait | wait2 | wait3);
+}
+
+static cc_causes_t
+fsmdef_get_cause (boolean data_valid, cc_feature_data_t *data)
+{
+    cc_causes_t cause;
+
+    if (data_valid) {
+        cause = data->endcall.cause;
+    } else {
+        cause = CC_CAUSE_NORMAL;
+    }
+
+    return (cause);
+}
+
+
+/**
+ * common function to release a call given cause code
+ * i.e onhooks the given call.
+ *
+ * @param[in] fcb     The pointer to the fsm_fcb_t structure of this
+ *                    call chain.
+ * @param[in] cause   release cause code.
+ *
+ * @param[in] send_release When set to TRUE the function sends release
+ *                    and waits for release complete.
+ *                         When set to FALSE the function cleans up
+ *                    dcb and release fcb.
+ *
+ * @pre               (fcb not_eq NULL)
+ *
+ * @return            sm_rcs_t indicates whether the execution of
+ *                    next statmachine to end (SM_RC_END) or clean up
+ *                    (SM_RC_CLEANUP)
+ *
+ * @Usage Note:       The function uses send_release flag to
+ *                    as an indicator for sending release out or not.
+ *                    If release is sent, the function transitions fsmdef's
+ *                    state to FSMDEF_S_RELEASING and
+ *                    return the SM_RC_END to waite for release complete.
+ *
+ *                    Otherwise, it cleans up dcb and releases fcb and
+ *                    return SM_RC_CLEANUP to the caller.
+ *
+ *                    If the SM_RC_CLEANUP is returned, the caller should
+ *                    terminate any access or perform any operation
+ *                    afterward.
+ */
+sm_rcs_t
+fsmdef_release (fsm_fcb_t *fcb, cc_causes_t cause, boolean send_release)
+{
+    fsmdef_dcb_t   *dcb = fcb->dcb;
+    cc_state_data_t state_data;
+    cc_kfact_t      kfactor;
+    fsmdef_media_t *media;
+    char tmp_str[STATUS_LINE_MAX_LEN];
+
+    if (!dcb) {
+      /* Already been released */
+      return SM_RC_CLEANUP;
+    }
+
+    FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Entered. cause= %s\n",
+               DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, __FUNCTION__), cc_cause_name(cause));
+
+    if (g_dock_undock_event != MEDIA_INTERFACE_UPDATE_NOT_REQUIRED) {
+        ui_update_media_interface_change(dcb->line, dcb->call_id, MEDIA_INTERFACE_UPDATE_FAIL);
+    }
+    memset(&kfactor, 0, sizeof(cc_kfact_t));
+    /* Cancel any existing autoanswer timer */
+    (void) cprCancelTimer(dcb->autoAnswerTimer);
+
+
+    /*
+     * Let Dialog Manager know that there is ONHOOK event
+     */
+    fsmdef_notify_hook_event(fcb, CC_MSG_ONHOOK, NULL, CC_NO_CALL_ID,
+                             CC_REASON_NONE, CC_MONITOR_NONE,CFWDALL_NONE);
+
+    media = gsmsdp_find_audio_media(dcb);
+    if ((media) && (media->direction != SDP_DIRECTION_INACTIVE)) {
+        fsmdef_get_rtp_stat(dcb, &kfactor);
+    }
+
+    if ( cause == CC_SIP_CAUSE_ANSWERED_ELSEWHERE ) {
+        ui_log_disposition(dcb->call_id, CC_CALL_LOG_DISP_IGNORE );
+    }
+
+    if ( cause == CC_CAUSE_RESP_TIMEOUT) {
+        if ((platGetPhraseText(STR_INDEX_RESP_TIMEOUT,
+                                (char *) tmp_str,
+                                STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) {
+            lsm_ui_display_status(tmp_str, dcb->line, dcb->call_id);
+        }
+    }
+
+    if (send_release) {
+        cc_int_release(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                       cause, NULL, &kfactor);
+        /*
+         * Wait around for the release_complete.
+         */
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_RELEASING);
+
+        /*
+         * Only move the UI if we changed the UI's state when the call was
+         * received.
+         */
+        if ((dcb->line != LSM_NO_LINE) || (cause != CC_CAUSE_BUSY)) {
+            state_data.onhook.caller_id = dcb->caller_id;
+            state_data.onhook.local = FALSE;
+            state_data.onhook.cause     = CC_CAUSE_NORMAL;
+            cc_call_state(dcb->call_id, dcb->line, CC_STATE_ONHOOK,
+                          &state_data);
+        }
+        return (SM_RC_END);
+    } else {
+        /*
+         * Only move the UI if we changed the UI's state when the call was
+         * initiated.
+         */
+        if ((dcb->line != LSM_NO_LINE) || (cause != CC_CAUSE_BUSY)) {
+            state_data.onhook.caller_id = dcb->caller_id;
+            state_data.onhook.local     = FALSE;
+            state_data.onhook.cause     = CC_CAUSE_NORMAL;
+            cc_call_state(dcb->call_id, dcb->line, CC_STATE_ONHOOK,
+                          &state_data);
+        }
+
+        /*
+         * Only send a release complete to the remote end if they are waiting
+         * for it. This is the case when we have sent a proceeding or we
+         * have received a release.
+         */
+        if (FSM_CHK_FLAGS(dcb->msgs_sent, FSMDEF_MSG_PROCEEDING) ||
+            FSM_CHK_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE)) {
+            cc_int_release_complete(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                                    dcb->line, cause, &kfactor);
+        }
+
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_IDLE);
+        fsmdef_free_dcb(dcb);
+        fsm_release(fcb, __LINE__, cause);
+        /*
+         * fsmdef has been released, indiate cleanup FSM chain.
+         */
+        return (SM_RC_CLEANUP);
+    }
+}
+
+/*
+ * fsmdef_convert_esc_plus
+ *
+ * replaces an escaped "+" (%2B) with a real "+"
+ */
+static void
+fsmdef_convert_esc_plus (const char *src_number)
+{
+    int   i, len;
+    char *number;
+
+    len = strlen(src_number) - 2;
+    number = (char *) src_number;
+    number[0] = '+';
+    for (i = 1; i < len; i++) {
+        number[i] = number[i + 2];
+    }
+    number[i] = '\0';
+}
+
+static boolean
+fsmdef_compare_caller_id_string (string_t dest, string_t src)
+{
+    if ((dest == NULL) && (src == NULL)) {
+        /*
+         * Strings are same.
+         */
+        return (FALSE);
+    }
+
+    if ((dest == NULL) || (src == NULL)) {
+        /*
+         * Strings differ.
+         */
+        return (TRUE);
+    }
+
+    if (strncmp(dest, src, FSMDEF_MAX_CALLER_ID_LEN) != 0) {
+        /*
+         * Strings differ.
+         */
+        return (TRUE);
+    }
+
+    /*
+     * Strings are same.
+     */
+    return (FALSE);
+}
+
+static boolean
+fsmdef_compare_caller_id (cc_caller_id_t *dest_caller_id,
+                          cc_caller_id_t *src_caller_id)
+{
+    if (fsmdef_compare_caller_id_string(dest_caller_id->calling_name,
+                                        src_caller_id->calling_name)) {
+        return (TRUE);
+    }
+
+    if (fsmdef_compare_caller_id_string(dest_caller_id->calling_number,
+                                        src_caller_id->calling_number)) {
+        return (TRUE);
+    }
+
+    if (fsmdef_compare_caller_id_string(dest_caller_id->called_name,
+                                        src_caller_id->called_name)) {
+        return (TRUE);
+    }
+
+    if (fsmdef_compare_caller_id_string(dest_caller_id->called_number,
+                                        src_caller_id->called_number)) {
+        return (TRUE);
+    }
+
+    if (fsmdef_compare_caller_id_string(dest_caller_id->orig_called_name,
+                                        src_caller_id->orig_called_name)) {
+        return (TRUE);
+    }
+
+    if (fsmdef_compare_caller_id_string(dest_caller_id->orig_called_number,
+                                        src_caller_id->orig_called_number)) {
+        return (TRUE);
+    }
+
+    if (fsmdef_compare_caller_id_string(dest_caller_id->last_redirect_name,
+                                        src_caller_id->last_redirect_name)) {
+        return (TRUE);
+    }
+
+    if (fsmdef_compare_caller_id_string(dest_caller_id->last_redirect_number,
+                                        src_caller_id->last_redirect_number)) {
+        return (TRUE);
+    }
+
+    if (fsmdef_compare_caller_id_string(dest_caller_id->orig_rpid_number,
+                                        src_caller_id->orig_rpid_number)) {
+        return (TRUE);
+    }
+
+    if (dest_caller_id->display_calling_number != src_caller_id->display_calling_number ||
+        dest_caller_id->display_called_number  != src_caller_id->display_called_number ||
+        dest_caller_id->call_type              != src_caller_id->call_type ||
+        dest_caller_id->call_instance_id       != src_caller_id->call_instance_id) {
+        return (TRUE);
+    }
+
+    return (FALSE);
+}
+
+static void
+fsmdef_mv_caller_id (fsmdef_dcb_t *dcb, cc_caller_id_t *caller_id)
+{
+    /*
+     * Move the caller ID from the source to the storage in dcb if there
+     * is a change in what is already stored in the dcb.
+     */
+    if (fsmdef_compare_caller_id(&dcb->caller_id, caller_id)) {
+        cc_mv_caller_id(&dcb->caller_id, caller_id);
+        dcb->ui_update_required = TRUE;
+    }
+}
+
+static void
+fsmdef_update_callinfo (fsm_fcb_t *fcb, cc_feature_t *msg)
+{
+    static const char fname[]    = "fsmdef_update_callinfo";
+    fsmdef_dcb_t      *dcb       = fcb->dcb;
+    cc_feature_data_t *feat_data = &(msg->data);
+    cc_action_data_t   action_data;
+    cc_caller_id_t    *caller_id;
+
+    if (msg->data_valid == FALSE) {
+        /* No data to use for update. Just ignore the event. */
+        return;
+    }
+
+    if ((feat_data->call_info.feature_flag & CC_UI_STATE) &&
+        (feat_data->call_info.ui_state == CC_UI_STATE_RINGOUT)) {
+
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
+                     dcb->call_id, dcb->line, fname,
+                     "setting spoof_ringout_requested");
+
+        dcb->spoof_ringout_requested = TRUE;
+    } else {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_RQSTD),
+                     dcb->call_id, dcb->line, fname);
+
+        dcb->spoof_ringout_requested = FALSE;
+    }
+
+    caller_id = &feat_data->call_info.caller_id;
+
+    if (feat_data->call_info.feature_flag & CC_CALLER_ID) {
+        fsmdef_mv_caller_id(dcb, caller_id);
+    }
+
+    /*
+     * If CCM provides a call instance id and it does not match
+     * the current call instance id, free the current call instance
+     * id and set the call instance id to the newly provided value.
+     */
+    if (feat_data->call_info.feature_flag & CC_CALL_INSTANCE &&
+        feat_data->call_info.caller_id.call_instance_id != dcb->caller_id.call_instance_id) {
+        if (dcb->caller_id.call_instance_id != 0) {
+            fsmutil_free_ci_id(dcb->caller_id.call_instance_id, dcb->line);
+        }
+        dcb->caller_id.call_instance_id =
+            feat_data->call_info.caller_id.call_instance_id;
+        fsmutil_set_ci_id(dcb->caller_id.call_instance_id, dcb->line);
+        dcb->ui_update_required = TRUE;
+    }
+
+    /*
+     * Update security status
+     */
+    fsmdef_update_callinfo_security_status(dcb, &feat_data->call_info);
+
+    /*
+     * Update call policy
+     */
+    if (feat_data->call_info.feature_flag & CC_POLICY) {
+        if (dcb->policy != feat_data->call_info.policy) {
+           dcb->policy = feat_data->call_info.policy;
+           dcb->ui_update_required = TRUE;
+       }
+    }
+
+    /*
+     * Save orientation so that UI call update can be done at
+     * any time that UI needs to be updated.
+     */
+    if (feat_data->call_info.feature_flag & CC_ORIENTATION) {
+        if (dcb->orientation != feat_data->call_info.orientation) {
+            dcb->orientation = feat_data->call_info.orientation;
+            dcb->ui_update_required = TRUE;
+        }
+    }
+
+    /*
+     * This call info. event may be as part of media effecting
+     * signaling event (such as INVITE, re-INVITE, 180 etc.)
+     * which will be followed by actual media effected event. It is
+     * indicated by SIP stack to improve media cutting through as soonest.
+     * If SIP indicates that UI can be delayed then do not update call
+     * information now. The UI will be updated as part of media
+     * manipulation event that will follow.
+     * (Note: call info event is sent separately from the SIP signaling
+     *  event currently and it is sent before SIP signaling event).
+     */
+    if (feat_data->call_info.feature_flag & CC_DELAY_UI_UPDATE) {
+        /* Delay UI update */
+    } else {
+        /*
+         * Only perform a UI update if something changed.
+         */
+        if (dcb->ui_update_required == TRUE
+           || dcb->spoof_ringout_requested == TRUE) {
+
+            action_data.update_ui.action = CC_UPDATE_CALLER_INFO;
+            action_data.update_ui.data.caller_info = feat_data->call_info;
+
+            (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_UPDATE_UI,
+                                 &action_data);
+
+            FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id,
+                         dcb->line, fname, "UI update");
+        } else {
+            FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
+                         dcb->call_id, dcb->line, fname, "No UI update");
+        }
+    }
+    /* update callref */
+    if ( dcb->callref == 0 ) {
+        dcb->callref = feat_data->call_info.callref;
+        ui_update_callref(dcb->line, dcb->call_id, feat_data->call_info.callref);
+    }
+    /* update gcid */
+    if (feat_data->call_info.global_call_id[0] != '\0') {
+        ui_update_gcid(dcb->line, dcb->call_id, feat_data->call_info.global_call_id);
+        /*
+         * store the gcid lcb, this is used to prevent short ringing
+         * in scenarios where a call is routed to the calling phone.
+         */
+        lsm_update_gcid(dcb->call_id, feat_data->call_info.global_call_id);
+    }
+}
+
+/**
+ *  Function: fsmdef_set_feature_timer
+ *
+ *  Description: This function is called to set (start) timer for a
+ *    given timer. The context of the timer is set so that upon
+ *    expiration the corresponding context (dcb) can be obtained.
+ *
+ *  Parameters:
+ *    dcb      - pointer to the fsmdef_dcb_t. The caller must ensure
+ *               dcb is not NULL.
+ *    timer    - pointer to cprTimer_t. The caller must ensure that
+ *               timer is not NULL.
+ *    duration - the time duration for the timer to be set.
+ *
+ *  Returns:
+ *    N/A.
+ */
+static void
+fsmdef_set_feature_timer (fsmdef_dcb_t *dcb, cprTimer_t *timer,
+                          uint32_t duration)
+{
+    static const char fname[] = "fsmdef_set_feature_timer";
+
+    if (cprCancelTimer(*timer) != CPR_SUCCESS) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CANCEL_FAILED),
+                     dcb->call_id, dcb->line, fname, "Feature", cpr_errno);
+
+        return;
+    }
+
+    if (cprStartTimer(*timer, duration, (void *)(long)dcb->call_id) == CPR_FAILURE) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_START_FAILED),
+                     dcb->call_id, dcb->line, fname, "Feature", cpr_errno);
+    }
+}
+
+static void
+fsmdef_set_req_pending_timer (fsmdef_dcb_t *dcb)
+{
+    static const char fname[] = "fsmdef_set_req_pending_timer";
+    uint32_t msec;
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_INVALID_DCB), fname);
+        return;
+    }
+
+    if (!dcb->req_pending_tmr) {
+        dcb->req_pending_tmr = cprCreateTimer("Request Pending",
+                                              GSM_REQ_PENDING_TIMER,
+                                              TIMER_EXPIRATION,
+                                              gsm_msg_queue);
+
+        if (dcb->req_pending_tmr == NULL) {
+            FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
+                         dcb->call_id, dcb->line, fname, "Request Pending");
+
+            return;
+        }
+    }
+
+    if (dcb->inbound) {
+        // We did not initiate this call, so set timer between 0 and 2000ms
+        msec = abs(cpr_rand()) % 2000;
+    } else {
+        // We initiated this call, so set the timer between 2100 and 4000ms
+        msec = abs(cpr_rand()) % 1900 + 2100;
+    }
+
+    FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Starting req pending timer for %d ms.\n",
+               DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname), msec);
+
+    fsmdef_set_feature_timer(dcb, &dcb->req_pending_tmr, msec);
+}
+
+static void
+fsmdef_set_ringback_delay_timer (fsmdef_dcb_t *dcb)
+{
+    static const char fname[] = "fsmdef_set_ringback_delay_timer";
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_INVALID_DCB), fname);
+        return;
+    }
+
+    FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Starting Ringback Delay timer"
+                 " for %d ms.\n",
+                 DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname), RINGBACK_DELAY);
+
+    fsmdef_set_feature_timer(dcb, &dcb->ringback_delay_tmr, RINGBACK_DELAY);
+}
+
+/*******************************************************************
+ * event functions
+ */
+static sm_rcs_t
+fsmdef_ev_default (sm_event_t *event)
+{
+    fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
+
+    FSM_DEBUG_SM(get_debug_string(FSM_DBG_SM_DEFAULT_EVENT));
+    if (fcb->dcb) {
+        cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                      NULL);
+    }
+    return (SM_RC_END);
+}
+
+/*
+ * Default event handler for feature_ack event
+ */
+static sm_rcs_t
+fsmdef_ev_default_feature_ack (sm_event_t *event)
+{
+  static const char fname[] = "fsmdef_ev_default_feature_ack";
+    fsm_fcb_t        *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t     *dcb    = fcb->dcb;
+    cc_feature_ack_t *msg    = (cc_feature_ack_t *) event->msg;
+    cc_features_t     ftr_id = msg->feature_id;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, "fsmdef_ev_default_feature_ack"));
+
+    if (ftr_id == CC_FEATURE_SELECT) {
+        /* Reeceived the response for select, so turn the flag off */
+        dcb->select_pending = FALSE;
+        if (dcb->selected) {
+            dcb->selected = FALSE;
+            g_numofselected_calls--;
+            FSM_DEBUG_SM(DEB_L_C_F_PREFIX"call is unselected and number of selected \
+                          calls on the phone is %d\n",
+                                                 DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, msg->call_id, fname),
+                          g_numofselected_calls);
+
+        } else {
+            dcb->selected = TRUE;
+            if ((g_b2bjoin_pending == FALSE) &&
+                (dcb->active_feature == CC_FEATURE_B2B_JOIN)) {
+                g_b2bjoin_pending = TRUE;
+                g_b2bjoin_callid  = dcb->call_id;
+            }
+            g_numofselected_calls++;
+            FSM_DEBUG_SM(DEB_L_C_F_PREFIX"call is selected and number of selected \
+                          calls on the phone is %d\n",
+                                                 DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname),
+                          g_numofselected_calls);
+        }
+        ui_call_selected(dcb->line, lsm_get_ui_id(dcb->call_id), (dcb->selected)?CC_DIALOG_LOCKED:CC_DIALOG_UNLOCKED);
+
+    } else if (dcb->active_feature != ftr_id) {
+        // check if we are getting feature_ack for the active feature
+        FSM_DEBUG_SM(DEB_L_C_F_PREFIX"feature_ack rcvd for %s but %s is active\n",
+                     DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname),
+                                        cc_feature_name(ftr_id), cc_feature_name(dcb->active_feature));
+
+    }
+
+    // reset active feature
+    dcb->active_feature = CC_FEATURE_NONE;
+
+    return (SM_RC_END);
+}
+
+
+static void
+fsmdef_sm_ignore_ftr (fsm_fcb_t *fcb, int fname, cc_features_t ftr_id)
+{
+    fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+    if (fcb->dcb) {
+        cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                      NULL);
+    }
+}
+
+static void
+fsmdef_sm_ignore_src (fsm_fcb_t *fcb, int fname, cc_srcs_t src_id)
+{
+    fsm_sm_ignore_src(fcb, __LINE__, src_id);
+
+    if (fcb->dcb) {
+        cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                      NULL);
+    }
+}
+
+/*
+ * fsmdef_error_onhook_timeout
+ *
+ * Timer is started immediately after the call error. This function is called
+ * when there is a timeout event generated by the timer .
+ *
+ * @param[in] data    The gsm ID (callid_t) of the call onhook timeout
+ *                    has occured.
+ *
+ * @return            N/A
+ */
+void
+fsmdef_error_onhook_timeout (void *data)
+{
+    static const char fname[] = "fsmdef_error_onhook_timeout";
+    fsmdef_dcb_t *dcb;
+    callid_t   call_id;
+
+    call_id = (callid_t)(long)data;
+    if (call_id == CC_NO_CALL_ID) {
+        /* Invalid call id */
+        GSM_ERR_MSG(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "invalid data");
+        return;
+    }
+
+    /* Retrieve dcb from call id */
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+    if (dcb == NULL) {
+        GSM_ERR_MSG(get_debug_string(FSMDEF_DBG_INVALID_DCB), fname);
+
+        return;
+    }
+
+    FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
+                 dcb->call_id, dcb->line, fname, "timeout");
+
+    cc_int_onhook(CC_SRC_GSM, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE,
+                  dcb->call_id, dcb->line, FALSE, FALSE);
+}
+
+/**
+ *  Function: fsmdef_feature_timer_timeout
+ *
+ *  Description: This function is called when receives time out
+ *    notification. The function then converts the timer event
+ *    into the CCAPI event suitable for GSM's call state machine.
+ *
+ *  Parameters:
+ *    feature_id - corresponding feature ID of the timer event.
+ *    data       - the opaque data for the caller which is actually
+ *                 is the GSM's call id.
+ *
+ *  Returns:
+ *    NULL or
+ *    pointer to the cc_feature_t.
+ */
+void *
+fsmdef_feature_timer_timeout (cc_features_t feature_id, void *data)
+{
+    static const char fname[] = "fsmdef_feature_timer_timeout";
+    cc_feature_t     *pmsg;
+    callid_t         call_id;
+    fsmdef_dcb_t     *dcb;
+
+    FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "timeout");
+
+    call_id = (callid_t)(long)data;
+    if (call_id == CC_NO_CALL_ID) {
+        /* Invalid call id */
+        GSM_ERR_MSG(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "invalid data");
+        return NULL;
+    }
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+    if (dcb == NULL) {
+        /* The corresponding dcb for the call ID is not found */
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_INVALID_DCB), fname);
+        return (NULL);
+    }
+
+    if (dcb->inband_received && feature_id == CC_FEATURE_RINGBACK_DELAY_TIMER_EXP) {
+        /* Double check if inbound ringback indication is received*/
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "inband received!");
+        return (NULL);
+    }
+
+    pmsg = (cc_feature_t *) gsm_get_buffer(sizeof(*pmsg));
+    if (!pmsg) {
+        GSM_ERR_MSG(get_debug_string(FSMDEF_DBG1),
+                    call_id, dcb->line, fname,
+                    "failed to allocate feature timer message");
+        return NULL;
+    }
+
+    memset(pmsg, 0, sizeof(*pmsg));
+
+    pmsg->msg_id     = CC_MSG_FEATURE;
+    pmsg->src_id     = CC_SRC_GSM;
+    pmsg->call_id    = call_id;
+    pmsg->line       = dcb->line;
+    pmsg->feature_id = feature_id;
+    pmsg->data_valid = FALSE;
+
+    return (void *) pmsg;
+}
+
+/**
+ *
+ * Function handles idle setup request received from the network
+ *
+ * @sm_eent_t   event
+ *
+ * @return  SM_RC_END
+ *
+ * @pre     (called_number and  called_number not NULL)
+ * @pre     (event->data not_eq NULL)
+ * @pre     (event->msg not_eq NULL)
+ */
+static sm_rcs_t
+fsmdef_ev_idle_setup (sm_event_t *event)
+{
+    static const char fname[] = "fsmdef_ev_idle_setup";
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_setup_t       *msg     = (cc_setup_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    int               temp;
+    string_t          called_number  = msg->caller_id.called_number;
+    string_t          calling_number = msg->caller_id.calling_number;
+    fsmdef_dcb_t     *dcb;
+    cc_causes_t       cause;
+    fsmxfr_xcb_t     *xcb;
+    fsm_fcb_t        *other_fcb;
+    callid_t          other_call_id;
+    boolean           alerting = TRUE;
+    boolean           replaces = msg->replaces;
+    int               other_active_calls;
+    boolean           transfer_target = FALSE;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /*
+     * Make sure we have a valid called_number.
+     */
+    if ((called_number == NULL) || (called_number[0] == '\0')) {
+        return (SM_RC_CLEANUP);
+    }
+
+    /*
+     * Check the called/calling number for the E.164 escaped "+"
+     * and convert it to a real "+" if present.
+     */
+    if (cpr_strncasecmp(called_number, "%2B", 3) == 0) {
+        fsmdef_convert_esc_plus(called_number);
+    }
+    if (cpr_strncasecmp(calling_number, "%2B", 3) == 0) {
+        fsmdef_convert_esc_plus(calling_number);
+    }
+
+    FSM_DEBUG_SM(DEB_L_C_F_PREFIX"called_number= %s calling_number= %s\n",
+               DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, msg->call_id, fname),
+               msg->caller_id.called_number, msg->caller_id.calling_number);
+
+    //idle = lsm_is_phone_idle();
+
+    xcb = fsmxfr_get_xcb_by_call_id(call_id);
+    if (xcb && replaces) {
+        transfer_target = TRUE;
+    }
+
+    /*
+     * Get a new incoming call context.
+     * if we the target of a transfer, request that the line
+     * availability be increased by one to account for the third
+     * instance of a line to become available to allow completion
+     * of transfer.
+     */
+    cause = fsm_get_new_incoming_call_context(call_id, fcb, called_number,
+                                              transfer_target);
+    dcb = fcb->dcb;
+    if ((msg->call_info.type != CC_FEAT_MONITOR) &&
+        (replaces != TRUE)) {
+        if (lsm_is_line_available(dcb->line, TRUE) == FALSE) {
+            /* increment it to compensate for decrementing while ending the call. */
+            lsm_increment_call_chn_cnt(dcb->line);
+            fsmdef_end_call(dcb, CC_CAUSE_BUSY);
+            return (SM_RC_END);
+        }
+        lsm_increment_call_chn_cnt(dcb->line);
+    }
+    else {
+        /*
+         * join calls (barged, M & R) are not counted by CUCM. so we should not count either
+         */
+        dcb->call_not_counted_in_mnc_bt = TRUE;
+        dcb->join_call_id = msg->call_info.data.join.join_call_id;
+    }
+    /*
+     * Set default orientation for the incoming setup as "from" to
+     * avoid updating UI when call info is received in "ACK" which
+     * shoule be "from" for typicall incoming call.
+     */
+    dcb->orientation = CC_ORIENTATION_FROM;
+
+    switch (cause) {
+    case CC_CAUSE_OK:
+        break;
+
+    case CC_CAUSE_NO_RESOURCE:
+        cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                      NULL);
+        return (SM_RC_CLEANUP);
+
+    default:
+        fsmdef_end_call(dcb, cause);
+        return (SM_RC_END);
+    }
+
+    /*
+     * Check for Anonymous call blocking. If low bit is set,
+     * then do not allow call. Note that we must allow both upper and lowercase
+     * ANON strings, hence the use of strcasestr
+     */
+    config_get_value(CFGID_ANONYMOUS_CALL_BLOCK, &temp, sizeof(temp));
+    if (temp & 1) {
+        /*
+         * We compare the calling name to the hardcoded Anonymous string we use in
+         * our SIP headers. This handles the case where calling name was pulled from
+         * the From header and was set to Anonymous. We also compare the calling name
+         * to the localized string index for Private which is what the calling name will
+         * be set to if an RPID header was received.
+         */
+        char tmp_str[STATUS_LINE_MAX_LEN];
+        sstrncpy(tmp_str, platform_get_phrase_index_str(UI_PRIVATE), sizeof(tmp_str));
+        if (strcasestr(msg->caller_id.calling_name, SIP_HEADER_ANONYMOUS_STR) ||
+            strcasestr(msg->caller_id.calling_name, tmp_str)) {
+            fsmdef_end_call(dcb, CC_CAUSE_ANONYMOUS);
+            return (SM_RC_END);
+        }
+    }
+
+    /*
+     * Check if Call Waiting is disabled.
+     * If call-waiting is disabled and there is another call active
+     * then the GSM will return busy.
+     * unless this is a barge/monitor target call
+     *
+     */
+    config_get_line_value(CFGID_LINE_CALL_WAITING, &temp, sizeof(temp),
+                          dcb->line);
+    other_active_calls = fsmdef_get_active_call_cnt(call_id);
+
+    if ((msg->call_info.type != CC_FEAT_MONITOR)) {
+        if ((!(temp & 1)) && (other_active_calls > 0) &&
+            (!((xcb != NULL) && (xcb->mode == FSMXFR_MODE_TARGET)))) {
+
+            fsmdef_end_call(dcb, CC_CAUSE_BUSY);
+
+            return (SM_RC_END);
+        }
+    }
+
+    /*
+     * If this is a call to replace another call,
+     * check that we have a call to replace
+     */
+    other_call_id = fsmxfr_get_other_call_id(xcb, call_id);
+    if (replaces) {
+        if ((xcb == NULL) || (other_call_id == CC_NO_CALL_ID)) {
+            FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
+                         dcb->call_id, dcb->line, "",
+                         "No call to replace");
+
+            fsmdef_end_call(dcb, CC_CAUSE_NO_REPLACE_CALL);
+            return (SM_RC_END);
+        }
+    }
+
+
+    /*
+     * The called name and number will not be obtained from
+     * the setup. Remove it before getting from the setup message.
+     */
+    if (msg->caller_id.called_name != NULL) {
+        strlib_free(msg->caller_id.called_name);
+        msg->caller_id.called_name = NULL;
+    }
+    if (msg->caller_id.called_number != NULL) {
+        strlib_free(msg->caller_id.called_number);
+        msg->caller_id.called_number = NULL;
+    }
+    /* Get the caller ID from the setup message */
+    fsmdef_mv_caller_id(dcb, &msg->caller_id);
+
+    if (msg->caller_id.call_type == CC_CALL_FORWARDED) {
+
+        dcb->call_type = FSMDEF_CALL_TYPE_FORWARD;
+    }
+
+    /*
+     * If a call instance id is provided, use it in the dcb. We will
+     * check to see that the call instance id provided differs from
+     * what is currently stored in the dcb before assigning it.
+     */
+    if (msg->call_info.type == CC_FEAT_CALLINFO) {
+        cc_feature_data_call_info_t *data;
+
+        data = &msg->call_info.data.call_info_feat_data;
+        if (data->feature_flag & CC_CALL_INSTANCE) {
+            if (data->caller_id.call_instance_id != 0 &&
+                data->caller_id.call_instance_id !=
+                               dcb->caller_id.call_instance_id) {
+                if (dcb->caller_id.call_instance_id != 0) {
+                    fsmutil_free_ci_id(dcb->caller_id.call_instance_id,
+                                       dcb->line);
+                }
+                dcb->caller_id.call_instance_id =
+                    data->caller_id.call_instance_id;
+                fsmutil_set_ci_id(dcb->caller_id.call_instance_id, dcb->line);
+            }
+        }
+
+        if (data->feature_flag & CC_SECURITY) {
+            FSM_SET_SECURITY_STATUS(dcb, data->security);
+        }
+
+        if (data->feature_flag & CC_POLICY) {
+            FSM_SET_POLICY(dcb, data->policy);
+        }
+    }
+    dcb->alert_info = msg->alert_info;
+    dcb->alerting_ring = msg->alerting_ring;
+    dcb->alerting_tone = msg->alerting_tone;
+
+    /*
+     * This is an incoming call, so we know we will need to send a
+     * RELEASE when we clear the call.
+     */
+    dcb->send_release = TRUE;
+
+    cause = gsmsdp_negotiate_offer_sdp(fcb, &msg->msg_body, TRUE);
+    if (cause != CC_CAUSE_OK) {
+        return (fsmdef_release(fcb, cause, dcb->send_release));
+    }
+
+    if (transfer_target) {
+        /*
+         * Send a proceeding event to the transfer call.
+         *
+         * The proceeding event will end the other call
+         * and answer the current call.
+         *
+         */
+        cc_int_proceeding(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                          dcb->line, &(dcb->caller_id));
+    }
+
+    cc_int_setup_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                     &(dcb->caller_id), NULL);
+
+    FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_SETUP_ACK);
+
+
+
+    cc_int_proceeding(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                          &(dcb->caller_id));
+
+    FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_PROCEEDING);
+
+
+    alerting = fsmdef_extract_join_target(event);
+
+    /*
+     * This might be the transfer call from the transferee to the target.
+     * In such a case we want this new call to match the state of the call
+     * that it is replacing.
+     */
+    if (xcb != NULL) {
+        other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                    FSM_TYPE_DEF);
+        if (other_fcb && (other_fcb->old_state == FSMDEF_S_CONNECTED ||
+            other_fcb->old_state == FSMDEF_S_CONNECTED_MEDIA_PEND ||
+            other_fcb->old_state == FSMDEF_S_RESUME_PENDING ||
+            other_fcb->state == FSMDEF_S_CONNECTED ||
+            other_fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND ||
+            other_fcb->state == FSMDEF_S_RESUME_PENDING)) {
+            alerting = FALSE;
+        }
+    }
+
+
+    if (alerting == TRUE) {
+        /*
+         * set the call priority in lcb. This is used by ringer logic.
+         */
+        if ((msg->call_info.type == CC_FEAT_CALLINFO) &&
+            (msg->call_info.data.call_info_feat_data.priority == CC_CALL_PRIORITY_URGENT)) {
+            lsm_set_lcb_call_priority(call_id);
+        }
+        if ((msg->call_info.type == CC_FEAT_CALLINFO) &&
+            (msg->call_info.data.call_info_feat_data.dusting == TRUE)) {
+            lsm_set_lcb_dusting_call(call_id);
+        }
+
+        cc_call_state(dcb->call_id, dcb->line, CC_STATE_ALERTING,
+                      FSMDEF_CC_CALLER_ID);
+        /*
+         * Currently we do not send SDP in the 180 response.
+         */
+        cc_int_alerting(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                        &(dcb->caller_id), NULL, FALSE);
+
+       /* update callref */
+       if ( dcb->callref == 0 ) {
+           dcb->callref = msg->call_info.data.call_info_feat_data.callref;
+           ui_update_callref(dcb->line, dcb->call_id, msg->call_info.data.call_info_feat_data.callref);
+       }
+       /* update gcid */
+       ui_update_gcid(dcb->line, dcb->call_id, msg->call_info.data.call_info_feat_data.global_call_id);
+       /*
+        * store the gcid lcb, this is used to prevent short ringing
+        * in scenarios where a call is routed to the calling phone.
+        */
+       lsm_update_gcid(dcb->call_id, msg->call_info.data.call_info_feat_data.global_call_id);
+    }
+
+    ui_cc_capability(dcb->line, lsm_get_ui_id(dcb->call_id), msg->recv_info_list);
+
+    FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_ALERTING);
+
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_INCOMING_ALERTING);
+
+    return (SM_RC_END);
+}
+
+
+sm_rcs_t
+fsmdef_dialstring (fsm_fcb_t *fcb, const char *dialstring,
+                   cc_redirect_t *redirect, boolean replace,
+                   cc_call_info_t *call_info)
+{
+    static const char fname[] = "fsmdef_dialstring";
+    fsmdef_dcb_t     *dcb = fcb->dcb;
+    cc_causes_t       cause;
+    cc_msgbody_info_t msg_body;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (dialstring) {
+        if (strlen(dialstring) > MAX_SIP_URL_LENGTH) {
+            FSM_DEBUG_SM(DEB_F_PREFIX"Dial string too long\n", DEB_F_PREFIX_ARGS(FSM, fname));
+            /* Force clean up call without sending release */
+            return (fsmdef_release(fcb, CC_CAUSE_INVALID_NUMBER, FALSE));
+        }
+    }
+
+    /*
+     * If there is active feature which is waiting for digit collection
+     * then use service URI preceded with dialed number.
+     */
+    switch (dcb->active_feature) {
+
+    case CC_FEATURE_CFWD_ALL:
+        fsmdef_append_dialstring_to_feature_uri(dcb, dialstring);
+        break;
+
+    default:
+        if (dialstring) {
+            dcb->caller_id.called_number =
+                strlib_update(dcb->caller_id.called_number, dialstring);
+        }
+        break;
+    }
+
+    cause = gsmsdp_create_local_sdp(dcb, FALSE, TRUE, TRUE, TRUE, TRUE);
+    if (cause != CC_CAUSE_OK) {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        /* Force clean up call without sending release */
+        return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    /* Build SDP for sending out */
+    cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
+    if (cause != CC_CAUSE_OK) {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        /* Force clean up call without sending release */
+        return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    /*
+     * Since we are sending setup to UI we will also have to send
+     * release to it to for sip stack to clean up the call
+     */
+    dcb->send_release = TRUE;
+
+    /*
+     * lsm_parse_displaystr will free present called number and return
+     * pointer to parsed called number
+     */
+    dcb->caller_id.called_number =
+        lsm_parse_displaystr(dcb->caller_id.called_number);
+
+    /* set default orientation for outgoing call */
+    dcb->orientation = CC_ORIENTATION_TO;
+    dcb->inbound = FALSE;
+
+    /*
+     * Invoke cc_call_state with modified caller_id for features such as
+     * pickups
+     */
+    fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_DIALING_COMPLETED, CC_CAUSE_MIN);
+
+    FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_SETUP);
+
+    fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_CALL_SENT, CC_CAUSE_MIN);
+
+    /*
+     * Send setup or INVITE out after finishing all UI activities and media
+     * preparation. This is done as the final step in order to minimize
+     * UI activities/media activities to run concurrently while processing
+     * the response from the network. On the platform that uses Java VM to
+     * support UI and media, the time to process the UI and media activities
+     * may take longer than the network response time to the INVITE (such as
+     * voice mail case) and JNI calls may be blocked while invoking the
+     * UI or media calls in the LSM.
+     */
+    cc_int_setup(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                 &(dcb->caller_id), dcb->alert_info, VCM_INSIDE_RING,
+                 VCM_INSIDE_DIAL_TONE, redirect, call_info, replace, NULL, &msg_body);
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_CALL_SENT);
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_dialstring (sm_event_t *event)
+{
+    sm_rcs_t      sm_rc;
+    fsm_fcb_t    *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t *dcb = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    // handle dialstring event if from callfwdall
+    if (fsmdef_process_dialstring_for_callfwd(event) == SM_RC_END) {
+        // release the call started to collect callfwd info
+        dcb->send_release = FALSE;
+        return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
+    }
+
+    sm_rc = fsmdef_dialstring(fcb, ((cc_dialstring_t *)event->msg)->dialstring,
+                              NULL, FALSE, NULL);
+
+    return (sm_rc);
+}
+
+
+/**
+ * fsmdef_ev_createoffer
+ *
+ * Generates Offer SDP
+ *
+ */
+static sm_rcs_t
+fsmdef_ev_createoffer (sm_event_t *event) {
+    sm_rcs_t            sm_rc;
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    cc_msgbody_info_t   msg_body;
+    cc_feature_t        *msg = (cc_feature_t *) event->msg;
+    line_t              line = msg->line;
+    callid_t            call_id = msg->call_id;
+    cc_causes_t         lsm_rc;
+    int                 sdpmode = 0;
+    char                *ufrag = NULL;
+    char                *ice_pwd = NULL;
+    short               vcm_res;
+    session_data_t      *sess_data_p = NULL;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (!sdpmode) {
+      /* Force clean up call without sending release */
+      return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    if (dcb == NULL) {
+      FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+      return SM_RC_CLEANUP;
+    }
+
+   if (msg->data.session.has_constraints) {
+        sess_data_p = (session_data_t *)findhash(msg->data.session.sessionid);
+        if (sess_data_p) {
+            gsmsdp_process_cap_constraints(dcb, sess_data_p->cc_constraints);
+
+            if (0 > delhash(msg->data.session.sessionid)) {
+                FSM_DEBUG_SM (DEB_F_PREFIX"failed to delete hash sessid=0x%08x\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, __FUNCTION__), msg->data.session.sessionid);
+            }
+            cpr_free(sess_data_p);
+        }
+    }
+
+    vcmGetIceParams(dcb->peerconnection, &ufrag, &ice_pwd);
+    if (!ufrag || !ice_pwd) {
+      ui_create_offer(evCreateOfferError, line, call_id, dcb->caller_id.call_instance_id, NULL);
+      return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    dcb->ice_ufrag = (char *)cpr_malloc(strlen(ufrag) + 1);
+    if (!dcb->ice_ufrag)
+       return SM_RC_END;
+
+    sstrncpy(dcb->ice_ufrag, ufrag, strlen(ufrag) + 1);
+    free(ufrag);
+
+
+    dcb->ice_pwd = (char *)cpr_malloc(strlen(ice_pwd) + 1);
+    if (!dcb->ice_pwd)
+       return SM_RC_END;
+
+    sstrncpy(dcb->ice_pwd, ice_pwd, strlen(ice_pwd) + 1);
+    free(ice_pwd);
+
+    vcm_res = vcmGetDtlsIdentity(dcb->peerconnection,
+                    dcb->digest_alg, FSMDEF_MAX_DIGEST_ALG_LEN,
+                    dcb->digest, FSMDEF_MAX_DIGEST_LEN);
+
+    if (vcm_res) {
+       FSM_DEBUG_SM(DEB_F_PREFIX"vcmGetDtlsIdentity returned an error\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_END;
+    }
+
+    cause = gsmsdp_create_local_sdp(dcb, FALSE, TRUE, TRUE, TRUE, TRUE);
+    if (cause != CC_CAUSE_OK) {
+        ui_create_offer(evCreateOfferError, line, call_id, dcb->caller_id.call_instance_id, NULL);
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
+    if (cause != CC_CAUSE_OK) {
+        ui_create_offer(evCreateOfferError, line, call_id, dcb->caller_id.call_instance_id, NULL);
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    /* Pass offer SDP back to UI */
+    ui_create_offer(evCreateOffer, line, call_id, dcb->caller_id.call_instance_id, msg_body.parts[0].body);
+
+    return (SM_RC_END);
+}
+
+
+/**
+ * fsmdef_ev_createanswer
+ *
+ * Generates Answer SDP
+ *
+ */
+static sm_rcs_t
+fsmdef_ev_createanswer (sm_event_t *event) {
+    sm_rcs_t            sm_rc;
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_feature_t        *msg = (cc_feature_t *) event->msg;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    cc_msgbody_info_t   msg_body;
+    line_t              line = msg->line;
+    callid_t            call_id = msg->call_id;
+    line_t              free_line;
+    int                 sdpmode = 0;
+    const char          *called_number = "1234";
+    cc_causes_t         lsm_rc;
+    cc_msgbody_t        *part;
+    uint32_t            body_length;
+    char                *ufrag = NULL;
+    char                *ice_pwd = NULL;
+    short               vcm_res;
+    session_data_t      *sess_data_p;
+    boolean             has_audio;
+    boolean             has_video;
+    boolean             has_data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (!sdpmode) {
+        return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_CLEANUP;
+    }
+
+    if (msg->data.session.has_constraints) {
+        sess_data_p = (session_data_t *)findhash(msg->data.session.sessionid);
+        if (sess_data_p) {
+            gsmsdp_process_cap_constraints(dcb, sess_data_p->cc_constraints);
+
+            if (0 > delhash(msg->data.session.sessionid)) {
+                FSM_DEBUG_SM (DEB_F_PREFIX"failed to delete hash sessid=0x%08x\n",
+                DEB_F_PREFIX_ARGS(SIP_CC_PROV, __FUNCTION__), msg->data.session.sessionid);
+            }
+            cpr_free(sess_data_p);
+        }
+    }
+
+    vcmGetIceParams(dcb->peerconnection, &ufrag, &ice_pwd);
+    if (!ufrag || !ice_pwd) {
+      ui_create_offer(evCreateAnswerError, line, call_id, dcb->caller_id.call_instance_id, NULL);
+      return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    dcb->ice_ufrag = (char *)cpr_malloc(strlen(ufrag) + 1);
+    if (!dcb->ice_ufrag)
+        return SM_RC_END;
+
+    sstrncpy(dcb->ice_ufrag, ufrag, strlen(ufrag) + 1);
+    free(ufrag);
+
+
+    dcb->ice_pwd = (char *)cpr_malloc(strlen(ice_pwd) + 1);
+    if (!dcb->ice_pwd)
+        return SM_RC_END;
+
+    sstrncpy(dcb->ice_pwd, ice_pwd, strlen(ice_pwd) + 1);
+    free(ice_pwd);
+
+    vcm_res = vcmGetDtlsIdentity(dcb->peerconnection,
+                    dcb->digest_alg, FSMDEF_MAX_DIGEST_ALG_LEN,
+                    dcb->digest, FSMDEF_MAX_DIGEST_LEN);
+
+    if (vcm_res) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"vcmGetDtlsIdentity returned an error\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_END;
+    }
+
+    /*
+     * Determine what media types are offered, used to create matching local SDP
+     * for negotiation.
+     */
+    gsmsdp_get_offered_media_types(fcb, dcb->sdp, &has_audio, &has_video, &has_data);
+
+    /*
+     * The sdp member of the dcb has local and remote sdp
+     * this next function fills in the local part
+     */
+    cause = gsmsdp_create_local_sdp(dcb, FALSE, has_audio, has_video, has_data, FALSE);
+    if (cause != CC_CAUSE_OK) {
+        ui_create_answer(evCreateAnswerError, line, call_id, dcb->caller_id.call_instance_id, NULL);
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        // Force clean up call without sending release
+        return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    /* TODO(ekr@rtfm.com): The second true is because we are acting as if we are
+       processing an offer. The first, however, is for an initial offer and we may
+       want to set that conditionally. */
+    cause = gsmsdp_negotiate_media_lines(fcb, dcb->sdp, TRUE, TRUE, FALSE, TRUE);
+
+    if (cause != CC_CAUSE_OK) {
+        ui_create_answer(evCreateAnswerError, line, call_id, dcb->caller_id.call_instance_id, NULL);
+        return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
+    if (cause != CC_CAUSE_OK) {
+        ui_create_answer(evCreateAnswerError, line, call_id, dcb->caller_id.call_instance_id, NULL);
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        return (fsmdef_release(fcb, cause, FALSE));
+    }
+
+    /* Pass SDP back to UI */
+    ui_create_answer(evCreateAnswer, line, call_id, dcb->caller_id.call_instance_id, msg_body.parts[0].body);
+
+    return (SM_RC_END);
+}
+
+
+/**
+ * SetLocalDescription
+ *
+ */
+static sm_rcs_t
+fsmdef_ev_setlocaldesc(sm_event_t *event) {
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_feature_t        *msg = (cc_feature_t *) event->msg;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    cc_msgbody_info_t   msg_body;
+    int                 action = msg->action;
+    string_t            sdp = msg->sdp;
+    int                 sdpmode = 0;
+    callid_t            call_id = msg->call_id;
+    line_t              line = msg->line;
+    cc_causes_t         lsm_rc;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (!sdpmode) {
+        ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SETLOCALDESCERROR);
+        return (SM_RC_END);
+    }
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_CLEANUP;
+    }
+
+    if (JSEP_OFFER == action) {
+        cause = gsmsdp_encode_sdp(dcb->sdp, &msg_body);
+        if (cause != CC_CAUSE_OK) {
+            FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+            ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SETLOCALDESCERROR);
+            return (SM_RC_END);
+        }
+
+        /* compare and fail if different:
+         * anant: Why? The JS should be able to modify the SDP. Commenting out for now (same for answer)
+        if (strcmp(msg_body.parts[0].body, sdp) != 0) {
+            ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SDPCHANGED);
+            return (SM_RC_END);
+        }
+        */
+
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_CALL_SENT);
+
+    } else if (JSEP_ANSWER == action) {
+
+        /* compare SDP generated from CreateAnswer */
+        cause = gsmsdp_encode_sdp(dcb->sdp, &msg_body);
+        if (cause != CC_CAUSE_OK) {
+            FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+            ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SETLOCALDESCERROR);
+            return (SM_RC_END);
+        }
+
+        /* compare and fail if different
+        if (strcmp(msg_body.parts[0].body, sdp) != 0) {
+            ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SDPCHANGED);
+            return (SM_RC_END);
+        }*/
+
+        FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_CONNECTED);
+
+
+        cc_call_state(dcb->call_id, dcb->line, CC_STATE_ANSWERED,
+                      FSMDEF_CC_CALLER_ID);
+
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTING);
+
+        /*
+         * Now that we have negotiated the media, time to set up ICE.
+         * There also needs to be an ICE check in negotiate_media_lines.
+         */
+        cause = gsmsdp_install_peer_ice_attributes(fcb);
+        if (cause != CC_CAUSE_OK) {
+            ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SDPCHANGED);
+            return (SM_RC_END);
+        }
+
+        /* taken from fsmdef_ev_connected_ack start rx and tx  */
+        cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                  FSMDEF_CC_CALLER_ID);
+        /*
+         * If DSP is not able to start rx/tx channels, release the call
+         */
+        if (dcb->dsp_out_of_resources == TRUE) {
+            cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN, NULL);
+            return (SM_RC_END);
+        }
+
+        /* we may want to use the functionality in the following method
+         *  to handle media capability changes, needs discussion
+         * fsmdef_transition_to_connected(fcb);
+         */
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED);
+
+    }
+
+    ui_set_local_description(evSetLocalDesc, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_OK);
+
+    return (SM_RC_END);
+}
+
+
+/**
+ * SetRemoteDescription
+ *
+ */
+static sm_rcs_t
+fsmdef_ev_setremotedesc(sm_event_t *event) {
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_feature_t        *msg = (cc_feature_t *) event->msg;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    int                 action = msg->action;
+    int                 sdpmode = 0;
+    callid_t            call_id = msg->call_id;
+    line_t              line = msg->line;
+    cc_causes_t         lsm_rc;
+    cc_msgbody_t        *part;
+    uint32_t            body_length;
+    cc_msgbody_info_t   msg_body;
+    boolean             has_audio;
+    boolean             has_video;
+    boolean             has_data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (!sdpmode) {
+        ui_set_remote_description(evSetRemoteDescError, line, call_id,
+            dcb->caller_id.call_instance_id, NULL, PC_SETREMOTEDESCERROR);
+        return (SM_RC_END);
+    }
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_CLEANUP;
+    }
+
+    cc_initialize_msg_body_parts_info(&msg_body);
+
+    msg_body.num_parts = 1;
+    msg_body.content_type = cc_content_type_SDP;
+    part = &msg_body.parts[0];
+    body_length = strlen(msg->sdp);
+    part->body = msg->sdp;
+    part->body_length = body_length;
+    part->content_type = cc_content_type_SDP;
+    part->content_disposition.required_handling = FALSE;
+    part->content_disposition.disposition = cc_disposition_session;
+    part->content_id = NULL;
+
+    if (JSEP_OFFER == action) {
+
+        cause = gsmsdp_process_offer_sdp(fcb, &msg_body, TRUE);
+        if (cause != CC_CAUSE_OK) {
+            ui_set_remote_description(evSetRemoteDescError, line, call_id,
+                dcb->caller_id.call_instance_id, NULL, PC_SETREMOTEDESCERROR);
+            return (SM_RC_END);
+        }
+
+        /*
+         * Determine what media types are offered, used to create matching local SDP
+         * for negotiation.
+         */
+        gsmsdp_get_offered_media_types(fcb, dcb->sdp, &has_audio, &has_video, &has_data);
+
+        /*
+         * The sdp member of the dcb has local and remote sdp
+         * this next function fills in the local part
+         */
+        cause = gsmsdp_create_local_sdp(dcb, TRUE, has_audio, has_video, has_data, FALSE);
+        if (cause != CC_CAUSE_OK) {
+            ui_set_remote_description(evSetRemoteDescError, line, call_id, dcb->caller_id.call_instance_id,
+                NULL, PC_SETREMOTEDESCERROR);
+            FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+            // Force clean up call without sending release
+            return (fsmdef_release(fcb, cause, FALSE));
+        }
+
+        cause = gsmsdp_negotiate_media_lines(fcb, dcb->sdp, TRUE, TRUE, TRUE, FALSE);
+        if (cause != CC_CAUSE_OK) {
+            ui_set_remote_description(evSetRemoteDescError, line, call_id, dcb->caller_id.call_instance_id,
+                NULL, PC_SETREMOTEDESCERROR);
+            return (fsmdef_release(fcb, cause, FALSE));
+        }
+
+        gsmsdp_clean_media_list(dcb);
+
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_INCOMING_ALERTING);
+
+    } else if (JSEP_ANSWER == action) {
+
+        cause = gsmsdp_negotiate_answer_sdp(fcb, &msg_body);
+        if (cause != CC_CAUSE_OK) {
+            ui_set_remote_description(evSetRemoteDescError, line, call_id, dcb->caller_id.call_instance_id,
+                NULL, PC_SETREMOTEDESCERROR);
+            return (SM_RC_END);
+        }
+
+        /*
+         * Now that we have negotiated the media, time to set up ICE.
+         * There also needs to be an ICE check in negotiate_media_lines.
+         */
+        cause = gsmsdp_install_peer_ice_attributes(fcb);
+        if (cause != CC_CAUSE_OK) {
+            ui_set_remote_description(evSetRemoteDescError, line, call_id, dcb->caller_id.call_instance_id,
+                NULL, PC_SETREMOTEDESCERROR);
+            return (SM_RC_END);
+        }
+
+        cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED, FSMDEF_CC_CALLER_ID);
+
+        /* we may want to use the functionality in the following method
+         * to handle media capability changes, needs discussion
+         * fsmdef_transition_to_connected(fcb);
+         * fsmdef_transition_to_connected(fcb);
+         */
+
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED);
+    }
+
+    ui_set_remote_description(evSetRemoteDesc, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_OK);
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_localdesc(sm_event_t *event) {
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    int                 sdpmode = 0;
+    cc_causes_t         lsm_rc;
+    cc_msgbody_t        *part;
+    uint32_t            body_length;
+    cc_msgbody_info_t   msg_body;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (!sdpmode) {
+        return (SM_RC_END);
+    }
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_CLEANUP;
+    }
+
+
+    return (SM_RC_END);
+}
+
+static sm_rcs_t
+fsmdef_ev_remotedesc(sm_event_t *event) {
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    int                 sdpmode = 0;
+    cc_causes_t         lsm_rc;
+    cc_msgbody_t        *part;
+    uint32_t            body_length;
+    cc_msgbody_info_t   msg_body;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (!sdpmode) {
+
+        return (SM_RC_END);
+    }
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_CLEANUP;
+    }
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_setpeerconnection(sm_event_t *event) {
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    cc_feature_t        *msg = (cc_feature_t *) event->msg;
+    callid_t            call_id = msg->call_id;
+    int                 sdpmode = 0;
+    line_t              line = msg->line;
+    cc_causes_t         lsm_rc;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (!sdpmode) {
+        return (SM_RC_END);
+    }
+
+    if (!msg)
+      return SM_RC_END;
+
+    if (!msg->data_valid)
+      return SM_RC_END;
+
+    if (dcb == NULL) {
+      dcb = fsmdef_get_new_dcb(call_id);
+      if (dcb == NULL) {
+        return SM_RC_ERROR;
+      }
+
+      lsm_rc = lsm_get_facility_by_line(call_id, line, FALSE, dcb);
+      if (lsm_rc != CC_CAUSE_OK) {
+          FSM_DEBUG_SM(DEB_F_PREFIX"lsm_get_facility_by_line failed.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+          return SM_RC_END;
+      }
+
+      fsmdef_init_dcb(dcb, call_id, FSMDEF_CALL_TYPE_NONE, NULL, line, fcb);
+
+      fsm_set_fcb_dcbs(dcb);
+    }
+
+    PR_ASSERT(strlen(msg->data.pc.pc_handle) < PC_HANDLE_SIZE);
+    sstrncpy(dcb->peerconnection, msg->data.pc.pc_handle, sizeof(dcb->peerconnection));
+    dcb->peerconnection_set = TRUE;
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_addstream(sm_event_t *event) {
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    cc_feature_t        *msg = (cc_feature_t *) event->msg;
+    int                 sdpmode = 0;
+    cc_causes_t         lsm_rc;
+    cc_msgbody_t        *part;
+    uint32_t            body_length;
+    cc_msgbody_info_t   msg_body;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (sdpmode == FALSE) {
+        return (SM_RC_END);
+    }
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_CLEANUP;
+    }
+
+    /*
+     * This is temporary code to allow configuration of the two
+     * default streams. When multiple streams > 2 are supported this
+     * will be re-implemented.
+     */
+    if (msg->data.track.media_type == VIDEO) {
+        dcb->media_cap_tbl->cap[CC_VIDEO_1].enabled = TRUE;
+        dcb->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_SENDRECV;
+        dcb->media_cap_tbl->cap[CC_VIDEO_1].pc_stream = msg->data.track.stream_id;
+        dcb->media_cap_tbl->cap[CC_VIDEO_1].pc_track = msg->data.track.track_id;
+    } else if (msg->data.track.media_type == AUDIO) {
+        dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled = TRUE;
+        dcb->media_cap_tbl->cap[CC_AUDIO_1].support_direction = SDP_DIRECTION_SENDRECV;
+        dcb->media_cap_tbl->cap[CC_AUDIO_1].pc_stream = msg->data.track.stream_id;
+        dcb->media_cap_tbl->cap[CC_AUDIO_1].pc_track = msg->data.track.track_id;
+    } else {
+        return (SM_RC_END);
+    }
+
+    return (SM_RC_END);
+}
+
+static sm_rcs_t
+fsmdef_ev_removestream(sm_event_t *event) {
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    cc_feature_t        *msg = (cc_feature_t *) event->msg;
+    int                 sdpmode = 0;
+    cc_causes_t         lsm_rc;
+    cc_msgbody_t        *part;
+    uint32_t            body_length;
+    cc_msgbody_info_t   msg_body;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (sdpmode == FALSE) {
+
+        return (SM_RC_END);
+    }
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_CLEANUP;
+    }
+
+    /*
+     * This is temporary code to allow configuration of the two
+     * default streams. When multiple streams > 2 are supported this
+     * will be re-implemented.
+     */
+    if (msg->data.track.media_type == AUDIO) {
+        dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled = TRUE;
+        dcb->media_cap_tbl->cap[CC_AUDIO_1].support_direction = SDP_DIRECTION_RECVONLY;
+        dcb->video_pref = SDP_DIRECTION_SENDRECV;
+    } else if (msg->data.track.media_type == VIDEO) {
+        dcb->media_cap_tbl->cap[CC_VIDEO_1].enabled = TRUE;
+        dcb->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_RECVONLY;
+    } else {
+        return (SM_RC_END);
+    }
+
+       return (SM_RC_END);
+}
+
+static sm_rcs_t
+fsmdef_ev_addcandidate(sm_event_t *event) {
+    fsm_fcb_t           *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t        *dcb = fcb->dcb;
+    cc_causes_t         cause = CC_CAUSE_NORMAL;
+    cc_feature_t        *msg = (cc_feature_t *) event->msg;
+    int                 sdpmode = 0;
+    short               vcm_res;
+    uint16_t            level;
+
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (sdpmode == FALSE) {
+
+        return (SM_RC_END);
+    }
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+        return SM_RC_CLEANUP;
+    }
+
+
+    /* Perform level lookup based on mid value */
+    /* comment until mid is properly updated
+    cause = gsmsdp_find_level_from_mid(dcb, (const char *)msg->data.candidate.mid, &level);
+    */
+
+    vcm_res = vcmSetIceCandidate(dcb->peerconnection, (char *)msg->data.candidate.candidate, msg->data.candidate.level);
+    if(vcm_res) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"failure setting ice candidate.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+    }
+
+
+       return (SM_RC_END);
+}
+
+static void
+fsmdef_check_active_feature (fsmdef_dcb_t *dcb, cc_features_t ftr_id)
+{
+    if ((dcb) && (dcb->active_feature != ftr_id)) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_FTR_REQ_ACT),
+                     dcb->call_id, dcb->line,
+                     cc_feature_name(ftr_id),
+                     cc_feature_name(dcb->active_feature));
+        lsm_ui_display_notify(INDEX_STR_KEY_NOT_ACTIVE, NO_FREE_LINES_TIMEOUT);
+    }
+}
+
+static sm_rcs_t
+fsmdef_ev_idle_feature (sm_event_t *event)
+{
+    static const char  fname[]    = "fsmdef_ev_idle_feature";
+    fsm_fcb_t         *fcb     = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t      *dcb     = fcb->dcb;
+    cc_feature_t      *msg     = (cc_feature_t *) event->msg;
+    cc_srcs_t          src_id  = msg->src_id;
+    cc_features_t      ftr_id  = msg->feature_id;
+    cc_feature_data_t *data    = &(msg->data);
+    line_t             line    = msg->line;
+    cc_causes_t        cause   = CC_CAUSE_NORMAL;
+    callid_t           call_id = fcb->call_id;
+    boolean            expline;
+    sm_rcs_t           sm_rc   = SM_RC_END;
+    fsmcnf_ccb_t      *ccb;
+    fsmxfr_xcb_t      *xcb;
+    char              *global_call_id = NULL;
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    switch (src_id) {
+    case CC_SRC_UI:
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+             if (dcb) {
+                 dcb->video_pref = data->caps.support_direction;
+             }
+             break;
+        case CC_FEATURE_CFWD_ALL:
+            if (fsmdef_is_feature_uri_configured(ftr_id) == FALSE) {
+                fsm_display_feature_unavailable();
+                fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                break;
+            }
+
+            // handle cfwd event for ccm and non-ccm cases
+            // process feature event only if no other active feature
+            if ((dcb->active_feature == CC_FEATURE_NONE) &&
+                (fsmdef_get_connected_call() == NULL)) {
+                dcb->active_feature = ftr_id;
+                (void) fsmdef_process_cfwd_softkey_event(event);
+            } else {
+                fsmdef_check_active_feature(dcb, ftr_id);
+                fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            }
+            break;
+        case CC_FEATURE_NEW_CALL:
+
+            /* fetch the global_call_id from feature data */
+            global_call_id = data->newcall.global_call_id;
+
+           /*
+             * Set the expanded parameter. This parameter is used when
+             * requesting a free line. A transfer can request that the line
+             * availability be increased by one to account for the third
+             * instance of a line to become available for transfers.
+             */
+            if (data != NULL) {
+                ccb = fsmcnf_get_ccb_by_call_id(call_id);
+                xcb = fsmxfr_get_xcb_by_call_id(call_id);
+                if ((ccb != NULL) || (xcb != NULL)) {
+                    expline = TRUE;
+                } else {
+                    expline = FALSE;
+                }
+            } else {
+                expline = FALSE;
+            }
+
+            /*
+             * Get a new outgoing call context if we have not already
+             * grabbed one.
+             */
+            if (fcb->dcb == NULL) {
+                cause = fsm_get_new_outgoing_call_context(call_id, line, fcb,
+                                                          expline);
+                switch (cause) {
+                case CC_CAUSE_OK:
+                    break;
+
+                case CC_CAUSE_NO_RESOURCE:
+                    GSM_ERR_MSG("%s No Resource! Return SM_RC_CLEANUP.", fname);
+                    return (SM_RC_CLEANUP);
+
+                default:
+                    /*
+                     * No free lines.
+                     */
+                    fsm_display_no_free_lines();
+
+                    /*
+                     * Send an endcall message. This behaviour is different
+                     * than an offhook or line event because the new_call
+                     * feature may have been generated by a transfer (as the
+                     * consultation call) and will need this end_call message
+                     * so that it can cleanup the transfer. The offhook can
+                     * only come from the UI so it can just be cleaned up here
+                     * without regard for a transfer.
+                     */
+                    fsmdef_end_call(fcb->dcb, cause);
+
+                    return (SM_RC_END);
+                }
+
+                dcb = fcb->dcb;
+                /*
+                 * Let Dialog Manager know that there is OFFHOOK event
+                 */
+                fsmdef_notify_hook_event(fcb, CC_MSG_OFFHOOK, global_call_id,
+                                         data->newcall.prim_call_id,
+                                         data->newcall.hold_resume_reason,
+                                         CC_MONITOR_NONE,CFWDALL_NONE);
+            }
+
+
+            /*
+             * The user is attempting to start a new call on a specific line:
+             * 1. need to place the connected call (if there is one) on hold,
+             * 2. clear any outgoing ringing calls,
+             * 3. initiate this call.
+             */
+            if (fsmdef_wait_to_start_new_call(TRUE, CC_SRC_GSM, call_id, line,
+                        ftr_id, data)) {
+                return (SM_RC_END);
+            }
+
+            //lsm_set_active_call_id(call_id);
+
+            cc_call_state(dcb->call_id, dcb->line, CC_STATE_OFFHOOK,
+                          FSMDEF_CC_CALLER_ID);
+
+            if ( data->newcall.cause == CC_CAUSE_CONF ||
+                 data->newcall.cause == CC_CAUSE_XFER_LOCAL ) {
+              /* suppress stutter dial tone for conf and transfer features */
+              fsmdef_call_cc_state_dialing(dcb, TRUE);
+            } else {
+              fsmdef_call_cc_state_dialing(dcb, FALSE);
+            }
+
+            switch (data->newcall.cause) {
+            case CC_CAUSE_XFER_REMOTE:
+                /*
+                 * This newcall feature is really the consultation part of
+                 * a local transfer that has been transferred by the
+                 * consultation call, so proceed as though this is a
+                 * dialstring call since we already have the called_number.
+                 */
+                if (data->newcall.redirect.redirects[0].number[0] != '\0') {
+                    sm_rc = fsmdef_dialstring(fcb, data->newcall.dialstring,
+                                              &(data->newcall.redirect), FALSE,
+                                              NULL);
+
+                } else if (data->newcall.redirect.redirects[0].redirect_reason
+                               == CC_REDIRECT_REASON_DEFLECTION) {
+                    /*
+                     * CC_REDIRECT_REASON_DEFLECTION shows that transferee is
+                     * going to initiate a new call for replacing the call leg
+                     * between transferor and target.
+                     */
+
+                    memset(data->newcall.redirect.redirects[0].number, 0,
+                           sizeof(CC_MAX_DIALSTRING_LEN));
+                    sm_rc = fsmdef_dialstring(fcb, data->newcall.dialstring,
+                                              &(data->newcall.redirect), FALSE,
+                                              NULL);
+
+                } else {
+                    sm_rc =
+                        fsmdef_dialstring(fcb, data->newcall.dialstring, NULL,
+                                          FALSE, NULL);
+                }
+
+                return (sm_rc);
+
+            case CC_CAUSE_REDIRECT:
+                sm_rc = fsmdef_dialstring(fcb, data->newcall.dialstring,
+                                          &(data->newcall.redirect), FALSE,
+                                          NULL);
+                return (sm_rc);
+
+            case CC_CAUSE_XFER_BY_REMOTE:
+
+                /* CC_REDIRECT_REASON_DEFLECTION shows that transferee is
+                 * going to initiate a new call for replacing the call leg
+                 * between transferor and target.
+                 */
+
+                memset(data->newcall.redirect.redirects[0].number, 0,
+                       sizeof(CC_MAX_DIALSTRING_LEN));
+                sm_rc = fsmdef_dialstring(fcb, data->newcall.dialstring,
+                                          &(data->newcall.redirect), FALSE,
+                                          NULL);
+                return (sm_rc);
+
+            default:
+                fsm_change_state(fcb, __LINE__, FSMDEF_S_COLLECT_INFO);
+
+                return (SM_RC_END);
+            }
+
+        case CC_FEATURE_END_CALL:
+            cause = fsmdef_get_cause(msg->data_valid, data);
+
+            /*
+             * There has to be a dcb to process this event.
+             */
+            if (fcb->dcb == NULL) {
+                // commented out following line due to klocwork error
+                // call is dereferencing fcb->dcb when it is NULL (sorry, can't do that!)
+                // cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN, NULL);
+                return (SM_RC_CLEANUP);
+            }
+
+            if (dcb->call_type == FSMDEF_CALL_TYPE_INCOMING ||
+                dcb->call_type == FSMDEF_CALL_TYPE_FORWARD) {
+                dcb->send_release = TRUE;
+            }
+
+            return (fsmdef_release(fcb, cause, dcb->send_release));
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }
+        break;
+
+    default:
+        fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
+
+    }                           /* switch (src_id) { */
+
+    return (sm_rc);
+}
+
+sm_rcs_t
+fsmdef_offhook (fsm_fcb_t *fcb, cc_msgs_t msg_id, callid_t call_id,
+                line_t line, const char *dial_string,
+                sm_event_t *event, char *global_call_id,
+                callid_t prim_call_id, cc_hold_resume_reason_e consult_reason,
+                monitor_mode_t monitor_mode)
+{
+    boolean     wait  = FALSE;
+    boolean     wait2 = FALSE;
+    boolean     wait3 = FALSE;
+    cc_causes_t cause;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /*
+     * Get a new outgoing call context if we have not already
+     * grabbed one.
+     */
+    if (fcb->dcb == NULL) {
+        cause = fsm_get_new_outgoing_call_context(call_id, line, fcb, FALSE);
+        switch (cause) {
+        case CC_CAUSE_OK:
+            break;
+
+        default:
+            /*
+             * No free lines
+             */
+            fsm_display_no_free_lines();
+
+            if (fsmdef_get_connected_call() != NULL) {
+                lsm_speaker_mode(ON);
+            } else {
+                lsm_speaker_mode(OFF);
+            }
+            return (SM_RC_CLEANUP);
+        }
+
+        /*
+         * Let Dialog Manager know that there is OFFHOOK event
+         */
+        fsmdef_notify_hook_event(fcb, CC_MSG_OFFHOOK, global_call_id,
+                                 prim_call_id, consult_reason, monitor_mode,CFWDALL_NONE);
+    }
+
+
+    /*
+     * The user is attempting to start a new call on a specific line:
+     * 1. need to place the connected call (if there is one) on hold,
+     * 2. clear any outgoing ringing calls, or calls are in reorder/busy state
+     * 3. initiate this call.
+     */
+    fsmdef_find_and_hold_connected_call(call_id, &wait, CC_SRC_GSM);
+
+    fsmdef_find_and_handle_ring_connecting_releasing_calls(call_id, &wait2);
+
+    fsmdef_clear_preserved_calls(&wait3);
+
+    /*
+     * Requeue the message if we need to wait for the connected line to
+     * hold
+     */
+    if ((wait == TRUE) || (wait2 == TRUE) || (wait3 == TRUE)) {
+        switch (msg_id) {
+        case CC_MSG_OFFHOOK:
+            cc_int_offhook(CC_SRC_GSM, CC_SRC_GSM, prim_call_id, consult_reason,
+                           call_id, line, global_call_id, monitor_mode,CFWDALL_NONE);
+            break;
+
+        case CC_MSG_LINE:
+            cc_int_line(CC_SRC_GSM, CC_SRC_GSM, call_id, line);
+            break;
+
+        case CC_MSG_DIALSTRING:
+            cc_int_dialstring(CC_SRC_GSM, CC_SRC_GSM, call_id, line,
+                              dial_string, global_call_id, monitor_mode);
+            break;
+
+        case CC_MSG_FEATURE:
+            if (dial_string != NULL) {
+                cc_int_dialstring(CC_SRC_GSM, CC_SRC_GSM, call_id, line,
+                                  dial_string, global_call_id, monitor_mode);
+                break;
+            }
+
+            /*FALLTHROUGH*/
+        default:
+            cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                          NULL);
+            return (SM_RC_CLEANUP);
+        }
+
+        return (SM_RC_END);
+    }
+
+    //lsm_set_active_call_id(call_id);
+
+    return (SM_RC_SUCCESS);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_idle_offhook (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb = (fsm_fcb_t *) event->data;
+    cc_offhook_t *msg = (cc_offhook_t *) event->msg;
+    fsmdef_dcb_t *dcb;
+    sm_rcs_t      sm_rc;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    sm_rc = fsmdef_offhook(fcb, msg->msg_id, msg->call_id, msg->line, NULL,
+                           event, msg->global_call_id, msg->prim_call_id,
+                           msg->hold_resume_reason, msg->monitor_mode);
+
+    if (sm_rc != SM_RC_SUCCESS) {
+        return (sm_rc);
+    }
+
+    dcb = fcb->dcb;
+
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_OFFHOOK,
+                  FSMDEF_CC_CALLER_ID);
+
+    fsmdef_call_cc_state_dialing(dcb, FALSE);
+
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_COLLECT_INFO);
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_idle_dialstring (sm_event_t *event)
+{
+    fsm_fcb_t       *fcb = (fsm_fcb_t *) event->data;
+    cc_dialstring_t *msg = (cc_dialstring_t *) event->msg;
+    fsmdef_dcb_t    *dcb;
+    cc_action_data_t data;
+    sm_rcs_t         sm_rc;
+    cc_call_info_t   call_info;
+    cc_call_info_t  *call_info_p = NULL;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    sm_rc = fsmdef_offhook(fcb, msg->msg_id, msg->call_id, msg->line,
+                           msg->dialstring, event, msg->g_call_id,
+                           CC_NO_CALL_ID, CC_REASON_NONE, msg->monitor_mode);
+
+    if (sm_rc != SM_RC_SUCCESS) {
+        return (sm_rc);
+    }
+
+    dcb = fcb->dcb;
+
+    if (msg->dialstring) {
+        lsm_set_lcb_dialed_str_flag(dcb->call_id);
+    }
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_OFFHOOK,
+                  FSMDEF_CC_CALLER_ID);
+
+    data.tone.tone = VCM_INSIDE_DIAL_TONE;
+    (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_TONE, &data);
+
+    dcb->send_release = TRUE;
+
+    /* Set call_info with global call_id, sent in the Initcallreq,
+     * If this call is not because Initcallreq, then don't set call_info
+     * Also, if this is a monitor call, add the mode to the call_info
+     */
+    if (msg->g_call_id != NULL) {
+
+        call_info.type = CC_FEAT_INIT_CALL;
+        call_info.data.initcall.monitor_mode = msg->monitor_mode;
+        sstrncpy(call_info.data.initcall.gcid, msg->g_call_id, CC_GCID_LEN);
+
+        call_info_p = &call_info;
+    }
+
+    if ( strncmp(CISCO_BLFPICKUP_STRING, msg->dialstring, strlen(CISCO_BLFPICKUP_STRING)) == 0 ) {
+        dcb->log_disp = CC_CALL_LOG_DISP_RCVD;
+    }
+
+    sm_rc = fsmdef_dialstring(fcb, msg->dialstring, NULL, FALSE, call_info_p);
+
+    return (sm_rc);
+}
+
+static sm_rcs_t
+fsmdef_ev_session_audit (sm_event_t *event)
+{
+    static const char  fname[]    = "fsmdef_ev_session_audit";
+    fsm_fcb_t          *fcb       = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t       *dcb       = fcb->dcb;
+    cc_audit_sdp_req_t *audit_msg = (cc_audit_sdp_req_t *) event->msg;
+    cc_msgbody_info_t   msg_body;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (gsmsdp_encode_sdp_and_update_version(dcb, &msg_body) != CC_CAUSE_OK) {
+        /*
+         * Failed to encode our local sdp. Send ack to SIP stack with
+         * no message body. SIP stack will include previously send
+         * SDP in response to session audit request.
+         */
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+
+        cc_int_audit_sdp_ack(CC_SRC_GSM, CC_SRC_SIP, audit_msg->call_id,
+                             audit_msg->line, NULL);
+    } else {
+        cc_int_audit_sdp_ack(CC_SRC_GSM, CC_SRC_SIP, audit_msg->call_id,
+                             audit_msg->line, &msg_body);
+    }
+
+    /*
+     * If we are currently performing a spoofed ringout and the current session audit
+     * does not indicate that we should continue to do so, go back to connected state.
+     * But only change to connected state if not locally held.
+     */
+    if (dcb->spoof_ringout_applied &&
+        !dcb->spoof_ringout_requested) {
+
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
+                     dcb->call_id, dcb->line, fname);
+
+        if ((fcb->state != FSMDEF_S_HOLDING) &&
+            (fcb->state != FSMDEF_S_HOLD_PENDING)) {
+            /*
+             * If is at least one media entry that is not in loally held
+             * then go to connected state.
+             */
+            dcb->spoof_ringout_applied = FALSE;
+            cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                          FSMDEF_CC_CALLER_ID);
+        }
+    }
+
+    return (SM_RC_SUCCESS);
+}
+
+static sm_rcs_t
+fsmdef_ev_collectinginfo_release (sm_event_t *event)
+{
+    fsm_fcb_t          *fcb       = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t       *dcb       = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+   fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_CALL_FAILED, CC_CAUSE_INVALID_NUMBER);
+
+    // Start onhook timer
+    if ( dcb->err_onhook_tmr) {
+        (void) cprDestroyTimer(dcb->err_onhook_tmr);
+    }
+    dcb->err_onhook_tmr = cprCreateTimer("Error Onhook",
+                                         GSM_ERROR_ONHOOK_TIMER,
+                                         TIMER_EXPIRATION,
+                                         gsm_msg_queue);
+    if (dcb->err_onhook_tmr == NULL) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
+                     dcb->call_id, dcb->line, "", "Error Onhook");
+
+        return (SM_RC_CLEANUP);
+    }
+
+    if (cprStartTimer(dcb->err_onhook_tmr,
+                      FSMDEF_ERR_ONHOOK_TMR_SECS * 1000,
+                      (void *)(long)dcb->call_id) == CPR_FAILURE) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_START_FAILED),
+                     dcb->call_id, dcb->line, "",
+                     "Error Onhook", cpr_errno);
+               return (SM_RC_CLEANUP);
+    }
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_collectinginfo_feature (sm_event_t *event)
+{
+    fsm_fcb_t       *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t    *dcb    = fcb->dcb;
+    cc_feature_t    *msg    = (cc_feature_t *) event->msg;
+    cc_srcs_t        src_id = msg->src_id;
+    cc_features_t    ftr_id = msg->feature_id;
+    cc_action_data_t data;
+    sm_rcs_t         sm_rc = SM_RC_END;
+    cc_causes_t      cause;
+    cc_feature_data_t *feature_data = &(msg->data);
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (msg->feature_id) {
+    case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+        dcb->video_pref = feature_data->caps.support_direction;
+        break;
+    case CC_FEATURE_END_CALL:
+        cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
+        if (fcb->state == FSMDEF_S_KPML_COLLECT_INFO) {
+            /* Clean up and send release */
+            return (fsmdef_release(fcb, cause, TRUE));
+        }
+        else {
+            /* Clean up without sending release */
+            return (fsmdef_release(fcb, cause, FALSE));
+        }
+
+    case CC_FEATURE_NUMBER:
+    case CC_FEATURE_URL:
+        dcb->dial_mode = ((msg->feature_id == CC_FEATURE_NUMBER) ?
+                          (DIAL_MODE_NUMERIC) : (DIAL_MODE_URL));
+
+        data.dial_mode.mode = dcb->dial_mode;
+        data.dial_mode.digit_cnt = dcb->digit_cnt;
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_DIAL_MODE,
+                             &data);
+
+        break;
+
+    case CC_FEATURE_CALLINFO:
+        fsmdef_update_callinfo(fcb, msg);
+        /*
+         * lsm_set_lcb_prevent_ringing() will check if there is a RINGIN call
+         * with the same GCID. If so, it will set a flag to prevent ringing.
+         */
+        lsm_set_lcb_prevent_ringing(dcb->call_id);
+        break;
+
+    case CC_FEATURE_SELECT:
+        fsmdef_select_invoke(dcb, feature_data);
+        return (SM_RC_END);
+
+    case CC_FEATURE_CFWD_ALL:
+        if (fsmdef_is_feature_uri_configured(msg->feature_id) == FALSE) {
+            fsm_set_call_status_feature_unavailable(dcb->call_id, dcb->line);
+
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }
+
+        // handle cfwd event for ccm and non-ccm cases
+        // process feature event only if no other active feature
+        if (dcb->active_feature == CC_FEATURE_NONE) {
+            dcb->active_feature = ftr_id;
+            (void) fsmdef_process_cfwd_softkey_event(event);
+         } else {
+            fsmdef_check_active_feature(dcb, ftr_id);
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+        }
+        break;
+
+    default:
+        dcb->active_feature = CC_FEATURE_NONE;
+        fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+        break;
+    }
+
+    return (sm_rc);
+}
+
+/*
+ *  Function: fsmdef_ev_digit_begin
+ *
+ *  Parameters: event
+ *
+ *  Description: This function is called each time a digit is
+ *    received from the platform code.  Currently, the platform code
+ *    parses the dialplan.  This function simply turns the
+ *    dialtone off every time a digit is received and
+ *    displays the appropriate keyset.  (Eventually, this
+ *    interface should be changed and GSM should have
+ *    better knowledge of the dialplan.)
+ *
+ *  Returns: SM_RC_END
+ */
+static sm_rcs_t
+fsmdef_ev_digit_begin (sm_event_t *event)
+{
+    static const char  fname[]    = "fsmdef_ev_digit_begin";
+    fsm_fcb_t        *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t     *dcb = fcb->dcb;
+    cc_digit_begin_t *msg = (cc_digit_begin_t *) event->msg;
+    char              digit;
+    cc_action_data_t  data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    digit = lsm_digit2ch(msg->digit);
+
+    FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Digit Received= %c: stopping dial tone..\n",
+               DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, msg->call_id, fname), digit);
+    data.tone.tone = VCM_INSIDE_DIAL_TONE;
+    (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_TONE, &data);
+
+    /*
+     * Increment digit_cnt so that the proper keyset will be
+     * displayed.
+     */
+    if (dcb->digit_cnt < CC_MAX_DIALSTRING_LEN) {
+        dcb->digit_cnt++;
+    }
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_proceeding (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t *dcb = fcb->dcb;
+
+    fcb->dcb->send_release = TRUE;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+#ifdef SAPP_SAPP_GSM
+    if ((event->msg != NULL) &&
+        (((cc_proceeding_t *)(event->msg))->caller_id.called_name != NULL)) {
+        dcb->caller_id.called_name =
+            strlib_update(dcb->caller_id.called_name,
+                          ((cc_proceeding_t *) (event->msg))->caller_id.
+                          called_name);
+    }
+#endif
+
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_FAR_END_PROCEEDING,
+                  FSMDEF_CC_CALLER_ID);
+
+
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_OUTGOING_PROCEEDING);
+
+    return (SM_RC_END);
+}
+
+static sm_rcs_t
+fsmdef_ev_out_alerting (sm_event_t *event)
+{
+    static const char fname[] = "fsmdef_ev_out_alerting";
+    fsm_fcb_t     *fcb   = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t  *dcb   = fcb->dcb;
+    cc_alerting_t *msg   = (cc_alerting_t *) event->msg;
+    cc_causes_t    cause = CC_CAUSE_ERROR;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    dcb->send_release = TRUE;
+
+    dcb->inband = FALSE;
+    if (msg->inband) {
+        dcb->inband = TRUE;
+
+        cause = gsmsdp_negotiate_answer_sdp(fcb, &msg->msg_body);
+        if (cause != CC_CAUSE_OK) {
+            cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                          NULL);
+            return (fsmdef_release(fcb, cause, dcb->send_release));
+        }
+
+        /*
+         * Record fact that we have successfully negotiated media that may be
+         * used for inband ringback.
+         */
+        dcb->inband_received = TRUE;
+        FSM_DEBUG_SM(DEB_F_PREFIX"inband_received, cancel timer.\n", DEB_F_PREFIX_ARGS(FSM, fname));
+
+        /*
+         * If ringback delay timer has been started, cancel it now.
+         */
+        if (cprCancelTimer(dcb->ringback_delay_tmr) != CPR_SUCCESS) {
+            FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CANCEL_FAILED),
+                         dcb->call_id, dcb->line, fname, "Ringback Delay",
+                         cpr_errno);
+        }
+    } else {
+        /*
+         * Not inband alerting case. Set ringback delay timer so that local
+         * ringback will eventually be played. We delay the ringback for
+         * a short time to handle the case where the messages key was pressed.
+         * This is because VM server can respond very quickly with RTP, 183,
+         * and 200 and we do not want local ringback tone to interfere with
+         * the playing of the VM prompt.
+         */
+        if (!cprIsTimerRunning(dcb->ringback_delay_tmr)) {
+            fsmdef_set_ringback_delay_timer(dcb);
+        }
+    }
+
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_FAR_END_ALERTING,
+                  FSMDEF_CC_CALLER_ID);
+
+    /*
+     * If DSP is not able to start rx/tx channels, release the call
+     */
+    if (dcb->dsp_out_of_resources == TRUE) {
+        (void)fsmdef_release(fcb, CC_CAUSE_NO_MEDIA, dcb->send_release);
+        cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                      NULL);
+        return (SM_RC_END);
+    }
+//    fsmdef_update_pd(dcb, FSMDEF_CALL_TYPE_OUTGOING);
+
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_OUTGOING_ALERTING);
+
+    return (SM_RC_END);
+}
+
+static sm_rcs_t
+fsmdef_ev_callsent_release (sm_event_t *event)
+{
+    fsm_fcb_t      *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t   *dcb    = fcb->dcb;
+    cc_release_t   *msg    = (cc_release_t *) event->msg;
+    cc_causes_t     cause  = msg->cause;
+    cc_srcs_t       src_id = msg->src_id;
+    sm_rcs_t        sm_rc  = SM_RC_END;
+    char            tmp_str[STATUS_LINE_MAX_LEN];
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /* if UI_STATE of BUSY in 183 Call-Info is causing the release,
+       do not modify dcb->send_release */
+    if (cause != CC_CAUSE_UI_STATE_BUSY) {
+        dcb->send_release = FALSE;
+    } else {
+        // CSCti63677
+        if ((fcb->state == FSMDEF_S_OUTGOING_ALERTING) &&
+            (dcb->inband_received == TRUE) &&
+            (dcb->placed_call_update_required)) {
+
+            lsm_update_placed_callinfo(dcb);
+            dcb->placed_call_update_required = FALSE;
+        }
+    }
+
+    FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
+
+    /* For 500 response from the CCM, disconnect the call and clear the UI.
+     * There are several cases in which CCM sends down 500 response code to
+     * clear the call and UI. Some of the cases are CFWDALL, early conference
+     * and CTI transfer of ringing call
+     *
+     * Non-auto pickups do receive 480 response, it is OK release the call.
+     */
+    if ((cause == CC_CAUSE_REMOTE_SERVER_ERROR) ||
+            (((strncmp(dcb->caller_id.called_number, CISCO_BLFPICKUP_STRING,
+                       (sizeof(CISCO_BLFPICKUP_STRING) - 1)) == 0)) &&
+             ((cause == CC_TEMP_NOT_AVAILABLE) || (cause == CC_CAUSE_CONGESTION) ))) {
+        if (cause == CC_CAUSE_CONGESTION) {
+            if (platGetPhraseText(STR_INDEX_NO_CALL_FOR_PICKUP, (char *)tmp_str, STATUS_LINE_MAX_LEN - 1) == CPR_SUCCESS)
+            {
+                ui_set_notification(CC_NO_LINE, CC_NO_CALL_ID, tmp_str, 2, FALSE, DEF_NOTIFY_PRI);
+            }
+        }
+        cause = CC_CAUSE_OK;
+    }
+
+    switch (cause) {
+        case CC_CAUSE_ERROR:
+        case CC_CAUSE_NOT_FOUND:
+        case CC_CAUSE_BUSY:
+        case CC_CAUSE_CONGESTION:
+        case CC_CAUSE_INVALID_NUMBER:
+        case CC_CAUSE_PAYLOAD_MISMATCH:
+        case CC_CAUSE_REMOTE_SERVER_ERROR:
+        case CC_TEMP_NOT_AVAILABLE:
+        case CC_CAUSE_UI_STATE_BUSY:
+        case CC_CAUSE_NO_USER_ANS:
+
+            fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_CALL_FAILED, cause);
+
+            if (cause != CC_CAUSE_UI_STATE_BUSY) {
+                cc_int_release_complete(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                        dcb->line, cause, NULL);
+            }
+            /* see if the SIP stack has aborted this call early for some reason
+             * If SIP brought this down, we are still offhook on the UI, so
+             * when we get the release_complete from the 200 for the BYE, we
+             * need to ignore it, so that reorder can be played AND when the user
+             * hangs up, then the UI will be driven to a clean state.
+             */
+            if (src_id == CC_SRC_SIP) {
+                dcb->early_error_release = TRUE;
+            }
+
+            if ( dcb->err_onhook_tmr) {
+                (void) cprDestroyTimer(dcb->err_onhook_tmr);
+            }
+            dcb->err_onhook_tmr = cprCreateTimer("Error Onhook",
+                    GSM_ERROR_ONHOOK_TIMER,
+                    TIMER_EXPIRATION,
+                    gsm_msg_queue);
+            if (dcb->err_onhook_tmr == NULL) {
+                FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
+                        dcb->call_id, dcb->line, "", "Error Onhook");
+                return (SM_RC_CLEANUP);
+            }
+
+            if (cprStartTimer(dcb->err_onhook_tmr,
+                        FSMDEF_ERR_ONHOOK_TMR_SECS * 1000,
+                        (void *)(long)dcb->call_id) == CPR_FAILURE) {
+
+                FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_START_FAILED),
+                        dcb->call_id, dcb->line, "",
+                        "Error Onhook", cpr_errno);
+
+                return (SM_RC_CLEANUP);
+            }
+
+            break;
+
+        default:
+            sm_rc = fsmdef_release(fcb, cause, dcb->send_release);
+            if (sm_rc == SM_RC_CLEANUP) {
+                /*
+                 * FSM release indicates clean up, do not continue
+                 * on since fcb and dcb have been freed or re-initialized.
+                 */
+                return (sm_rc);
+            }
+    }                           /* switch (cause) */
+
+    /*UI_STATE of BUSY in 183 is causing the release, so
+     *don't change state. This is needed to support
+     *callback feature. Since the callee is busy, we need
+     *update call UI status to "Busy" from "Ringout" to
+     *reflect this change.
+     */
+    if (cause != CC_CAUSE_UI_STATE_BUSY) {
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_RELEASING);
+    } else {
+        cc_action_data_t action_data;
+        action_data.update_ui.action = CC_UPDATE_SET_CALL_STATUS;
+        action_data.update_ui.data.set_call_status_parms.phrase_str_p = platform_get_phrase_index_str(LINE_BUSY);
+        action_data.update_ui.data.set_call_status_parms.timeout = 0;
+        action_data.update_ui.data.set_call_status_parms.call_id = dcb->call_id;
+        action_data.update_ui.data.set_call_status_parms.line = dcb->line;
+        /*Update UI status to "Busy".*/
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_UPDATE_UI,
+                             &action_data);
+    }
+
+    return (sm_rc);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_callsent_feature (sm_event_t *event)
+{
+    fsm_fcb_t       *fcb     = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t    *dcb     = fcb->dcb;
+    cc_feature_t    *msg     = (cc_feature_t *) event->msg;
+    cc_srcs_t        src_id  = msg->src_id;
+    cc_features_t    ftr_id  = msg->feature_id;
+    callid_t         call_id = msg->call_id;
+    line_t           line    = msg->line;
+    cc_causes_t      cause;
+    cc_feature_data_redirect_t *data = &(msg->data.redirect);
+    cc_action_data_t action_data;
+    cc_feature_data_t *select_data = &(msg->data);
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (ftr_id) {
+    case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+        dcb->video_pref = select_data->caps.support_direction;
+        break;
+    case CC_FEATURE_NOTIFY:
+        if (src_id == CC_SRC_SIP) {
+            fsmdef_ev_notify_feature(msg, dcb);
+        } else {
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+        }
+
+        break;
+
+    case CC_FEATURE_END_CALL:
+        /**
+         * In case of earlier attandence, there might a waiting call.
+         */
+        lsm_remove_lcb_prevent_ringing(dcb->call_id);
+        /*
+         *  Since user press the end call, no need to wait to play the busy tone.
+         *  So, clear the early_error_release and clean the fcb/dcb.
+         */
+        dcb->early_error_release = FALSE;
+        cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
+
+        return (fsmdef_release(fcb, cause, dcb->send_release));
+
+    case CC_FEATURE_REDIRECT:
+        /*
+         * The outgoing call has been redirected, so we need to:
+         * 1. ACK the redirect request,
+         * 2. release the current call,
+         * 3. start a new call to the redirect number.
+         */
+        cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id, line,
+                           CC_FEATURE_REDIRECT, NULL, CC_CAUSE_REDIRECT);
+        /*
+         * May need to update an xcb if this call is involved in a transfer.
+         */
+        //xcb = fsmxfr_get_xcb_by_call_id(call_id);
+        // fsmxfr_update_xfr_context(xcb, call_id, redirect_call_id);
+        dcb->caller_id.called_number =
+            strlib_update(dcb->caller_id.called_number, data->redirect_number);
+
+        cc_call_state(dcb->call_id, dcb->line, CC_STATE_DIALING_COMPLETED,
+                      FSMDEF_CC_CALLER_ID);
+
+        break;
+
+
+    case CC_FEATURE_CALLINFO:
+        fsmdef_update_calltype(fcb, msg);
+        fsmdef_update_callinfo(fcb, msg);
+        /*
+         * lsm_set_lcb_prevent_ringing() will check if there is a RINGIN call
+         * with the same GCID. If so, it will set a flag to prevent ringing.
+         */
+        lsm_set_lcb_prevent_ringing(dcb->call_id);
+        break;
+
+    case CC_FEATURE_UPDATE:
+        /* Simply reply with a 200OK to a received UPDATE */
+        cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id, line,
+                           CC_FEATURE_UPDATE, NULL, CC_CAUSE_OK);
+        break;
+
+    case CC_FEATURE_RINGBACK_DELAY_TIMER_EXP:
+        if (!dcb->inband_received) {
+            /*
+             * Ringback delay timer expired and we have not received
+             * a response from the far end indicating that they are
+             * playing inband ringback. Start local ringback tone now.
+             */
+            action_data.tone.tone = VCM_ALERTING_TONE;
+            (void)cc_call_action(call_id, line, CC_ACTION_PLAY_TONE,
+                                 &action_data);
+        }
+        break;
+
+
+    case CC_FEATURE_SELECT:
+        fsmdef_select_invoke(dcb, select_data);
+        return (SM_RC_END);
+
+
+    case CC_FEATURE_SUBSCRIBE:
+        /* KPML subscription received so collect digits for KPML */
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_KPML_COLLECT_INFO);
+        break;
+
+    case CC_FEATURE_CFWD_ALL:
+        fsm_set_call_status_feature_unavailable(call_id, line);
+
+        fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+        break;
+
+    default:
+        fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+        break;
+    }                           /* switch (ftr_id) { */
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_release_call (fsm_fcb_t *fcb, cc_feature_t *msg)
+{
+    cc_feature_data_t *data = &(msg->data);
+    cc_state_data_t    state_data;
+    cc_causes_t        cause;
+    fsmdef_dcb_t      *dcb  = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    cause = fsmdef_get_cause(msg->data_valid, data);
+
+    /*
+     * Do things a little different depending on the value of the
+     * release cause.
+     */
+    switch (cause) {
+    case CC_CAUSE_XFER_LOCAL:
+        /*
+         * Send release and then wait for the release_complete.
+         */
+        cc_int_release(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                       dcb->line, data->endcall.cause,
+                       data->endcall.dialstring, NULL);
+
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_RELEASING);
+
+        state_data.onhook.caller_id = dcb->caller_id;
+        state_data.onhook.local     = TRUE;
+        state_data.onhook.cause     = CC_CAUSE_NORMAL;
+        cc_call_state(dcb->call_id, dcb->line, CC_STATE_ONHOOK, &state_data);
+
+        break;
+
+    case CC_CAUSE_XFER_REMOTE:
+        /*
+         * No need to send release because the remote end initiated
+         * the transfer.
+         */
+        dcb->send_release = FALSE;
+        return (fsmdef_release(fcb, cause, dcb->send_release));
+
+    case CC_CAUSE_XFER_CNF:
+    case CC_CAUSE_REPLACE:
+        /*
+         * We are the target of a transfer and this is the consultation
+         * call that is being replaced, so we just need to onhook this call
+         * but leave the signaling up until the stack notifies the FSM that
+         * the transfer is accepted - and then we will release the call.
+         * Same has to happen when bridge of conference ends the call. We are
+         * initiating transfer in this case so we want signaling to remain
+         * up while UI should be cleared up.
+         */
+        state_data.onhook.caller_id = dcb->caller_id;
+        state_data.onhook.local     = TRUE;
+        state_data.onhook.cause     = CC_CAUSE_NORMAL;
+        cc_call_state(dcb->call_id, dcb->line, CC_STATE_ONHOOK, &state_data);
+
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLDING);
+
+        break;
+
+    default:
+        return (fsmdef_release(fcb, cause, dcb->send_release));
+    }
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_inalerting_feature (sm_event_t *event)
+{
+    fsm_fcb_t     *fcb     = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t  *dcb     = fcb->dcb;
+    cc_feature_t  *msg     = (cc_feature_t *) event->msg;
+    cc_srcs_t      src_id  = msg->src_id;
+    cc_features_t  ftr_id  = msg->feature_id;
+    callid_t       call_id = msg->call_id;
+    line_t         line    = msg->line;
+    cc_feature_data_t *data = &(msg->data);
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_UI:
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+             dcb->video_pref = data->caps.support_direction;
+             /* force an update to media cap */
+             dcb->media_cap_tbl->id--;
+             gsmsdp_update_local_sdp_media_capability(dcb, FALSE, FALSE);
+             break;
+
+        case CC_FEATURE_END_CALL:
+            return (fsmdef_release_call(fcb, msg));
+
+        case CC_FEATURE_ANSWER:
+            /*
+             * The user wants to answer this call, so...
+             * 1. need to place the connected call (if there is one) on hold,
+             * 2. clear all the outgoing ringing lines,
+             * 3. answer this call.
+             */
+            if (fsmdef_wait_to_start_new_call(TRUE, CC_SRC_GSM, dcb->call_id, dcb->line,
+                                              CC_FEATURE_ANSWER, NULL)) {
+
+                /*
+                 * Inform the LSM that the answering of this call has
+                 * been delayed while waiting for other calls to clear.
+                 */
+                (void)cc_call_action(dcb->call_id, dcb->line,
+                                     CC_ACTION_ANSWER_PENDING, NULL);
+
+                return (SM_RC_END);
+            }
+
+            return (fsmdef_handle_inalerting_offhook_answer(event));
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (ftr_id) { */
+
+        break;
+
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_CALLINFO:
+            fsmdef_update_callinfo(fcb, msg);
+            break;
+
+        case CC_FEATURE_UPDATE:
+            /* Simply reply with a 200 OK to a received UPDATE */
+            cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id, line,
+                               CC_FEATURE_UPDATE, NULL, CC_CAUSE_OK);
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (ftr_id) { */
+
+        break;
+
+    default:
+        fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+        break;
+    }                           /* switch (src_id) { */
+
+    return (SM_RC_END);
+}
+
+
+/*
+ * This function contains the common code for fsmdef_ev_inalerting_offhook()
+ * and the ANSWER event handling in the fsmdef_ev_inalerting_feature().
+ */
+static sm_rcs_t
+fsmdef_handle_inalerting_offhook_answer (sm_event_t *event)
+{
+    fsm_fcb_t        *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t     *dcb = fcb->dcb;
+    cc_causes_t       cause;
+    cc_msgbody_info_t msg_body;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /* Build our response SDP to include in the connected */
+    cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
+    if (cause != CC_CAUSE_OK) {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        return (fsmdef_release(fcb, cause, dcb->send_release));
+    }
+
+    /* For CCM, call_type indicate if the call is forwarded or not
+     * for forwarded call display will be shown as "Forward", only
+     * during ringing state. Once the call is  connected then the call
+     * is shown as normal incoming call "From". so change call type now
+     * Do this only if Retain Forward Information is disabled or not configured.
+     * If configured/enabled then leave the call type as Forward.
+     */
+
+    if (dcb->call_type == FSMDEF_CALL_TYPE_FORWARD) {
+        if (!fsmdef_check_retain_fwd_info_state()) {
+            dcb->call_type = FSMDEF_CALL_TYPE_INCOMING;
+            /*
+             * Force us to update the UI so that any possible callinfo received
+             * prior to the call is answered takes effect.
+             */
+            dcb->ui_update_required = TRUE;
+        }
+    }
+
+    /* Cancel any existing autoanswer timer */
+    (void)cprCancelTimer(dcb->autoAnswerTimer);
+
+    cc_int_connected(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                     &(dcb->caller_id), NULL, &msg_body);
+
+    FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_CONNECTED);
+
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_ANSWERED,
+                  FSMDEF_CC_CALLER_ID);
+
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTING);
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_inalerting_offhook (sm_event_t *event)
+{
+    return (fsmdef_handle_inalerting_offhook_answer(event));
+}
+
+
+static sm_rcs_t
+fsmdef_ev_connecting_feature (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t *dcb    = fcb->dcb;
+    cc_feature_t *msg    = (cc_feature_t *) event->msg;
+    cc_srcs_t     src_id = msg->src_id;
+    cc_features_t ftr_id = msg->feature_id;
+    cc_causes_t   cause;
+    cc_feature_data_t *data = &(msg->data);
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_UI:
+        switch (ftr_id) {
+        case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+             dcb->video_pref = data->caps.support_direction;
+             break;
+        case CC_FEATURE_END_CALL:
+            cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
+
+            return (fsmdef_release(fcb, cause, dcb->send_release));
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }
+
+        break;
+
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_CALLINFO:
+            fsmdef_update_callinfo(fcb, msg);
+            break;
+
+        case CC_FEATURE_CALL_PRESERVATION:
+            return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
+
+        case CC_FEATURE_NOTIFY:
+            fsmdef_ev_notify_feature(msg, dcb);
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }
+
+        break;
+
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_END_CALL:
+            cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
+
+            return (fsmdef_release(fcb, cause, dcb->send_release));
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }
+
+        break;
+
+    default:
+        fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
+
+        break;
+    }
+
+    return (SM_RC_END);
+}
+
+/**
+ *
+ * Function to handle transition to FSMDEF_S_CONNECTED. It checks
+ * whether there is any media capability that needs to be updated
+ * or not. If there is not then it transition to FSMDEF_S_CONNECTED
+ * otherwise it transitions to the FSMDEF_S_CONNECTED_MEDIA_PEND state
+ * and sends out the media update request.
+ *
+ * @param[in] fcb     - The pointer to the fsm_fcb_t structure of this
+ *                      call.
+ *
+ * @return  SM_RC_END or SM_RC_CLEANUP
+ *
+ * @pre     (fcb not_eq NULL)
+ */
+static sm_rcs_t
+fsmdef_transition_to_connected (fsm_fcb_t *fcb)
+{
+    fsmdef_dcb_t      *dcb = fcb->dcb;
+    cc_feature_data_t feature_data;
+    sm_rcs_t          sm_rc = SM_RC_END;
+    cc_causes_t       cause;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (dcb->req_pending_tmr) {
+        /* cancel any request pending timer, just in case */
+        (void) cprCancelTimer(dcb->req_pending_tmr);
+    }
+
+    /*
+     * Update the media capability without effecting the existing media line.
+     */
+    if (!gsmsdp_update_local_sdp_media_capability(dcb, FALSE, FALSE)) {
+        /* not thing is changed, transition to connected state */
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED);
+        return (sm_rc);
+    }
+
+
+    feature_data.resume.call_info.type = CC_FEAT_NONE;
+    feature_data.resume.call_info.data.hold_resume_reason = CC_REASON_NONE;
+    feature_data.resume.msg_body.num_parts = 0;
+    feature_data.resume.call_info.data.call_info_feat_data.swap = FALSE;
+    feature_data.resume.call_info.data.call_info_feat_data.protect = FALSE;
+    /* Encode SDP */
+    cause = gsmsdp_encode_sdp_and_update_version(dcb,
+                                                 &feature_data.resume.msg_body);
+    if (cause != CC_CAUSE_OK) {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        return(fsmdef_release(fcb, cause, dcb->send_release));
+    }
+
+    fsmdef_get_rtp_stat(dcb, &(feature_data.resume.kfactor));
+
+    /* Send feature request to SIP */
+    cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                   CC_FEATURE_MEDIA, &feature_data);
+
+
+    if (g_dock_undock_event == MEDIA_INTERFACE_UPDATE_STARTED) {
+        g_dock_undock_event = MEDIA_INTERFACE_UPDATE_IN_PROCESS;
+        ui_update_media_interface_change(dcb->line, dcb->call_id, MEDIA_INTERFACE_UPDATE_BEGIN);
+    } else if (g_dock_undock_event == MEDIA_INTERFACE_UPDATE_IN_PROCESS) {
+        DEF_DEBUG(DEB_F_PREFIX" MEDIA_INTERFACE_UPDATE is already in process. "
+            " Ignore another update event.\n", DEB_F_PREFIX_ARGS(FSM, "fsmdef_transition_to_connected"));
+    }
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED_MEDIA_PEND);
+    return (sm_rc);
+}
+
+static sm_rcs_t
+fsmdef_ev_connected (sm_event_t *event)
+{
+    static const char fname[] = "fsmdef_ev_connected";
+    fsm_fcb_t      *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t   *dcb = fcb->dcb;
+    cc_connected_t *msg = (cc_connected_t *) event->msg;
+    cc_causes_t     cause;
+    sm_rcs_t        sm_rc;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    dcb->send_release = TRUE;
+
+    cause = gsmsdp_negotiate_answer_sdp(fcb, &msg->msg_body);
+    if (cause != CC_CAUSE_OK) {
+
+               cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                                               NULL);
+               return (fsmdef_release(fcb, cause, dcb->send_release));
+    }
+
+    // Reset dcb->active_feature flag
+    dcb->active_feature = CC_FEATURE_NONE;
+
+    /* Reset spoof ring out in case t was set before going to connected state. */
+    FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
+                 dcb->call_id, dcb->line, fname);
+
+    dcb->spoof_ringout_applied = FALSE;
+
+    /*
+     * Cancel ringback delay timer
+     */
+    if (cprCancelTimer(dcb->ringback_delay_tmr) != CPR_SUCCESS) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CANCEL_FAILED),
+                     dcb->call_id, dcb->line, fname, "Ringback Delay",
+                     cpr_errno);
+    }
+
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                  FSMDEF_CC_CALLER_ID);
+
+    if ( dcb->log_disp != CC_CALL_LOG_DISP_UNKNWN ) {
+        ui_log_disposition(dcb->call_id, dcb->log_disp );
+    }
+
+
+    ui_cc_capability(dcb->line, lsm_get_ui_id(dcb->call_id), msg->recv_info_list);
+
+    /*
+     * If DSP is not able to start rx/tx channels, release the call
+     */
+    if (dcb->dsp_out_of_resources == TRUE) {
+        (void)fsmdef_release(fcb, CC_CAUSE_NO_MEDIA, dcb->send_release);
+        cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                      NULL);
+        return (SM_RC_END);
+    }
+    cc_int_connected_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                         &(dcb->caller_id), NULL);
+
+    FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_CONNECTED_ACK);
+
+//    fsmdef_update_pd(dcb, FSMDEF_CALL_TYPE_OUTGOING);
+
+    /*
+     * Handle media capability changes if there is before transition to
+     * connected state.
+     */
+    sm_rc = fsmdef_transition_to_connected(fcb);
+    fsmutil_set_shown_calls_ci_element(dcb->caller_id.call_instance_id, dcb->line);
+
+    return (sm_rc);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_connected_ack (sm_event_t *event)
+{
+    fsm_fcb_t          *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t       *dcb = fcb->dcb;
+    cc_connected_ack_t *msg = (cc_connected_ack_t *) event->msg;
+    cc_causes_t         cause;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /*
+     * Check the remote SDP. The far end may not have included the SDP in an
+     * earlier message, which means that the SDP must be in this message.
+     */
+    if (dcb->remote_sdp_in_ack == TRUE) {
+        cause = gsmsdp_negotiate_answer_sdp(fcb, &msg->msg_body);
+        if (cause != CC_CAUSE_OK) {
+            return (fsmdef_release(fcb, cause, dcb->send_release));
+        }
+    }
+
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                  FSMDEF_CC_CALLER_ID);
+    /*
+     * If DSP is not able to start rx/tx channels, release the call
+     */
+    if (dcb->dsp_out_of_resources == TRUE) {
+        (void)fsmdef_release(fcb, CC_CAUSE_NO_MEDIA, dcb->send_release);
+        cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
+                      NULL);
+        return (SM_RC_END);
+    }
+
+//    fsmdef_update_pd(dcb, FSMDEF_CALL_TYPE_INCOMING);
+
+    /*
+     * Handle media capability changes if there is before transition to
+     * connected state.
+     */
+    return (fsmdef_transition_to_connected(fcb));
+}
+
+/**
+ * The function handles local hold event but not sending any hold request
+ * out to the remote end. The local SDP is updated by the way.
+ *
+ * @param[in]fcb          - pointer to fsm_fcb_t
+ *
+ * @return  SM_RC_END or failrue.
+ *
+ * @pre     (fcb not_eq NULL)
+ */
+static sm_rcs_t
+fsm_hold_local_only (fsm_fcb_t *fcb)
+{
+    static const char fname[] = "fsm_hold_local_only";
+    cc_state_data_t state_data;
+    fsmdef_dcb_t *dcb = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /*
+     * Check local hold status, and allow request if the media is not
+     * locally held.
+     */
+    if (fsmdef_all_media_are_local_hold(dcb)) {
+        /*
+         * a new hold request is not allowed. Ignore the request
+         * but we should still ack the request.
+         */
+        cc_int_feature_ack(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                           dcb->line, CC_FEATURE_HOLD, NULL, CC_CAUSE_NORMAL);
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id, dcb->line,
+                     fname, "already hold");
+
+        return (SM_RC_END);
+    }
+
+    state_data.hold.caller_id = dcb->caller_id;
+    state_data.hold.local     = TRUE;
+
+    /*
+     * Update the SDP so that offer indicates hold. Reinitialize the local
+     * sdp media to include all available codecs. We do this because our local
+     * list has been shortened to the one negotiated codec.
+     */
+    (void)gsmsdp_update_local_sdp_media_capability(dcb, TRUE, TRUE);
+
+    FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
+                 dcb->call_id, dcb->line, fname);
+
+    dcb->spoof_ringout_applied = FALSE;
+
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_HOLD, &state_data);
+
+    /* set all the media to local hold */
+    fsmdef_update_media_hold_status(dcb, NULL, TRUE);
+
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLDING);
+
+    sipsdp_src_dest_free(CCSIP_DEST_SDP_BIT | CCSIP_SRC_SDP_BIT,
+                             &dcb->sdp);
+
+    return (SM_RC_END);
+}
+
+/**
+ * The function handles local hold event. The function also supports
+ * re-sending hold request out such as during a glare condition.
+ *
+ * @param[in]fcb          - pointer to fsm_fcb_t
+ * @param[in]data_p       - pointer to the cc_feature_data_t of the
+ *                          hold feature.
+ * @param[in]resend       - TRUE indicates to resend hold request.
+ *
+ * @return  SM_RC_END or failrue.
+ *
+ * @pre     (fcb not_eq NULL)
+ * @pre     (data_p not_eq NULL)
+ */
+static sm_rcs_t
+fsm_hold_local (fsm_fcb_t *fcb, cc_feature_data_t *data_p,
+                boolean resend)
+{
+    static const char fname[] = "fsm_hold_local";
+    cc_state_data_t state_data;
+    fsmdef_dcb_t   *dcb = fcb->dcb;
+    cc_causes_t     cause;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /*
+     * Check local hold status, and allow request if the media is not
+     * locally held or the caller indicates that to resend the hold
+     * request (such as in glare resolution).
+     */
+    if (!resend && fsmdef_all_media_are_local_hold(dcb)) {
+        /*
+         * a new hold request is not allowed. Ignore the request
+         * but we should still ack the request.
+         */
+        cc_int_feature_ack(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                           dcb->line, CC_FEATURE_HOLD, NULL,
+                           CC_CAUSE_NORMAL);
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id, dcb->line,
+                     fname, "already hold");
+        return (SM_RC_END);
+    }
+
+    state_data.hold.caller_id = dcb->caller_id;
+    state_data.hold.local     = TRUE;
+    state_data.hold.reason    = data_p->hold.call_info.data.hold_resume_reason;
+
+    /* Store hold reason in case we need to resend the hold request due to
+     * request pending response.
+     */
+    dcb->hold_reason = data_p->hold.call_info.data.hold_resume_reason;
+
+    FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
+                 dcb->call_id, dcb->line, fname);
+
+    dcb->spoof_ringout_applied = FALSE;
+
+    fsmdef_get_rtp_stat(dcb, &(data_p->hold.kfactor));
+
+    /* put the call on hold before building the SDP  as DSP
+     * will then be able to give us a full set of codecs
+     * CUCM doesn't like to see a change in codecs on the fly
+     * ( i.e. without going to inactive state ) */
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_HOLD, &state_data);
+
+    /*
+     * Update the SDP so that offer indicates hold. Reinitialize the local
+     * sdp to include all available codecs. We do this because our
+     * local list has been shortened to the one negotiated codec.
+     */
+    (void)gsmsdp_update_local_sdp_media_capability(dcb, TRUE, TRUE);
+
+    /*
+     * Do not expect any msg. body from local hold but free them
+     * just in case before build new SDP body to send out.
+     */
+    cc_free_msg_body_parts(&data_p->hold.msg_body);
+
+    /* Build SDP for sending out */
+    cause = gsmsdp_encode_sdp_and_update_version(dcb, &data_p->hold.msg_body);
+    if (cause != CC_CAUSE_OK) {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        return (fsmdef_release(fcb, cause, dcb->send_release));
+    }
+
+    /* set all the media to local hold */
+    fsmdef_update_media_hold_status(dcb, NULL, TRUE);
+
+    cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                   CC_FEATURE_HOLD, data_p);
+
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLDING);
+
+    sipsdp_src_dest_free(CCSIP_DEST_SDP_BIT | CCSIP_SRC_SDP_BIT,
+                             &dcb->sdp);
+
+    return (SM_RC_END);
+}
+
+/**
+ * Handles hold feature in connected media update pending state.
+ *
+ * @param[in] fcb     The pointer to the fsm_fcb_t structure of this
+ *                    call chain.
+ * @param[in] data_p  pointer to the cc_feature_data_t.
+ *
+ * @return            sm_rsc_t indicates whether the execution of
+ *                    next statmachine to end or to clean up.
+ */
+static sm_rcs_t
+fsm_connected_media_pend_local_hold (fsm_fcb_t *fcb, cc_feature_data_t *data_p)
+{
+    static const char fname[] = "fsm_hold_local_connected_media_pend";
+    fsmdef_dcb_t   *dcb = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /*
+     * Check local hold status, and allow request if the media is not
+     * locally held.
+     */
+    if (fsmdef_all_media_are_local_hold(dcb)) {
+        /*
+         * a new hold request is not allowed. Ignore the request
+         * but we should still ack the request.
+         */
+        cc_int_feature_ack(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                           dcb->line, CC_FEATURE_HOLD, NULL,
+                           CC_CAUSE_NORMAL);
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id, dcb->line,
+                     fname, "already hold");
+        return (SM_RC_END);
+    }
+
+    if (dcb->req_pending_tmr &&
+        cprIsTimerRunning(dcb->req_pending_tmr)) {
+        /*
+         * Request timer is running, that means we are waiting
+         * to re-send media update again due to previously glare.
+         * Since the previous offer has not been accepted, we can
+         * simply just send hold instead when glare resolution timer
+         * expires.
+         */
+
+        /* store the reason to resend when glare timer expires */
+        dcb->hold_reason = data_p->hold.call_info.data.hold_resume_reason;
+        /*
+         * reset feature hold pending flag in case that there are
+         * multiple hold feature received while waiting for
+         * glare resolution to resolve.
+         */
+        FSM_RESET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLD_PENDING);
+        return (SM_RC_END);
+    }
+
+    /*
+     * We have sent media capability update out but have not received
+     * any response yet. The glare condition may occur but we can only
+     * assume that the media update was sent out at this point.
+     * We can not send out any more request until the result is
+     * known. We can not do any thing now but simply remember
+     * to re-send media with the hold feature pending when
+     * the result is known.
+     */
+    FSM_SET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
+    return (SM_RC_END);
+}
+
+/**
+ * common function to handles media feature from remote end.
+ *
+ * @param[in] fcb     The pointer to the fsm_fcb_t structure of this
+ *                    call chain.
+ * @param[in] msg     The pointer to cc_feature_t.
+ *
+ * @return            sm_rsc_t indicates whether the execution of
+ *                    next statmachine to end or to clean up.
+ */
+static sm_rcs_t
+fsmdef_remote_media (fsm_fcb_t *fcb, cc_feature_t *msg)
+{
+    static const char fname[] = "fsmdef_remote_media";
+    fsmdef_dcb_t      *dcb  = fcb->dcb;
+    cc_feature_data_t *data = &(msg->data);
+    cc_feature_data_t  feature_data;
+    cc_causes_t        cause;
+    boolean            send_ack = TRUE;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    memset(&feature_data, 0 , sizeof(cc_feature_data_t));
+    /*
+     * Determine what type of RESUME/MEDIA this is:
+     * 1. third-party control is just trying to change the media -
+     *    midcall-invite with no SDP, so we need to wait for the SDP
+     *    in the SIP ACK before we can truly resume the media.
+     * 2. remote end wants to resume a held call or just a media
+     *    changes.
+     *
+     * We can distinguish between the two because case 1 will not
+     * have any data and case 2 will have data.
+     */
+    if (msg->data_valid == FALSE) {
+        /*
+         * Case 1.
+         *
+         * negotiate offer without SDP will reset all local media entries
+         * to have all codecs included. This is to re-advertise the
+         * capabilities again.
+         */
+         (void) gsmsdp_negotiate_offer_sdp(fcb, NULL, FALSE);
+
+        /*
+         * Update the media direction based on whether each media
+         * stream is locally held or not before sending out the
+         * offer SDP.
+         */
+        fsmdef_set_per_media_local_hold_sdp(dcb);
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_MEDIA,
+                                 NULL);
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_START_RCV,
+                                 NULL);
+    } else {
+        /*
+         * SIP may send MEDIA feature when answer SDP is received in
+         * ACK. The secnario is found when remote resumes and the
+         * resume INVITE is a delayed media INVITE. We sent an offer in
+         * the 200 OK and gets the answer back in the ACK. In this
+         * case, SIP will send MEDIA feature to GSM. We need to check
+         * whether we are waiting for an answer in ACK or not and
+         * use the corresponding offer/answer SDP negotiation function.
+         */
+        if (dcb->remote_sdp_in_ack) {
+            cause = gsmsdp_negotiate_answer_sdp(fcb,
+                                               &data->resume.msg_body);
+            if (cause != CC_CAUSE_OK) {
+                /*
+                 * There is some thing wrong the answer SDP for some
+                 * reason, can not go on.
+                 */
+                FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+                return (fsmdef_release(fcb, cause, dcb->send_release));
+            }
+
+            /*
+             * This is the answer to our previous offer, no need to
+             * to ack to SIP this one.
+             */
+            send_ack = FALSE;
+        } else {
+            /* This is a new offer */
+
+            /*
+             * get k factor to be included in the feature ack. Getting
+             * the k factor needs to be done before maniputate media
+             * stream by the LSM.
+             */
+            fsmdef_media_t *media = gsmsdp_find_audio_media(dcb);
+            if ((media) && (media->direction != SDP_DIRECTION_INACTIVE)) {
+                fsmdef_get_rtp_stat(dcb, &(feature_data.resume.kfactor));
+            }
+
+            cause = gsmsdp_negotiate_offer_sdp(fcb,
+                                               &data->resume.msg_body, FALSE);
+            if (cause != CC_CAUSE_OK) {
+                /*
+                 * Received a sdp that cannot be accepted.
+                 * It should just reject the new sdp offer rather than
+                 * tearing down the call.
+                 */
+                cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                                   dcb->line, msg->feature_id, NULL, cause);
+                return (SM_RC_END);
+            }
+            /*
+             * Update the media based on local hold.
+             */
+            fsmdef_set_per_media_local_hold_sdp(dcb);
+        }
+
+        /*
+         * If spoof ringout is not being requested and we are currently
+         * playing spoof ringout, transition the LSM from the far end alerting
+         * to the connected state.
+         */
+        if ((!dcb->spoof_ringout_requested) && (dcb->spoof_ringout_applied)) {
+            FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
+                         dcb->call_id, dcb->line, fname);
+
+            dcb->spoof_ringout_applied = FALSE;
+            cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                          FSMDEF_CC_CALLER_ID);
+        } else {
+            (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_MEDIA,
+                                 NULL);
+        }
+    }
+
+    if (send_ack) {
+        /* Build SDP from our current SDP */
+        cause = gsmsdp_encode_sdp_and_update_version(dcb, &feature_data.resume.msg_body);
+        if (cause != CC_CAUSE_OK) {
+            FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+            return (fsmdef_release(fcb, cause, dcb->send_release));
+        }
+        cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                           dcb->line, msg->feature_id, &feature_data,
+                           CC_CAUSE_NORMAL);
+    }
+    return (SM_RC_END);
+}
+
+/**
+ *
+ * Function to handles connected state feature events. Function handles
+ * feature events generated by GSM, UI and SIP stack.
+ *
+ * @param sm_event_t        event
+ *
+ * @return  SM_RC_END or SM_RC_CLEANUP
+ *
+ * @pre     (fcb->dcb not_eq NULL)
+ * @pre     (event->data not_eq NULL)
+ * @pre     (event->msg not_eq NULL)
+ */
+static sm_rcs_t
+fsmdef_ev_connected_feature (sm_event_t *event)
+{
+    static const char fname[] = "fsmdef_ev_connected_feature";
+    fsm_fcb_t         *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t      *dcb    = fcb->dcb;
+    cc_feature_t      *msg    = (cc_feature_t *) event->msg;
+    cc_srcs_t          src_id = msg->src_id;
+    cc_features_t      ftr_id = msg->feature_id;
+    cc_feature_data_t *data   = &(msg->data);
+    sm_rcs_t           sm_rc;
+    cc_feature_data_t  feature_data;
+    cc_action_data_t   action_data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_UI:
+    case CC_SRC_GSM:
+        switch (msg->feature_id) {
+        case CC_FEATURE_HOLD:
+            /* If the line number is 0xFF, then this request
+             * came from GSM during a Transfer. We want to
+             * put the call on local hold only. We do not want
+             * to send a cc_feature to the SIP stack because
+             * that will cause an Invite Hold to go via SIP.
+             * We don't want to put the other end on hold, just
+             * ourselves.
+             */
+            if (msg->line == 0xFF) {
+                sm_rc = fsm_hold_local_only(fcb);
+            } else {
+                if (msg->data_valid) {
+                    sm_rc = fsm_hold_local(fcb, data, FALSE);
+                } else {
+                    feature_data.hold.call_info.type = CC_FEAT_HOLD;
+                    feature_data.hold.call_info.data.hold_resume_reason =
+                        CC_REASON_NONE;
+                    feature_data.hold.msg_body.num_parts = 0;
+                    feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
+                    feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
+                    sm_rc = fsm_hold_local(fcb, &feature_data, FALSE);
+                }
+
+            }
+            fsmdef_handle_join_pending(dcb);
+            return (sm_rc);
+
+        case CC_FEATURE_END_CALL:
+            sm_rc = fsmdef_release_call(fcb, msg);
+
+            fsmdef_handle_join_pending(dcb);
+            return (sm_rc);
+
+        case CC_FEATURE_JOIN:
+            /*
+             * Send offhook to the new call that triggers the
+             * completion of the setup of the join in call
+             */
+            fsmdef_ev_join(data);
+            break;
+
+        case CC_FEATURE_SELECT:
+            if (msg->data_valid == FALSE) {
+                fsmdef_select_invoke(dcb, NULL);
+            } else {
+                fsmdef_select_invoke(dcb, data);
+            }
+            return (SM_RC_END);
+
+        case CC_FEATURE_B2B_JOIN:
+            if (msg->data_valid == FALSE) {
+                fsmdef_b2bjoin_invoke(dcb, NULL);
+            } else {
+                fsmdef_b2bjoin_invoke(dcb, data);
+            }
+            return (SM_RC_END);
+
+        case CC_FEATURE_DIRTRXFR:
+        case CC_FEATURE_UNDEFINED:
+            fsm_display_feature_unavailable();
+
+            fsmdef_handle_join_pending(dcb);
+            return (SM_RC_END);
+
+        case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+            dcb->video_pref = data->caps.support_direction;
+            // Force an re-INVITE by mismatching the id
+            dcb->media_cap_tbl->id--;
+            /* FALL THRU */
+        case CC_FEATURE_UPD_MEDIA_CAP:
+            /*
+             * Media capability update request, check to see if
+             * there is any change in media capability and transition
+             * the pending state or stay in the connected state.
+             */
+            sm_rc = fsmdef_transition_to_connected(fcb);
+            return (sm_rc);
+
+        case CC_FEATURE_REQ_PEND_TIMER_EXP:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+
+        default:
+            fsmdef_handle_join_pending(dcb);
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (msg->feature_id) */
+
+        break;
+
+    case CC_SRC_SIP:
+        switch (msg->feature_id) {
+
+        case CC_FEATURE_MEDIA:
+            /*
+             * remote send media update which can be resume or
+             * or just media changes.
+             */
+            sm_rc = fsmdef_remote_media(fcb, msg);
+            return (sm_rc);
+
+        case CC_FEATURE_CALLINFO:
+            fsmdef_update_callinfo(fcb, msg);
+            break;
+
+        case CC_FEATURE_CALL_PRESERVATION:
+            action_data.update_ui.action = CC_UPDATE_CALL_PRESERVATION;
+            (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_UPDATE_UI,
+                                 &action_data);
+            fsm_change_state(fcb, __LINE__, FSMDEF_S_PRESERVED);
+            break;
+        case CC_FEATURE_NOTIFY:
+            fsmdef_ev_notify_feature(msg, dcb);
+            break;
+
+        case CC_FEATURE_UPDATE:
+            /*
+             * We only get an UPDATE feature event if we receive a medialess UPDATE.
+             * This type of event only conveys UI updates that are processed with
+             * a call info event. We do perform one check to see if we are currently
+             * spoofing ringout. If we are and the spoof ringout requested flag
+             * has been cleared, we tell the LSM to go connected.
+             */
+            if ((!dcb->spoof_ringout_requested) && (dcb->spoof_ringout_applied)) {
+                FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
+                             dcb->call_id, dcb->line, fname);
+
+                dcb->spoof_ringout_applied = FALSE;
+                cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                              FSMDEF_CC_CALLER_ID);
+            }
+
+           /*
+            * For chaperone call, we will update call state here, to update
+            * the related key's status.
+            */
+           if(dcb->policy == CC_POLICY_CHAPERONE){
+                cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                              FSMDEF_CC_CALLER_ID);
+           }
+            break;
+
+        case CC_FEATURE_FAST_PIC_UPD:
+
+            vcmMediaControl(CREATE_CALL_HANDLE(dcb->line, dcb->call_id), VCM_MEDIA_CONTROL_PICTURE_FAST_UPDATE);
+
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (msg->feature_id) */
+        break;
+
+    default:
+        fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
+        break;
+    }                           /* switch (src_id) */
+
+    return (SM_RC_END);
+}
+
+/**
+ *
+ * Function to handles connected state media update pending feature.
+ *
+ * @param sm_event_t        event
+ *
+ * @return  SM_RC_END or SM_RC_CLEANUP
+ *
+ * @pre     (fcb->dcb not_eq NULL)
+ * @pre     (event->data not_eq NULL)
+ * @pre     (event->msg not_eq NULL)
+ */
+static sm_rcs_t
+fsmdef_ev_connected_media_pend_feature (sm_event_t *event)
+{
+    fsm_fcb_t         *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t      *dcb    = fcb->dcb;
+    cc_feature_t      *msg    = (cc_feature_t *) event->msg;
+    cc_srcs_t          src_id = msg->src_id;
+    cc_features_t      ftr_id = msg->feature_id;
+    cc_feature_data_t *data   = &(msg->data);
+    sm_rcs_t           sm_rc = SM_RC_END;
+    cc_feature_data_t  feature_data;
+    cc_causes_t        cause;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_UI:
+    case CC_SRC_GSM:
+        switch (msg->feature_id) {
+        case CC_FEATURE_HOLD:
+            /* If the line number is 0xFF, then this request
+             * came from GSM during a Transfer. We want to
+             * put the call on local hold only. We do not want
+             * to send a cc_feature to the SIP stack because
+             * that will cause an Invite Hold to go via SIP.
+             * We don't want to put the other end on hold, just
+             * ourselves.
+             */
+            if (msg->line == 0xFF) {
+                sm_rc = fsm_hold_local_only(fcb);
+            } else {
+                if (msg->data_valid) {
+                    sm_rc = fsm_connected_media_pend_local_hold(fcb, data);
+                } else {
+                    feature_data.hold.call_info.type = CC_FEAT_HOLD;
+                    feature_data.hold.call_info.data.hold_resume_reason =
+                        CC_REASON_NONE;
+                    feature_data.hold.msg_body.num_parts = 0;
+                    feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
+                    feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
+                    sm_rc = fsm_connected_media_pend_local_hold(fcb,
+                                                                &feature_data);
+                }
+            }
+            fsmdef_handle_join_pending(dcb);
+            return (sm_rc);
+
+        case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+            dcb->video_pref = data->caps.support_direction;
+            /* FALL THRU */
+        case CC_FEATURE_UPD_MEDIA_CAP:
+            /* We are already in the media update state */
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            return (SM_RC_END);
+
+        case CC_FEATURE_REQ_PEND_TIMER_EXP:
+            /*
+             * Glare resolution timer expires.
+             */
+            if (FSM_CHK_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING)) {
+                /* There is a hold feature pending, send out hold instead */
+                feature_data.hold.call_info.type = CC_FEAT_HOLD;
+                feature_data.hold.call_info.data.hold_resume_reason =
+                       dcb->hold_reason;
+                feature_data.hold.msg_body.num_parts = 0;
+                feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
+                feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
+                FSM_RESET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
+                return (fsm_hold_local(fcb, &feature_data, FALSE));
+            }
+
+            /*
+             * Check the possible media capbility changes.
+             */
+            (void)gsmsdp_update_local_sdp_media_capability(dcb, FALSE, FALSE);
+            feature_data.resume.call_info.type = CC_FEAT_NONE;
+            feature_data.resume.call_info.data.hold_resume_reason =
+                                                            CC_REASON_NONE;
+            feature_data.resume.msg_body.num_parts = 0;
+            feature_data.resume.call_info.data.call_info_feat_data.swap = FALSE;
+            feature_data.resume.call_info.data.call_info_feat_data.protect = FALSE;
+            /* Encode SDP */
+            cause = gsmsdp_encode_sdp_and_update_version(dcb,
+                                                         &feature_data.resume.msg_body);
+
+            if (cause != CC_CAUSE_OK) {
+                FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+                return(fsmdef_release(fcb, cause, dcb->send_release));
+            }
+
+            /* Send feature request to SIP */
+            cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                           CC_FEATURE_MEDIA, &feature_data);
+            return (SM_RC_END);
+
+        default:
+            /*
+             * The rest of the feature handles the same way as the
+             * connected feature handling.
+             */
+            break;
+        }                       /* switch (msg->feature_id) */
+        break;
+
+    default:
+        /*
+         * The rest of the feature handles the same way as the
+         * connected feature handling.
+         */
+        break;
+    }
+
+    /*
+     * Unhandled features are handled by the normal connected feature
+     */
+    return (fsmdef_ev_connected_feature(event));
+}
+
+/**
+ *
+ * Function to handles connected media update pending state feature ack
+ * events.
+ *
+ * @param sm_event_t        event
+ *
+ * @return  SM_RC_END or SM_RC_CLEANUP
+ *
+ * @pre     (fcb->dcb not_eq NULL)
+ * @pre     (event->data not_eq NULL)
+ * @pre     (event->msg not_eq NULL)
+ */
+static sm_rcs_t
+fsmdef_ev_connected_media_pend_feature_ack (sm_event_t *event)
+{
+    static const char fname[] = "fsmdef_ev_connected_media_pend_feature_ack";
+    fsm_fcb_t        *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t     *dcb    = fcb->dcb;
+    cc_feature_ack_t *msg    = (cc_feature_ack_t *) event->msg;
+    cc_features_t     ftr_id = msg->feature_id;
+    cc_srcs_t         src_id = msg->src_id;
+    cc_feature_data_t feature_data;
+    sm_rcs_t          sm_rc = SM_RC_END;
+    cc_msgbody_info_t *msg_body;
+    cc_causes_t        cause;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+    switch (src_id) {
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_MEDIA:
+            /* Media update feature ack from SIP */
+
+            if (msg->cause == CC_CAUSE_REQUEST_PENDING) {
+                /*
+                 * The glare condition occurs from the previously sent
+                 * media update. Starts a request pending timer so that
+                 * we retry.
+                 */
+                fsmdef_set_req_pending_timer(dcb);
+                if (FSM_CHK_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING)) {
+                    /*
+                     * Feature hold is pending, abort the media capability
+                     * update and retry with hold instead.
+                     */
+                    FSM_RESET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
+                    fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLD_PENDING);
+                }
+                return (SM_RC_END);
+            }
+
+            /* Check for error code reported */
+            if ((msg->cause != CC_CAUSE_NORMAL) &&
+                (msg->cause != CC_CAUSE_OK)) {
+                /* Unable to send media request */
+                GSM_ERR_MSG(get_debug_string(FSMDEF_DBG2),
+                            dcb->call_id, dcb->line, fname,
+                            " Media request failed, cause= ", msg->cause);
+                cc_call_state(dcb->call_id, dcb->line, CC_STATE_UNKNOWN, NULL);
+                return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
+            }
+
+            msg_body = &msg->data.resume.msg_body;
+            cause = gsmsdp_negotiate_answer_sdp(fcb, msg_body);
+            if (cause != CC_CAUSE_OK) {
+                return (fsmdef_release(fcb, cause, dcb->send_release));
+            }
+
+            /*
+             * Check to see if we have a feature request pending
+             */
+            if (FSM_CHK_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING)) {
+                /* There is a hold feature pending, send out hold instead */
+                feature_data.hold.call_info.type = CC_FEAT_HOLD;
+                feature_data.hold.call_info.data.hold_resume_reason =
+                    dcb->hold_reason;
+                feature_data.hold.msg_body.num_parts = 0;
+                feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
+                feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
+                FSM_RESET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
+                sm_rc = fsm_hold_local(fcb, &feature_data, FALSE);
+            } else {
+                /*
+                 * If spoof ringout is not being requested and we are
+                 * currently playing spoof ringout, transition the LSM from
+                 * the far end alerting to the connected state.
+                 */
+                if ((!dcb->spoof_ringout_requested) &&
+                    (dcb->spoof_ringout_applied)) {
+                    FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
+                                 dcb->call_id, dcb->line, fname);
+
+                    dcb->spoof_ringout_applied = FALSE;
+                    cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                                  FSMDEF_CC_CALLER_ID);
+                } else {
+                    (void)cc_call_action(dcb->call_id, dcb->line,
+                                         CC_ACTION_MEDIA, NULL);
+                }
+
+                /*
+                 * Check any media capability changes that might occurs
+                 * while we were in the middle of the previous transaction.
+                 */
+                sm_rc = fsmdef_transition_to_connected(fcb);
+                if (g_dock_undock_event != MEDIA_INTERFACE_UPDATE_NOT_REQUIRED) {
+                    if (is_gsmsdp_media_ip_updated_to_latest(dcb) == TRUE) {
+                        ui_update_media_interface_change(dcb->line, dcb->call_id, MEDIA_INTERFACE_UPDATE_SUCCESSFUL);
+                    } else {
+                        DEF_DEBUG("We must have received another MEDIA_INTERFACE_UPDATE  events "
+                            " while current MEDIA_INTERFACE_UPDATE event is in procoess. Sending re-invite again");
+                        escalateDeescalate();
+                    }
+                }
+            }
+            return (sm_rc);
+
+        default:
+            break;
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    /* call default feature ack handler to take common/default actions*/
+    (void) fsmdef_ev_default_feature_ack(event);
+    return (SM_RC_END);
+}
+
+static sm_rcs_t
+fsmdef_ev_offhook (sm_event_t *event)
+{
+    fsm_fcb_t       *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t    *dcb = fcb->dcb;
+    cc_action_data_t data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /*
+     * User has gone offhook while using the speaker.
+     */
+    data.speaker.on = FALSE;
+    (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_SPEAKER, &data);
+
+    //lsm_set_active_call_id(dcb->call_id);
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_connected_line (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t *dcb = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                  FSMDEF_CC_CALLER_ID);
+
+    /*
+     * Handle media capability changes if there is before transition to
+     * connected state.
+     */
+    return (fsmdef_transition_to_connected(fcb));
+}
+
+
+static sm_rcs_t
+fsmdef_ev_onhook (sm_event_t *event)
+{
+    fsm_fcb_t       *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t    *dcb = fcb->dcb;
+    sm_rcs_t         sm_rc;
+    cc_action_data_t data;
+    int              sdpmode = 0;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /* currently this flag is only set by conference case. It signals
+     * that onhook has been received, do not process it anymore.
+     */
+    if (dcb->onhook_received) {
+        dcb->onhook_received = FALSE;
+        return SM_RC_END;
+    }
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    if (sdpmode) {
+       if(dcb->ice_ufrag)
+               cpr_free(dcb->ice_ufrag);
+
+       if(dcb->ice_pwd)
+               cpr_free(dcb->ice_pwd);
+    }
+
+    /*
+     * If the user presses the ENDCALL softkey for an
+     * incoming call set the release cause to Busy.
+     */
+    if (fcb->state == FSMDEF_S_INCOMING_ALERTING) {
+        sm_rc = fsmdef_release(fcb, CC_CAUSE_BUSY, dcb->send_release);
+    } else  {
+        dcb->early_error_release = FALSE;
+        sm_rc = fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release);
+    }
+
+    if (sm_rc == SM_RC_CLEANUP) {
+        /* This dcb has been cleaned up, do nothing more */
+        return (sm_rc);
+    } else if (fcb->state == FSMDEF_S_HOLDING ||
+               fcb->state == FSMDEF_S_HOLD_PENDING) {
+        data.ringer.on = TRUE;
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_RINGER, &data);
+        sm_rc = SM_RC_END;
+    } else {
+        sm_rc = SM_RC_END;
+    }
+
+    return (sm_rc);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_release (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb = (fsm_fcb_t *) event->data;
+    cc_release_t *msg = (cc_release_t *) event->msg;
+    fsmdef_dcb_t *dcb = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    dcb->send_release = FALSE;
+
+    FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
+
+       if (msg->cause == CC_CAUSE_REMOTE_DISCONN_REQ_PLAYTONE) {
+
+               fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_CALL_FAILED, CC_CAUSE_REMOTE_DISCONN_REQ_PLAYTONE);
+
+               /* what to return for return code */
+               return(SM_RC_SUCCESS);
+       } else {
+               return (fsmdef_release(fcb, msg->cause, dcb->send_release));
+       }
+}
+
+
+static sm_rcs_t
+fsmdef_ev_releasing_release (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb = (fsm_fcb_t *) event->data;
+    cc_release_t *msg = (cc_release_t *) event->msg;
+    fsmdef_dcb_t *dcb = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /* see if the SIP stack has aborted this call early for some reason
+     * If SIP brought this down, we are still offhook on the UI. We
+     * need to ignore it, so that reorder can be played AND when the user
+     * hangs up, then the UI will be driven to a clean state.
+     */
+    if (fcb->dcb->early_error_release == FALSE) {
+
+        cc_int_release_complete(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                                msg->cause, NULL);
+
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_IDLE);
+
+        fsmdef_free_dcb(dcb);
+
+        FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
+
+        fsm_release(fcb, __LINE__, msg->cause);
+
+        return (SM_RC_CLEANUP);
+    } else {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SM_DEFAULT_EVENT));
+        return (SM_RC_END);
+    }
+}
+
+
+static sm_rcs_t
+fsmdef_ev_releasing_feature (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb    = (fsm_fcb_t *) event->data;
+    cc_feature_t *msg    = (cc_feature_t *) event->msg;
+    cc_srcs_t     src_id = msg->src_id;
+    cc_features_t ftr_id = msg->feature_id;
+    cc_causes_t   cause;
+    sm_rcs_t      sm_rc  = SM_RC_END;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (ftr_id) {
+    case CC_FEATURE_END_CALL:
+        cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
+
+        /* Clean up call chain, no release sent */
+        return (fsmdef_release(fcb, cause, FALSE));
+
+    default:
+        fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+        break;
+    }
+
+    return (sm_rc);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_releasing_onhook (sm_event_t *event)
+{
+    fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /* Clean up call chain, no release sent */
+    return (fsmdef_release(fcb, CC_CAUSE_NORMAL, FALSE));
+}
+
+
+static sm_rcs_t
+fsmdef_ev_release_complete (sm_event_t *event)
+{
+    fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (fcb->dcb == NULL) {
+        return (SM_RC_CLEANUP);
+    }
+    /* see if the SIP stack has aborted this call early for some reason
+     * If SIP brought this down, we are still offhook on the UI. We
+     * need to ignore it, so that reorder can be played AND when the user
+     * hangs up, then the UI will be driven to a clean state.
+     */
+    if (fcb->dcb->early_error_release == FALSE) {
+
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_IDLE);
+
+        fsmdef_free_dcb(fcb->dcb);
+
+        fsm_release(fcb, __LINE__,
+                    ((cc_release_complete_t *) (event->msg))->cause);
+
+        return (SM_RC_CLEANUP);
+
+    } else {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SM_DEFAULT_EVENT));
+        return (SM_RC_END);
+    }
+}
+
+static sm_rcs_t
+fsmdef_ev_hold_pending_feature (sm_event_t *event)
+{
+    fsm_fcb_t         *fcb     = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t      *dcb     = fcb->dcb;
+    fsmcnf_ccb_t      *ccb     = NULL;
+    cc_feature_t      *msg     = (cc_feature_t *) event->msg;
+    cc_srcs_t          src_id  = msg->src_id;
+    cc_features_t      ftr_id  = msg->feature_id;
+    callid_t           call_id = msg->call_id;
+    line_t             line    = msg->line;
+    cc_feature_data_t *data = &(msg->data);
+    cc_feature_data_t  feature_data;
+    sm_rcs_t           sm_rc;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case (CC_SRC_UI):
+    case (CC_SRC_GSM):
+        switch (ftr_id) {
+
+        case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+             dcb->video_pref = data->caps.support_direction;
+             break;
+
+        case CC_FEATURE_RESUME:
+            /*
+             * We will not be able to resume this call since we are in the
+             * hold pending state but we can place any other active call
+             * on hold. Find the connected call (if there is one) and place
+             * it on hold but not call if it is involved in a conference.
+             */
+            if (msg->data.resume.cause != CC_CAUSE_CONF) {
+                if (fsmdef_wait_to_start_new_call(TRUE, src_id, call_id, line,
+                                                  CC_FEATURE_RESUME, NULL)) {
+                    ccb = fsmcnf_get_ccb_by_call_id(call_id);
+                    if (ccb != NULL) {
+                        ccb->cnf_ftr_ack = FALSE;
+                    }
+                }
+            }
+            return (SM_RC_END);
+
+        case CC_FEATURE_REQ_PEND_TIMER_EXP:
+            feature_data.hold.call_info.type = CC_FEAT_HOLD;
+            feature_data.hold.call_info.data.hold_resume_reason =
+                dcb->hold_reason;
+            feature_data.hold.msg_body.num_parts = 0;
+            feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
+            feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
+            sm_rc = fsm_hold_local(fcb, &feature_data, TRUE);
+            return sm_rc;
+
+        case CC_FEATURE_END_CALL:
+            sm_rc = fsmdef_release_call(fcb, msg);
+            return (sm_rc);
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    case (CC_SRC_SIP):
+        switch (ftr_id) {
+        case CC_FEATURE_MEDIA:
+            return (fsmdef_remote_media(fcb, msg));
+
+        case CC_FEATURE_CALLINFO:
+            fsmdef_update_callinfo(fcb, msg);
+            break;
+
+        case CC_FEATURE_CALL_PRESERVATION:
+            return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
+
+        case CC_FEATURE_NOTIFY:
+            fsmdef_ev_notify_feature(msg, dcb);
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    default:
+        fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
+
+        break;
+    }                           /* switch (src_id) */
+
+    return (SM_RC_END);
+}
+
+/**
+ * feature ack event handler in hold pending state.
+ *
+ * @param[in] event   Pointer to sm_event_t structure for feature ack event.
+ *
+ * @return            Value of type sm_rcs_t to state machine
+ *
+ * @pre               (event not_eqs NULL) and
+ *                    (event->data not_eqs NULL) and
+ *                    ((fsm_fcb_t *)(event->data)->dcb not_eqs NULL)
+ */
+static sm_rcs_t
+fsmdef_ev_hold_pending_feature_ack (sm_event_t *event)
+{
+    fsm_fcb_t         *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t      *dcb    = fcb->dcb;
+    cc_feature_ack_t  *msg    = (cc_feature_ack_t *) event->msg;
+    cc_srcs_t          src_id = msg->src_id;
+    cc_features_t      ftr_id = msg->feature_id;
+    cc_causes_t        cause;
+    cc_msgbody_info_t *msg_body;
+    cc_feature_data_t  feature_data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case (CC_SRC_SIP):
+        switch (ftr_id) {
+        case CC_FEATURE_RESUME:
+            /*
+             * This is feature ack for resume. We received resume
+             * feature ack because the hold request was received while
+             * we are waiting to resume i.e. was in the
+             * resume pending state and resume request has been sent out.
+             *
+             * If the resume ack indicates glare, then ignore sending the
+             * resume and transition to holding (we were in hold and
+             * unable to send RESUME). Otherwise send hold out right away
+             * if there is no other error.
+             */
+            fsm_sm_ftr(ftr_id, src_id);
+            if (msg->cause == CC_CAUSE_REQUEST_PENDING) {
+                /*
+                 * The glare condition occurs from the previously sent
+                 * resume transition to holding.
+                 */
+                (void)fsm_hold_local_only(fcb);
+                break;
+            }
+
+            /* call default feature ack handler to take common/default actions*/
+            (void) fsmdef_ev_default_feature_ack(event);
+
+            if ((msg->cause != CC_CAUSE_NORMAL) &&
+                (msg->cause != CC_CAUSE_OK)) {
+                cc_call_state(dcb->call_id, dcb->line,
+                              CC_STATE_UNKNOWN, NULL);
+                return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
+            }
+
+            if (msg->data_valid != TRUE) {
+                cc_call_state(dcb->call_id, dcb->line,
+                              CC_STATE_UNKNOWN, NULL);
+                return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
+            }
+
+            msg_body = &msg->data.resume.msg_body;
+            cause = gsmsdp_negotiate_answer_sdp(fcb, msg_body);
+            if (cause != CC_CAUSE_OK) {
+                return(fsmdef_release(fcb, cause, dcb->send_release));
+            }
+
+            /*
+             * HOLD can be sent now.
+             */
+            feature_data.hold.call_info.type = CC_FEAT_HOLD;
+            feature_data.hold.call_info.data.hold_resume_reason =
+                dcb->hold_reason;
+            feature_data.hold.msg_body.num_parts = 0;
+            feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
+            feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
+            fsm_hold_local(fcb, &feature_data, FALSE);
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }
+        break;
+
+    default:
+        fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+        break;
+    }
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_holding_release (sm_event_t *event)
+{
+    cc_release_t *msg = (cc_release_t *) event->msg;
+    fsm_fcb_t    *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t *dcb = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (msg->cause != CC_CAUSE_XFER_LOCAL) {
+        fcb->dcb->send_release = FALSE;
+    }
+
+    FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
+
+    return (fsmdef_release(fcb, msg->cause, fcb->dcb->send_release));
+}
+
+static sm_rcs_t
+fsmdef_ev_holding_onhook (sm_event_t *event)
+{
+    cc_onhook_t  *msg = (cc_onhook_t *) event->msg;
+    fsm_fcb_t    *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t *dcb = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (!(msg->softkey)) {
+        /* Meaning Hangup, ignore, a held call can't be hung up */
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SM_DEFAULT_EVENT));
+        return (SM_RC_END);
+    }
+
+    /*
+     * Meaning EndCall softkey is sent, take down
+     * the call, this happens during failover.
+     */
+    FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
+
+    return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
+}
+
+/**
+  *
+  * fsmdef_reversion_timeout - Triggers LSM for doing Reversion alerts.
+  *
+  * @param fsmdef_dcb_t     dcb for this call
+  *
+  * @return  none
+  *
+  * @pre     (dcb not_eq NULL)
+  */
+
+void fsmdef_reversion_timeout(callid_t call_id)
+{
+
+       int ret = CPR_SUCCESS;
+
+       fsmdef_dcb_t *dcb = fsmdef_get_dcb_by_call_id(call_id) ;
+
+    if ( (dcb == NULL ) || (dcb->fcb == NULL)) {
+           return;
+    }
+
+    // check that we are in HOLDING state before proceeding
+    if ((dcb->fcb->state != FSMDEF_S_HOLDING) &&
+        (dcb->fcb->state != FSMDEF_S_HOLD_PENDING)) {
+        return;
+    }
+
+    if  (dcb->reversionInterval > 0) {
+           ret = cprStartTimer(dcb->revertTimer, dcb->reversionInterval * 1000, (void*)(long)call_id);
+       }
+
+       if ( ret == CPR_FAILURE ) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_START_FAILED),
+              dcb->call_id, dcb->line, "", "Reversion", cpr_errno);
+        return;
+    }
+
+       cc_call_state(dcb->call_id, dcb->line, CC_STATE_HOLD_REVERT, NULL);
+
+}
+
+
+/**
+  *
+  * fsmdef_resume - Performs Resume Operation.
+  *
+  * @param sm_event_t     event
+  *
+  * @return  sm_rcs_t     SM_RC_END - indicating the event has been consumed
+  *
+  * @pre     (event not_eq NULL)
+  */
+
+static void
+fsmdef_resume (sm_event_t *event)
+{
+
+    static const char fname[] = "fsmdef_resume";
+    fsm_fcb_t         *fcb     = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t      *dcb     = fcb->dcb;
+    fsmcnf_ccb_t      *ccb     = NULL;
+    cc_feature_t      *msg     = (cc_feature_t *) event->msg;
+    cc_feature_data_t *data    = &(msg->data);
+    cc_srcs_t          src_id  = msg->src_id;
+    callid_t           call_id = msg->call_id;
+    line_t             line    = msg->line;
+    cc_feature_data_t feature_data;
+    cc_causes_t        cause;
+    boolean            req_pending_tmr_running = FALSE;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /*
+     * Ignore the request if local hold is not active.
+     */
+    if (fsmdef_num_media_in_local_hold(dcb) == 0) {
+        /*
+         * No media in local held, should not happen here.
+         */
+        cc_int_feature_ack(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                           dcb->line, CC_FEATURE_RESUME, NULL,
+                           CC_CAUSE_NORMAL);
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), call_id, dcb->line,
+                     fname, "resume media not in hold state\n");
+        return;
+    }
+
+    (void) cprCancelTimer(dcb->revertTimer);
+       dcb->reversionInterval = -1;
+    /*
+     * Make sure the connected call (if there is one) goes on hold,
+     * but do not hold the connected call if it is involved in a
+     * conference.
+     */
+    if (msg->data.resume.cause != CC_CAUSE_CONF) {
+        if (fsmdef_wait_to_start_new_call(TRUE, src_id, call_id, line,
+                                          CC_FEATURE_RESUME, (msg->data_valid ?
+                                        data : NULL))) {
+
+            ccb = fsmcnf_get_ccb_by_call_id(call_id);
+            if (ccb != NULL) {
+                ccb->cnf_ftr_ack = FALSE;
+            }
+            return ;
+        }
+    }
+
+    /* Clear all media holding status */
+    fsmdef_update_media_hold_status(dcb, NULL, FALSE);
+
+    if (dcb->req_pending_tmr && cprIsTimerRunning(dcb->req_pending_tmr)) {
+        req_pending_tmr_running = TRUE;
+    }
+
+    if (!req_pending_tmr_running) {
+        /*
+         * Reinitialize the local sdp to include all available codecs.
+         * We do this because it is possible that our local list
+         * may have been shortened to the one negotiated codec. This is
+         * the case when we receive an incoming call, we negotiate a
+         * single codec, copy it into the local sdp and send it back
+         * in the connected msg.
+         */
+        (void)gsmsdp_update_local_sdp_media_capability(dcb, TRUE, FALSE);
+
+        if (msg->data_valid) {
+            feature_data.resume.call_info = data->resume.call_info;
+        } else {
+            feature_data.resume.call_info.type = CC_FEAT_RESUME;
+            feature_data.resume.call_info.data.hold_resume_reason =
+                CC_REASON_NONE;
+            feature_data.resume.msg_body.num_parts = 0;
+            feature_data.resume.call_info.data.call_info_feat_data.swap = FALSE;
+            feature_data.resume.call_info.data.call_info_feat_data.protect = FALSE;
+        }
+        /* Encode SDP */
+        cause = gsmsdp_encode_sdp_and_update_version(dcb,
+                                                     &feature_data.resume.msg_body);
+        if (cause != CC_CAUSE_OK) {
+            FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+            (void)fsmdef_release(fcb, cause, dcb->send_release);
+            return ;
+        }
+    }
+
+    /*
+     * If spoof ringout is currently requested, transition to the
+     * far end alerting state instead of the connected state.
+     */
+    if (dcb->spoof_ringout_requested) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
+                     dcb->call_id, dcb->line, fname,
+                     "setting spoof_ringout_applied");
+
+        dcb->spoof_ringout_applied = TRUE;
+        cc_call_state(dcb->call_id, dcb->line,
+                      CC_STATE_FAR_END_ALERTING, FSMDEF_CC_CALLER_ID);
+    } else {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
+                     dcb->call_id, dcb->line, fname);
+
+        dcb->spoof_ringout_applied = FALSE;
+        /* Start receiving but not transmit, before sending resume */
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_START_RCV,
+                             NULL);
+    }
+
+    if (!req_pending_tmr_running) {
+        cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                       CC_FEATURE_RESUME, &feature_data);
+    }
+
+    /*
+     * We lock the UI until we learn the result of the resume request
+     */
+    fim_lock_ui(call_id);
+
+    /* Wait for feature ack */
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_RESUME_PENDING);
+
+       return ;
+
+}
+
+/**
+  *
+  * fsmdef_ev_holding_offhook - Handles offhook in for holding state.
+  *
+  * @param sm_event_t     event
+  *
+  * @return  sm_rcs_t     SM_RC_END - indicating the event has been consumed
+  *
+  * @pre     (event not_eq NULL)
+  */
+
+static sm_rcs_t
+fsmdef_ev_holding_offhook (sm_event_t *event)
+{
+    fsm_fcb_t         *fcb     = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t      *dcb     = fcb->dcb;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (cprIsTimerRunning(dcb->revertTimer)) {
+               // Off hook resume calls only if HR is active else ignore this event
+               fsmdef_resume(event);
+       }
+
+       return  SM_RC_END;
+
+}
+
+static sm_rcs_t
+fsmdef_ev_holding_feature (sm_event_t *event)
+{
+    fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t *dcb = fcb->dcb;
+    cc_feature_t *msg = (cc_feature_t *) event->msg;
+    cc_srcs_t src_id = msg->src_id;
+    cc_features_t ftr_id = msg->feature_id;
+    cc_feature_data_t *data = &(msg->data);
+    cc_feature_data_t feature_data;
+    sm_rcs_t sm_rc;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case (CC_SRC_UI):
+    case (CC_SRC_GSM):
+        switch (ftr_id) {
+
+        case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+             dcb->video_pref = data->caps.support_direction;
+             break;
+
+        case CC_FEATURE_HOLD:
+            if (msg->data_valid) {
+                sm_rc = fsm_hold_local(fcb, data, FALSE);
+            } else {
+                feature_data.hold.call_info.type = CC_FEAT_HOLD;
+                feature_data.hold.call_info.data.hold_resume_reason =
+                    CC_REASON_NONE;
+                feature_data.hold.msg_body.num_parts = 0;
+                feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
+                feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
+                sm_rc = fsm_hold_local(fcb, &feature_data, FALSE);
+            }
+            fsmdef_handle_join_pending(dcb);
+            return (sm_rc);
+
+        case CC_FEATURE_HOLD_REVERSION:
+            // Stop the timer for alert interval
+            (void) cprCancelTimer(dcb->revertTimer);
+            dcb->reversionInterval = -1;
+
+                       // Do not revert if alertInterval is negative
+            if ( data->hold_reversion.alertInterval < 0 )
+               return SM_RC_END;
+                       // interval timer of 0 implies continue to revert
+                       // Clamp the interval to be in MIN/MAX_HOLD_REVERSION_INTERVAL_TIMER
+            if ( data->hold_reversion.alertInterval  > 0  &&
+                                data->hold_reversion.alertInterval < MIN_HOLD_REVERSION_INTERVAL_TIMER )
+               data->hold_reversion.alertInterval = MIN_HOLD_REVERSION_INTERVAL_TIMER;
+
+            if ( data->hold_reversion.alertInterval > MAX_HOLD_REVERSION_INTERVAL_TIMER )
+               data->hold_reversion.alertInterval = MAX_HOLD_REVERSION_INTERVAL_TIMER;
+
+            dcb->reversionInterval = data->hold_reversion.alertInterval ;
+
+            fsmdef_reversion_timeout(fcb->dcb->call_id);
+
+            fsmdef_handle_join_pending(dcb);
+            return SM_RC_END;
+
+        case CC_FEATURE_RESUME:
+            fsmdef_resume(event);
+            break;
+
+        case CC_FEATURE_END_CALL:
+            sm_rc = fsmdef_release_call(fcb, msg);
+            (void) cprCancelTimer(dcb->revertTimer);
+            dcb->reversionInterval = -1;
+            fsmdef_handle_join_pending(dcb);
+            return (sm_rc);
+
+        case CC_FEATURE_SELECT:
+            if (msg->data_valid == FALSE) {
+                fsmdef_select_invoke(dcb, NULL);
+            } else {
+                fsmdef_select_invoke(dcb, data);
+            }
+            return (SM_RC_END);
+
+        case CC_FEATURE_B2B_JOIN:
+            if (msg->data_valid == FALSE) {
+                fsmdef_b2bjoin_invoke(dcb, NULL);
+            } else {
+                fsmdef_b2bjoin_invoke(dcb, data);
+            }
+            return (SM_RC_END);
+
+        case CC_FEATURE_DIRTRXFR:
+        case CC_FEATURE_UNDEFINED:
+            fsm_display_feature_unavailable();
+
+            fsmdef_handle_join_pending(dcb);
+            return (SM_RC_END);
+
+        case CC_FEATURE_REQ_PEND_TIMER_EXP:
+        /* Ignore the request pending timer when in holding state */
+        default:
+            fsmdef_handle_join_pending(dcb);
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    case (CC_SRC_SIP):
+        switch (ftr_id) {
+        case CC_FEATURE_MEDIA:
+            return (fsmdef_remote_media(fcb, msg));
+
+        case CC_FEATURE_CALLINFO:
+            fsmdef_update_callinfo(fcb, msg);
+            break;
+
+        case CC_FEATURE_CALL_PRESERVATION:
+            (void) cprCancelTimer(dcb->revertTimer);
+            dcb->reversionInterval = -1;
+            return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
+
+        case CC_FEATURE_NOTIFY:
+            fsmdef_ev_notify_feature(msg, dcb);
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    default:
+        fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
+
+        break;
+    }                           /* switch (src_id) */
+
+    return (SM_RC_END);
+}
+
+
+static sm_rcs_t
+fsmdef_ev_holding_feature_ack (sm_event_t *event)
+{
+    static const char fname[] = "fsmdef_ev_holding_feature_ack";
+    fsm_fcb_t        *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t     *dcb    = fcb->dcb;
+    cc_feature_ack_t *msg    = (cc_feature_ack_t *) event->msg;
+    cc_srcs_t         src_id = msg->src_id;
+    cc_features_t     ftr_id = msg->feature_id;
+    cc_causes_t       cause  = msg->cause;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    switch (src_id) {
+    case (CC_SRC_SIP):
+        switch (ftr_id) {
+        case CC_FEATURE_HOLD:
+            if (cause == CC_CAUSE_REQUEST_PENDING) {
+                /*
+                 * If this is feature ack for hold and request is pending,
+                 * set a request pending timer so that we retry the hold
+                 * feature request.
+                 */
+                fsmdef_set_req_pending_timer(dcb);
+                fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLD_PENDING);
+                return (SM_RC_END);
+            }
+
+            /* Check for error code reported */
+            if ((cause != CC_CAUSE_NORMAL) &&
+                (cause != CC_CAUSE_OK)) {
+                /* Unable to send hold request */
+                GSM_ERR_MSG(get_debug_string(FSMDEF_DBG2),
+                            dcb->call_id, dcb->line, fname,
+                            "HOLD request failed, cause= ", cause);
+                cc_call_state(dcb->call_id, dcb->line, CC_STATE_UNKNOWN, NULL);
+                return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
+            }
+            // update video_avail as we are not negotiating the answer below
+            dcb->cur_video_avail = SDP_DIRECTION_INACTIVE;
+            lsm_update_video_avail(dcb->line, dcb->call_id, dcb->cur_video_avail);
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }
+        break;
+
+    default:
+        fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+        break;
+    }
+
+    /* call default feature ack handler to take common/default actions */
+    (void) fsmdef_ev_default_feature_ack(event);
+
+    return (SM_RC_END);
+}
+
+static sm_rcs_t
+fsmdef_ev_resume_pending_feature (sm_event_t *event)
+{
+    static const char fname[] = "fsmdef_ev_resume_pending_feature";
+    fsm_fcb_t         *fcb     = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t      *dcb     = fcb->dcb;
+    cc_feature_t      *msg     = (cc_feature_t *) event->msg;
+    cc_srcs_t          src_id  = msg->src_id;
+    cc_features_t      ftr_id  = msg->feature_id;
+    callid_t           call_id = msg->call_id;
+    cc_feature_data_t *data    = &(msg->data);
+    cc_feature_data_t  feature_data;
+    sm_rcs_t           sm_rc;
+    cc_causes_t        cause;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case (CC_SRC_UI):
+        switch (ftr_id) {
+        case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
+             dcb->video_pref = data->caps.support_direction;
+             break;
+
+        case CC_FEATURE_END_CALL:
+            fim_unlock_ui(call_id);
+            sm_rc = fsmdef_release_call(fcb, msg);
+            return (sm_rc);
+
+        case CC_FEATURE_HOLD:
+            /*
+             * The UI should be locked but the hold event from UI here
+             * can be the result of other call chain wants to put this
+             * other call that may be in conected state or in resume
+             * pending state on hold. One example, the user want to
+             * resume a call that is on hold, that call chain will look
+             * for any other call in connected state or call in resume
+             * pending (about to be connected) and puts this call on hold.
+             *
+             * There are 2 choices here. If we are in this state and has
+             * not sent a resume out to the network, then simply
+             * transition to holding state. If resume has already
+             * been sent and we are here waiting for resume pending ack,
+             * then we can not send out hold immediately. In the later
+             * case, go to hold pending state and wait for resume
+             * feature ack to send hold out.
+             *
+             * In either case, UI can be unlocked since we are now
+             * about to hold again.
+             */
+            fim_unlock_ui(dcb->call_id);
+            if (dcb->req_pending_tmr &&
+                cprIsTimerRunning(dcb->req_pending_tmr)) {
+                /*
+                 * Request timer is running, that means we are waiting
+                 * to send resume or we already have sent one out
+                 * but it resulted in glare condition and that we are waiting
+                 * to resent it again. In this, just go back to hold state.
+                 */
+                (void) cprCancelTimer(dcb->req_pending_tmr);
+
+                FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
+                             dcb->call_id, dcb->line, fname,
+                             "Received Hold while waiting to send resume\n");
+
+                /* Go back to holding without sending any thing out */
+                (void)fsm_hold_local_only(fcb);
+            } else {
+                /*
+                 * We have sent resume to the network and wait for
+                 * for the feature ack. The glare condition can
+                 * occur but we can only assume that resume was sent out
+                 * at this point. We can not send out any more request
+                 * until the result is known.
+                 */
+                if (msg->data_valid) {
+                    dcb->hold_reason =
+                         data->hold.call_info.data.hold_resume_reason;
+                } else {
+                    dcb->hold_reason = CC_REASON_NONE;
+                }
+                // since we are going to hold stop the media now
+                // else the next new call or resume will result in 2 sets of media ports open
+                (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_MEDIA,
+                                 NULL);
+                fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLD_PENDING);
+            }
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }
+        break;
+
+    case (CC_SRC_GSM):
+        switch (ftr_id) {
+
+        case CC_FEATURE_HOLD:
+            sm_rc = fsm_hold_local_only(fcb);
+            fim_unlock_ui(call_id);
+            return (sm_rc);
+
+        case CC_FEATURE_END_CALL:
+            fim_unlock_ui(call_id);
+            sm_rc = fsmdef_release_call(fcb, msg);
+            return (sm_rc);
+
+        case CC_FEATURE_REQ_PEND_TIMER_EXP:
+
+            /*
+             * Reinitialize the local sdp to include all available codecs.
+             * We do this because it is possible that our local list
+             * may have been shortened to the one negotiated codec. This is
+             * the case when we receive an incoming call, we negotiate a
+             * single codec, copy it into the local sdp and send it back
+             * in the connected msg.
+             */
+            (void)gsmsdp_update_local_sdp_media_capability(dcb, TRUE, FALSE);
+
+            feature_data.resume.call_info.type = CC_FEAT_RESUME;
+            feature_data.resume.call_info.data.hold_resume_reason =
+                CC_REASON_NONE;
+            feature_data.resume.msg_body.num_parts = 0;
+            feature_data.resume.call_info.data.call_info_feat_data.swap = FALSE;
+            feature_data.resume.call_info.data.call_info_feat_data.protect = FALSE;
+            /* Encode SDP */
+            cause = gsmsdp_encode_sdp_and_update_version(dcb, &feature_data.resume.msg_body);
+            if (cause != CC_CAUSE_OK) {
+                FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+                return (fsmdef_release(fcb, cause, dcb->send_release));
+            }
+
+            /*
+             * We lock the UI until we learn the result of the resume request.
+             */
+            fim_lock_ui(call_id);
+
+            cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                           CC_FEATURE_RESUME, &feature_data);
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    case (CC_SRC_SIP):
+        switch (ftr_id) {
+        case CC_FEATURE_MEDIA:
+            return (fsmdef_remote_media(fcb, msg));
+
+        case CC_FEATURE_CALLINFO:
+            fsmdef_update_callinfo(fcb, msg);
+            break;
+
+        case CC_FEATURE_CALL_PRESERVATION:
+            fim_unlock_ui(call_id);
+            return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
+
+        case CC_FEATURE_NOTIFY:
+            fsmdef_ev_notify_feature(msg, dcb);
+            break;
+
+        case CC_FEATURE_UPDATE:
+            /*
+             * We only get an UPDATE feature event if we receive a medialess UPDATE.
+             * This type of event only conveys UI updates that are processed with
+             * a call info event. We do perform one check to see if we are currently
+             * spoofing ringout. If we are and the spoof ringout requested flag
+             * has been cleared, we tell the LSM to go connected which halts the
+             * spoofed ringout.
+             */
+            if ((!dcb->spoof_ringout_requested) && (dcb->spoof_ringout_applied)) {
+                FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
+                             dcb->call_id, dcb->line, fname);
+
+                dcb->spoof_ringout_applied = FALSE;
+                cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                              FSMDEF_CC_CALLER_ID);
+            }
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    default:
+        fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
+        break;
+    }                           /* switch (src_id) */
+
+    return (SM_RC_END);
+}
+
+/**
+ * feature ack event handler in resume pending state.
+ *
+ * @param[in] event   Pointer to sm_event_t structure for feature ack event.
+ *
+ * @return            Value of type sm_rcs_t to state machine
+ *
+ * @pre               (event not_eqs NULL) and
+ *                    (event->data not_eqs NULL) and
+ *                    ((fsm_fcb_t *)(event->data)->dcb not_eqs NULL)
+ */
+static sm_rcs_t
+fsmdef_ev_resume_pending_feature_ack (sm_event_t *event)
+{
+    static const char fname[] = "fsmdef_ev_resume_pending_feature_ack";
+    fsm_fcb_t         *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t      *dcb    = fcb->dcb;
+    cc_feature_ack_t  *msg    = (cc_feature_ack_t *) event->msg;
+    cc_srcs_t          src_id = msg->src_id;
+    cc_features_t      ftr_id = msg->feature_id;
+    cc_causes_t        cause;
+    cc_msgbody_info_t *msg_body;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case (CC_SRC_SIP):
+        switch (ftr_id) {
+         case CC_FEATURE_HOLD:
+             /*
+              * This can occur because SIP stack has not sent HOLD out yet
+              * but the user has pressed resume since we already moved
+              * to HOLD state as soon as the user pressed HOLD. Ignore the
+              * HOLD feature ACK except if it indicates error. If we get
+              * REQUEST_PENDING, we'll just move to connected state.
+              */
+             if (msg->cause == CC_CAUSE_REQUEST_PENDING) {
+                cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                              FSMDEF_CC_CALLER_ID);
+                fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED);
+                return (SM_RC_END);
+             }
+             if ((msg->cause != CC_CAUSE_NORMAL) &&
+                 (msg->cause != CC_CAUSE_OK)) {
+                 cc_call_state(dcb->call_id, dcb->line,
+                               CC_STATE_UNKNOWN, NULL);
+                 return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
+             }
+             fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+             break;
+
+        case CC_FEATURE_RESUME:
+            /*
+             * If this is feature ack for resume and request is pending,
+             * set a request pending timer so that we retry the resume
+             * feature request. Otherwise, unlock the UI and continue
+             * processing.
+             */
+            fsm_sm_ftr(ftr_id, src_id);
+            if ( msg->cause == CC_CAUSE_REQUEST_PENDING ) {
+               fsmdef_set_req_pending_timer(dcb);
+                return (SM_RC_END);
+            } else {
+                fim_unlock_ui(dcb->call_id);
+            }
+            /* call default feature ack handler to take common/default actions*/
+            (void) fsmdef_ev_default_feature_ack(event);
+
+            if ((msg->cause == CC_CAUSE_SERV_ERR_UNAVAIL) &&
+                (dcb->hold_reason == CC_REASON_MONITOR_UPDATE)) {
+                FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
+                             dcb->call_id, dcb->line, fname,
+                             "msg->cause == CC_CAUSE_SERV_ERR_UNAVAIL, unable to monitor update\n");
+                return (fsmdef_transition_to_connected(fcb));
+            }
+
+            if ((msg->cause != CC_CAUSE_NORMAL) &&
+                (msg->cause != CC_CAUSE_OK)) {
+                cc_call_state(dcb->call_id, dcb->line,
+                              CC_STATE_UNKNOWN, NULL);
+                return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
+            }
+
+            if (msg->data_valid != TRUE) {
+                cc_call_state(dcb->call_id, dcb->line,
+                              CC_STATE_UNKNOWN, NULL);
+                return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
+            }
+
+            msg_body = &msg->data.resume.msg_body;
+            cause = gsmsdp_negotiate_answer_sdp(fcb, msg_body);
+            if (cause != CC_CAUSE_OK) {
+                return (fsmdef_release(fcb, cause, dcb->send_release));
+            }
+            if (!dcb->spoof_ringout_applied) {
+                cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                          FSMDEF_CC_CALLER_ID);
+            }
+            /*
+             * Handle media capability changes if there is before transition
+             * to connected state to catch any changes during resume
+             * transaction.
+             */
+            return (fsmdef_transition_to_connected(fcb));
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }
+        break;
+
+    default:
+        fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+        break;
+    }
+
+    /* call default feature ack handler to take common/default actions */
+    (void) fsmdef_ev_default_feature_ack(event);
+
+    return (SM_RC_END);
+}
+
+static sm_rcs_t
+fsmdef_ev_preserved_feature (sm_event_t *event)
+{
+    fsm_fcb_t         *fcb    = (fsm_fcb_t *) event->data;
+    cc_feature_t      *msg    = (cc_feature_t *) event->msg;
+    cc_srcs_t          src_id = msg->src_id;
+    cc_features_t      ftr_id = msg->feature_id;
+    sm_rcs_t           sm_rc;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    switch (src_id) {
+    case CC_SRC_UI:
+    case CC_SRC_GSM:
+        switch (msg->feature_id) {
+
+        case CC_FEATURE_END_CALL:
+            sm_rc = fsmdef_release_call(fcb, msg);
+            return (sm_rc);
+
+        default:
+            fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
+            break;
+        }
+
+        break;
+
+    case CC_SRC_SIP:
+        switch (msg->feature_id) {
+        case CC_FEATURE_HOLD:
+        case CC_FEATURE_RESUME:
+            sm_rc = fsmdef_release_call(fcb, msg);
+            return (sm_rc);
+
+        case CC_FEATURE_MEDIA:
+            sm_rc = fsmdef_remote_media(fcb, msg);
+            return (sm_rc);
+
+        case CC_FEATURE_CALLINFO:
+            fsmdef_update_callinfo(fcb, msg);
+            break;
+
+        default:
+            fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (msg->feature_id) */
+
+        break;
+
+    default:
+        fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
+        break;
+    }                           /* switch (src_id) */
+
+    return (SM_RC_END);
+}
+
+cc_int32_t
+fsmdef_show_cmd (cc_int32_t argc, const char *argv[])
+{
+    fsmdef_dcb_t *dcb;
+    fsm_fcb_t    *fcb;
+    int           i = 0;
+    callid_t      call_id;
+    unsigned long strtoul_result;
+    char *strtoul_end;
+
+    /*
+     * Check if need help.
+     */
+    if ((argc == 2) && (argv[1][0] == '?')) {
+        debugif_printf("show fsmdef [all|rel]\n");
+    } else if ((argc == 1) || (strcmp(argv[1], "all") == 0)) {
+        debugif_printf("\n-------- FSMDEF dcbs --------");
+        debugif_printf("\ni   call_id  dcb         line");
+        debugif_printf("\n-----------------------------\n");
+
+        /*
+         * Print info for all dcbs.
+         */
+        FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+            debugif_printf("%-2d  %-7d  0x%8p  %-4d\n",
+                           i++, dcb->call_id, dcb, dcb->line);
+        }
+    } else if (strcmp(argv[1], "rel") == 0) {
+        errno = 0;
+        strtoul_result = strtoul(argv[2], &strtoul_end, 10);
+
+        if (errno || argv[2] == strtoul_end || strtoul_result > USHRT_MAX) {
+            debugif_printf("%s parse error of call_id %s", __FUNCTION__, argv[2]);
+            return 0;
+        }
+
+        call_id = (callid_t) strtoul_result;
+
+        debugif_printf("\nDEF %-4d/%d: releasing\n", call_id, 0);
+
+        fcb = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+        if (fcb == NULL) {
+            return (0);
+        }
+
+        (void)fsmdef_release(fcb, CC_CAUSE_NORMAL, fcb->dcb->send_release);
+    }
+    return (0);
+}
+
+/**
+ *
+ * This function is called to determine if the phone
+ * is in a state where autoAnswer is supported. Even
+ * if the feature is enabled on the line being called,
+ * the phone may be in a state that does not allow it.
+ *
+ * @param autoAnswerAlt   to indicate what states auto answer enabled.
+ * @param myCallId     call_id
+ *
+ * @return  TRUE or FALSE
+ *
+ */
+static int
+fsmdef_check_auto_answer_allowed (int autoAnswerAlt, callid_t myCallId)
+{
+    fsmdef_dcb_t *dcb;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (dcb->call_id != myCallId && dcb->fcb != NULL) {
+            /*
+             * autoAnswer is disabled if there is an incoming or an
+             * outgoing call that has not reached the connected state
+             * regardless of the value of autoAnswerAlt.
+             */
+            if ((dcb->fcb->state == FSMDEF_S_COLLECT_INFO)        ||
+                (dcb->fcb->state == FSMDEF_S_CALL_SENT)           ||
+                (dcb->fcb->state == FSMDEF_S_OUTGOING_PROCEEDING) ||
+                (dcb->fcb->state == FSMDEF_S_KPML_COLLECT_INFO)   ||
+                (dcb->fcb->state == FSMDEF_S_CONNECTING)          ||
+                (dcb->fcb->state == FSMDEF_S_JOINING)) {
+                return (FALSE);
+            }
+
+            /*
+             * If autoAnswerAlt is 1, then autoAnswer is also
+             * disabled if there is a connected call or a call
+             * on hold.
+             */
+            if (autoAnswerAlt == 1) {
+                if ((dcb->fcb->state == FSMDEF_S_CONNECTED) ||
+                    (dcb->fcb->state == FSMDEF_S_PRESERVED) ||
+                    (dcb->fcb->state == FSMDEF_S_RESUME_PENDING) ||
+                    (dcb->fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND) ||
+                    (dcb->fcb->state == FSMDEF_S_HOLDING) ||
+                    (dcb->fcb->state == FSMDEF_S_OUTGOING_ALERTING) ||
+                    (dcb->fcb->state == FSMDEF_S_INCOMING_ALERTING) ||
+                    (dcb->fcb->state == FSMDEF_S_HOLD_PENDING)) {
+
+                    return (FALSE);
+                }
+            } else if (autoAnswerAlt == 0) {
+                if ((dcb->fcb->state == FSMDEF_S_CONNECTED) ||
+                    (dcb->fcb->state == FSMDEF_S_PRESERVED) ||
+                    (dcb->fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND) ||
+                    (dcb->fcb->state == FSMDEF_S_OUTGOING_ALERTING)) {
+
+                    return (FALSE);
+                }
+            }
+        }
+    }
+
+    return (TRUE);
+}
+
+/**
+ *
+ * This function is called when a call is received on a
+ * line that has auto answer enabled and the autoAnswer
+ * timer has expired.
+ *
+ * @param data   pointer to opaque data
+ *
+ * @return  none
+ *
+ * @pre     (fcb and fcb->dcb not_eq NULL)
+ * @pre     (data should be fcb data)
+ */
+
+void
+fsmdef_auto_answer_timeout (void *data)
+{
+    static const char fname[] = "fsmdef_auto_answer_timeout";
+    int        autoAnswerAlternate = 0;
+    int        autoAnswerOverride = 0;
+    int        headSetActive = 0;
+    int        speakerEnabled = 1;
+    callid_t   call_id;
+    char       autoAnswerMode[MAX_LINE_AUTO_ANS_MODE_SIZE];
+    fsmdef_dcb_t *dcb;
+
+    call_id = (callid_t)(long)data;
+    if (call_id == CC_NO_CALL_ID) {
+        /* Invalid call id */
+        GSM_ERR_MSG(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "invalid data");
+        return;
+    }
+
+    /* Retrieve dcb from call id */
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+    if (dcb == NULL) {
+        /*
+         * The only way this should happen is for the
+         * call to be released without cancelling the
+         * autoAnswer timer.
+         */
+        FSM_DEBUG_SM(DEB_F_PREFIX"AutoAnswer timer expired but no dcb was found.\n", DEB_F_PREFIX_ARGS(FSM, fname));
+        return;
+    }
+
+    /*
+     * The device-based config parameter autoAnswerIdleAlternate
+     * determines what state the phone must be in for autoAnswer
+     * feature to be enabled.
+     */
+    config_get_value(CFGID_AUTOANSWER_IDLE_ALTERNATE, &autoAnswerAlternate,
+                     sizeof(autoAnswerAlternate));
+
+    if (fsmdef_check_auto_answer_allowed(autoAnswerAlternate, dcb->call_id)) {
+        /*
+         * Is headset active? No need to check if headset has been disabled
+         * on the phone because the headset will never be active if it has.
+         */
+        headSetActive = platGetAudioDeviceStatus(VCM_AUDIO_DEVICE_HEADSET);
+
+        /*
+         * If function call fails, just assume headset is not active
+         * and drive on
+         */
+        if (headSetActive == -1) {
+            FSM_DEBUG_SM(DEB_F_PREFIX"platGetAudioDeviceStatus() for headset failed.\n", DEB_F_PREFIX_ARGS(FSM, fname));
+            headSetActive = 0;
+        }
+
+        /*
+         * Determine where the audio should be sent
+         */
+        config_get_line_string(CFGID_LINE_AUTOANSWER_MODE, autoAnswerMode,
+                               dcb->line, sizeof(autoAnswerMode));
+
+        /*
+         * Check to see if the speaker has been disabled
+         */
+        config_get_value(CFGID_SPEAKER_ENABLED, &speakerEnabled,
+                            sizeof(speakerEnabled));
+
+        /*
+         * autoAnswer using speaker
+         */
+        if (strcasestr(autoAnswerMode, "speaker")) {
+            /*
+             * Speaker has not been disabled, normal processing.
+             */
+            if (speakerEnabled) {
+                /*
+                 * Enable audio path to speaker if headset is not being used.
+                 */
+                if (!headSetActive) {
+                    platSetSpeakerMode(TRUE);
+                }
+                /* Send offhook event to GSM */
+                cc_int_feature(CC_SRC_UI, CC_SRC_GSM, dcb->call_id,
+                               dcb->line, CC_FEATURE_ANSWER, NULL);
+            } else {
+
+               /*
+                * Speaker is disabled, but autoAnswer is enabled and says to
+                * use the speaker. Check the device-based override
+                * parameter to see what action to take. If override is
+                * set just return and let normal call processing happen.
+                * If not, terminate the call. The best SIP response we
+                * could come up in this situation is to send a 480
+                * Temporarily Unavailable.
+                */
+                config_get_value(CFGID_AUTOANSWER_OVERRIDE, &autoAnswerOverride,
+                                 sizeof(autoAnswerOverride));
+                if (!autoAnswerOverride) {
+                    fsmdef_end_call(dcb, CC_TEMP_NOT_AVAILABLE);
+                }
+
+            }
+        } else if (strcasestr(autoAnswerMode, "headset")) {
+            /*
+             * autoAnswer using headset. If headset is not enabled just let
+             * the phone ring normally.
+             */
+            if (headSetActive) {
+                /* Send offhook event to GSM */
+                cc_int_feature(CC_SRC_UI, CC_SRC_GSM, dcb->call_id,
+                               dcb->line, CC_FEATURE_ANSWER, NULL);
+            }
+        } else {
+            /*
+             * Unknown autoAnswer mode
+             */
+            FSM_DEBUG_SM(DEB_F_PREFIX"Unknown autoAnswer Mode: %s  AutoAnswer is disabled.\n",
+                         DEB_F_PREFIX_ARGS(FSM, fname), autoAnswerMode);
+        }
+    }
+}
+
+/*
+ * fsmdef_b2bjoin_invoke
+ *
+ * Description:
+ *    This function implements the b2bjoin feature invocation.
+ *
+ * Parameters:
+ *    fsmdef_dcb_t *dcb - dcb associated with this call
+ *
+ * Returns: None
+ */
+static void
+fsmdef_b2bjoin_invoke (fsmdef_dcb_t *dcb, cc_feature_data_t *join_data)
+{
+    cc_feature_data_t feature_data;
+    int join_across_lines;
+    cc_uint32_t major_ver;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    // Disable Join if the CUCM doesn't support it
+    platGetSISProtocolVer(&major_ver, NULL, NULL, NULL);
+
+    if ( major_ver < SIS_PROTOCOL_MAJOR_VERSION_UNISON ) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id, dcb->line,
+            "fsmdef_b2bjoin_invoke", "Major sis is small than SIS_PROTOCOL_MAJOR_VERSION_UNISON, so, B2BJOIN is disabled");
+        fsm_display_feature_unavailable();
+        fsmdef_sm_ignore_ftr(dcb->fcb, __LINE__, CC_FEATURE_B2B_JOIN);
+        return;
+    }
+
+    config_get_value(CFGID_JOIN_ACROSS_LINES,
+                     &join_across_lines, sizeof(join_across_lines));
+    /*
+     * Invoke B2BJoin feature towards SIP. It will send a REFER to CCM
+     */
+    if (join_data) {
+        cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                       dcb->line, CC_FEATURE_B2B_JOIN, join_data);
+    } else {
+
+        if ((g_b2bjoin_pending == FALSE) && (dcb->fcb->state == FSMDEF_S_HOLDING)
+            && ((fsmdef_get_connected_call() != NULL) ||
+                (fsmdef_get_alertingout_call() != NULL))) {
+             /* Single Button Join case
+              * If Join is pressed on a held call while there is
+              * an active(connected or alerting) call, that completes the Join
+              */
+             feature_data.b2bjoin.b2bjoin_callid = dcb->call_id;
+             feature_data.b2bjoin.b2bjoin_joincallid = dcb->call_id;
+             cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                            dcb->line, CC_FEATURE_B2B_JOIN, &feature_data);
+             return;
+        }
+
+        if ((g_numofselected_calls == 0) ||
+            ((g_b2bjoin_pending == FALSE) && (join_across_lines == JOIN_ACROSS_LINES_DISABLED) &&
+            (fsmdef_are_there_selected_calls_onotherline(dcb->line) == TRUE))) {
+            dcb->active_feature =  CC_FEATURE_B2B_JOIN;
+            feature_data.select.select = TRUE;
+            fsmdef_select_invoke(dcb,&feature_data);
+            fsm_display_use_line_or_join_to_complete();
+            return;
+        }
+        if (g_b2bjoin_pending) {
+                       if (join_across_lines == JOIN_ACROSS_LINES_DISABLED) {
+                           if (fsmdef_are_join_calls_on_same_line(dcb->line) == FALSE) {
+
+                               fsm_display_use_line_or_join_to_complete();
+                               g_b2bjoin_pending = FALSE;
+                               g_b2bjoin_callid  = CC_NO_CALL_ID;
+                               return;
+                           }
+                       }
+                       if (dcb->call_id== g_b2bjoin_callid) {
+                           /* If join is pending, pressing Join on the same call will cancel it */
+                           g_b2bjoin_pending = FALSE;
+                           g_b2bjoin_callid  = CC_NO_CALL_ID;
+                           /* Remove the check mark from it*/
+                           cc_int_feature(CC_SRC_UI, CC_SRC_GSM, dcb->call_id,
+                                          dcb->line, CC_FEATURE_SELECT, NULL);
+                           return;
+                       }
+                       feature_data.b2bjoin.b2bjoin_callid = dcb->call_id;
+                        if( g_b2bjoin_callid == CC_NO_CALL_ID ){
+                            feature_data.b2bjoin.b2bjoin_joincallid = dcb->call_id;
+                        }
+                        else{
+                            feature_data.b2bjoin.b2bjoin_joincallid = g_b2bjoin_callid;
+                        }
+
+                       cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                                      dcb->line, CC_FEATURE_B2B_JOIN, &feature_data);
+
+        } else {
+                  if ((g_numofselected_calls == 1) && (dcb->selected)) {
+                    /*
+                     * If this is only one selected call, pressing
+                     * Join on it will start a new join operation
+                     */
+                           g_b2bjoin_pending = TRUE;
+                           g_b2bjoin_callid  = dcb->call_id;
+                           fsm_display_use_line_or_join_to_complete();
+                           return;
+                   }
+                       feature_data.b2bjoin.b2bjoin_callid = dcb->call_id;
+                       feature_data.b2bjoin.b2bjoin_joincallid = dcb->call_id;
+                       cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                                      dcb->line, CC_FEATURE_B2B_JOIN, &feature_data);
+        }
+    }
+    g_b2bjoin_pending = FALSE;
+    g_b2bjoin_callid  = CC_NO_CALL_ID;
+}
+
+/*
+ * fsmdef_select_invoke
+ *
+ *
+ * Description:
+ *    This function implements the join feature invocation.
+ *
+ * Parameters:
+ *    fsmdef_dcb_t *dcb - dcb associated with this call
+ *
+ * Returns: None
+ */
+static void
+fsmdef_select_invoke (fsmdef_dcb_t *dcb, cc_feature_data_t *select_data)
+{
+    cc_feature_data_t  feature_data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (dcb->select_pending) {
+        /* We have sent a select but have not received a
+         * response yet, so diallow further selects till then
+         */
+        return;
+    }
+    if (select_data) {
+        feature_data.select.select = select_data->select.select;
+    } else {
+        if (dcb->selected == TRUE) {
+            feature_data.select.select = FALSE;
+        } else {
+            feature_data.select.select = TRUE;
+        }
+    }
+    if ((g_b2bjoin_pending) && (dcb->call_id== g_b2bjoin_callid)) {
+        /* If join is pending, pressing Select on the same call cancels join*/
+         g_b2bjoin_pending = FALSE;
+         g_b2bjoin_callid  = CC_NO_CALL_ID;
+    }
+    dcb->select_pending = TRUE;;
+    cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                   dcb->line, CC_FEATURE_SELECT, &feature_data);
+}
+
+/*
+ * handle_join_pending
+ *
+ *
+ * Description:
+ *    This function checks if the join_pending flag
+ *    needs to be cleared.due to the invocation of other features
+ *
+ * Parameters:
+ *    fsmdef_dcb_t *dcb - dcb associated with this call
+ *
+ * Returns: None
+ */
+static void fsmdef_handle_join_pending (fsmdef_dcb_t *dcb)
+{
+    if ((g_b2bjoin_pending) && (dcb->call_id == g_b2bjoin_callid)) {
+         /* If join is pending, invoking any other feature cancels join*/
+         g_b2bjoin_pending = FALSE;
+         g_b2bjoin_callid  = CC_NO_CALL_ID;
+    }
+}
+
+
+/*
+ * fsmdef_process_dialstring_for_callfwd
+ *
+ * Description:
+ *    This function processes the dialstring event for callfwd.
+ *
+ * Parameters:
+ *    sm_event_t *event - data associated with this call/dialstring
+ *
+ * Returns: sm_rcs_t
+ */
+static sm_rcs_t
+fsmdef_process_dialstring_for_callfwd (sm_event_t *event)
+{
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /*
+     * For ccm case just return success.
+     * It will continue the call by sending INVITE
+     */
+    return (SM_RC_SUCCESS);
+}
+
+
+/*
+ * fsmdef_process_cfwd_softkey_event
+ *
+ * Description:
+ *    This function processes the cfwd softkey press event for callfwd.
+ *
+ * Parameters:
+ *    sm_event_t *event - data associated with this call/dialstring
+ *
+ * Returns: sm_rcs_t
+ */
+static sm_rcs_t
+fsmdef_process_cfwd_softkey_event (sm_event_t *event)
+{
+    fsm_fcb_t       *fcb    = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t    *dcb    = fcb->dcb;
+    cc_feature_t    *msg    = (cc_feature_t *) event->msg;
+    cc_features_t    ftr_id = msg->feature_id;
+    cc_feature_data_t *ftr_data = &(msg->data);
+    cc_action_data_t cc_data;
+    int              skMask[MAX_SOFT_KEYS];
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    // check if callfwd is active (i.e. set previously)
+    if (lsm_check_cfwd_all_ccm(dcb->line)) {
+        // call fsmdef_dialstring() version of the function specific
+        // to cfwd feature
+        return (fsmdef_cfwd_clear_ccm(fcb));
+    }
+
+
+    // code from here on is common to both modes/feature_id
+    if (fcb->state == FSMDEF_S_IDLE) {
+
+         /*
+          * The user is attempting to start a cfwdall call on a specific line:
+          * 1. need to place the connected call (if there is one) on hold,
+          * 2. clear any outgoing ringing calls,
+          * 3. initiate this call.
+          */
+         if (fsmdef_wait_to_start_new_call(TRUE, CC_SRC_GSM, dcb->call_id, dcb->line, ftr_id, ftr_data))
+         {
+             dcb->active_feature = CC_FEATURE_NONE;
+             return (SM_RC_END);
+         }
+
+
+        // go offhook and then move to dialing state to collect digits
+        //only contains <cfwdall-set> in initial NOTIFY to CUCM
+        fsmdef_notify_hook_event(fcb,CC_MSG_OFFHOOK,
+                               ftr_data->newcall.global_call_id,
+                               ftr_data->newcall.prim_call_id,
+                               ftr_data->newcall.hold_resume_reason,
+                               CC_MONITOR_NONE,
+                               (ftr_id == CC_FEATURE_CFWD_ALL) ? CFWDALL_SET:CFWDALL_NONE);
+        cc_call_state(dcb->call_id, dcb->line, CC_STATE_OFFHOOK,
+                      ((cc_state_data_t *) (&(dcb->caller_id))));
+
+        fsmdef_call_cc_state_dialing(dcb, FALSE);
+
+        // stop the dial tone for parity with SCCP phone behavior
+        cc_data.tone.tone = VCM_INSIDE_DIAL_TONE;
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_TONE,
+                             &cc_data);
+
+        // give different (zip-zip) tone rather than dial tone. zip_zip
+        // is not a permanent tone so it will end on it's own without
+        // us telling media termination to stop playing the tone.
+        cc_data.tone.tone = VCM_ZIP_ZIP;
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_PLAY_TONE,
+                             &cc_data);
+
+        // move to collect digit info state
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_COLLECT_INFO);
+    } else { // we must be in FSMDEF_S_COLLECT_INFO state
+        // stop the dial tone for parity with SCCP phone behavior
+        cc_data.tone.tone = VCM_INSIDE_DIAL_TONE;
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_TONE,
+                             &cc_data);
+
+        // give different (zip-zip) tone rather than dial tone. zip_zip
+        // is not a permanent tone so it will end on it's own without
+        // us telling media termination to stop playing the tone.
+        cc_data.tone.tone = VCM_ZIP_ZIP;
+        (void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_PLAY_TONE,
+                             &cc_data);
+    }
+    ui_control_feature(dcb->line, dcb->call_id, skMask, 1, FALSE);
+
+    return (SM_RC_END);
+}
+
+/**
+ * This function is a reduced version of the fsmdef_dialstring() function.
+ * It is customized for sending INVITE out to CCM to clear CFA state.
+ *
+ * @param[in] fcb     The pointer to the fsm_fcb_t structure of this
+ *                    call chain.
+ *
+ * @pre               (fcb not_eq NULL)
+ *
+ * @return            sm_rsc_t indicates whether the execution of
+ *                    next statmachine.
+ */
+static sm_rcs_t
+fsmdef_cfwd_clear_ccm (fsm_fcb_t *fcb)
+{
+    fsmdef_dcb_t     *dcb = fcb->dcb;
+    cc_causes_t       cause;
+    cc_msgbody_info_t msg_body;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    // to clear CFA in CCM mode... only put service uri... no dialstring.
+    fsmdef_append_dialstring_to_feature_uri(dcb, NULL);
+
+    // From here on all we need to do is send INVITE out.
+    // Since, its not a real call there is no need to update UI etc.
+    // Response to this call will be 5xx so it will be released by the SIP stack.
+    cause = gsmsdp_create_local_sdp(dcb, FALSE, TRUE, TRUE, TRUE, TRUE);
+    if (cause != CC_CAUSE_OK) {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        return (fsmdef_release(fcb, cause, dcb->send_release));
+    }
+
+    /* Build SDP for sending out */
+    cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
+    if (cause != CC_CAUSE_OK) {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        return (fsmdef_release(fcb, cause, dcb->send_release));
+    }
+    cc_int_setup(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                 &(dcb->caller_id), dcb->alert_info, VCM_INSIDE_RING,
+                 VCM_INSIDE_DIAL_TONE, NULL, NULL, FALSE, NULL, &msg_body);
+
+    /*
+     * Since we are sending setup to UI we will also have to send
+     * release to it to for sip stack to clean up the call
+     */
+    dcb->send_release = TRUE;
+
+    FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_SETUP);
+
+    fsm_change_state(fcb, __LINE__, FSMDEF_S_CALL_SENT);
+    return (SM_RC_END);
+}
+
+/*
+ * fsmdef_append_dialstring_to_feature_uri
+ *
+ * Description:
+ *    This function appends dialstring to feature URI.
+ *
+ * Parameters:
+ *    fsmdef_dcb_t *dcb, char *dialstring
+ *
+ * Return Value: none
+ *
+ * Note: TNP specific implementation.
+ */
+static void
+fsmdef_append_dialstring_to_feature_uri (fsmdef_dcb_t *dcb,
+                                        const char *dialstring)
+{
+    char service_uri[MAX_URL_LENGTH];
+
+    service_uri[0] = '\0';
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (dcb == NULL) {
+        FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_INVALID_DCB),
+                     __FUNCTION__);
+        return;
+    }
+
+    switch (dcb->active_feature) {
+    case CC_FEATURE_CFWD_ALL:
+        config_get_string(CFGID_CALL_FORWARD_URI, service_uri,
+                          sizeof(service_uri));
+        break;
+    default:
+        // do nothing
+        break;
+    }
+
+    if (service_uri[0] != NUL) {
+        dcb->caller_id.called_number =
+            strlib_update(dcb->caller_id.called_number, service_uri);
+        if (dialstring && dialstring[0]) {
+            dcb->caller_id.called_number =
+                strlib_append(dcb->caller_id.called_number, "-");
+            dcb->caller_id.called_number =
+                strlib_append(dcb->caller_id.called_number, dialstring);
+        }
+    } else {
+        FSM_DEBUG_SM(DEB_F_PREFIX"Configured Feature/Service URI Not Found For Feature[%d]\n", DEB_F_PREFIX_ARGS(FSM, "fsmdef_append_dialstring_to_feature_uri"), (int)dcb->active_feature);
+
+        if (dialstring && dialstring[0]) {
+            dcb->caller_id.called_number =
+                strlib_update(dcb->caller_id.called_number, dialstring);
+        }
+    }
+}
+
+
+/*
+ * fsmdef_is_feature_uri_configured
+ *
+ * Description:
+ *    This function checks is a feature URI is configured.
+ *
+ * Parameters:
+ *    cc_features_t ftr_id
+ *
+ * Return Value: TRUE or FALSE
+ *
+ * Note: TNP specific implementation.
+ */
+static boolean
+fsmdef_is_feature_uri_configured (cc_features_t ftr_id)
+{
+    char service_uri[MAX_URL_LENGTH];
+
+    service_uri[0] = '\0';
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    switch (ftr_id) {
+    case CC_FEATURE_CFWD_ALL:
+        config_get_string(CFGID_CALL_FORWARD_URI, service_uri,
+                          sizeof(service_uri));
+        break;
+    default:
+        break;
+    }
+
+    if (service_uri[0] != NUL) {
+        return TRUE;
+    }
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Configured Feature/Service URI Not Found For Feature[%d]\n", DEB_F_PREFIX_ARGS(FSM, "fsmdef_is_feature_uri_configured"), (int)ftr_id);
+    return FALSE;
+}
+
+/*
+ * fsmdef_check_if_ok_for_dial_call
+ *
+ * Description:
+ *    This function checks if there is a call in collecting info state
+ *    on a given line.
+ *
+ * Parameters:
+ *    line_t line
+ *
+ * Returns: TRUE or FALSE
+ */
+boolean
+fsmdef_check_if_ok_for_dial_call (line_t line)
+{
+
+    fsmdef_dcb_t *dcb;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (((dcb->line == line) && (dcb->call_id != CC_NO_CALL_ID)) &&
+            (dcb->fcb != NULL) &&
+            ((dcb->fcb->state == FSMDEF_S_COLLECT_INFO) ||
+             (dcb->fcb->state == FSMDEF_S_CALL_SENT) ||
+             (dcb->fcb->state == FSMDEF_S_OUTGOING_PROCEEDING) ||
+             (dcb->fcb->state == FSMDEF_S_KPML_COLLECT_INFO))) {
+            return (TRUE);
+        }
+    }
+    return (FALSE);
+}
+
+/*
+ * fsmdef_check_if_ok_to_ans_call
+ *
+ * Description:
+ *    Checks if given call is in ringing state.
+ *
+ * Parameters:
+ *    line
+ *    call_id
+ *
+ * Returns: TRUE or FALSE
+ */
+
+boolean
+fsmdef_check_if_ok_to_ans_call (line_t line, callid_t call_id)
+{
+    fsmdef_dcb_t   *dcb;
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+    if (dcb == NULL) {
+        return (FALSE);
+    }
+
+    if ((dcb->line != line) || ((dcb->fcb != NULL) && (dcb->fcb->state != FSMDEF_S_INCOMING_ALERTING))) {
+        return (FALSE);
+    }
+
+    return (TRUE);
+}
+
+/*
+ * fsmdef_check_if_ok_to_hold_call
+ *
+ * Description:
+ *    Checks if the requested call is in connected state.
+ *
+ * Parameters:
+ *    line
+ *    call_id
+ *
+ * Returns: TRUE - if there is a connected or resume pending call
+ *          FALSE - if the call is not in above state
+ */
+boolean
+fsmdef_check_if_ok_to_hold_call (line_t line, callid_t call_id)
+{
+    fsmdef_dcb_t *dcb;
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+    if (dcb == NULL) {
+
+        return (FALSE);
+    }
+
+    if ((dcb->line != line) ||
+        ((dcb->fcb != NULL) &&
+        ((dcb->fcb->state != FSMDEF_S_CONNECTED) &&
+         (dcb->fcb->state != FSMDEF_S_CONNECTED_MEDIA_PEND) &&
+         (dcb->fcb->state != FSMDEF_S_RESUME_PENDING)))) {
+
+        return (FALSE);
+    }
+
+    return (TRUE);
+}
+
+/*
+ * fsmdef_check_if_ok_to_resume_call
+ *
+ * Description:
+ *    Checks if the requested call is in held state.
+ *
+ * Parameters:
+ *    line
+ *    call_id
+ *
+ * Returns: TRUE or FALSE
+ */
+boolean
+fsmdef_check_if_ok_to_resume_call (line_t line, callid_t call_id)
+{
+    fsmdef_dcb_t *dcb;
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+    if (dcb == NULL) {
+        return (FALSE);
+    }
+
+    if ((dcb->line != line) || ((dcb->fcb != NULL) &&(dcb->fcb->state != FSMDEF_S_HOLDING))) {
+        return (FALSE);
+    }
+
+    return (TRUE);
+}
+
+/*
+ * fsmdef_check_if_ok_to_run_feature
+ *
+ * Description:
+ *    Checks if it is ok to run features on the call.
+ *
+ * Parameters:
+ *    line
+ *    call_id
+ *
+ * Returns: TRUE or FALSE
+ */
+boolean
+fsmdef_check_if_ok_to_run_feature (line_t line, callid_t call_id)
+{
+    fsmdef_dcb_t *dcb;
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+    if (dcb == NULL) {
+        return (FALSE);
+    }
+
+    if ((dcb->line != line) ||
+        ((dcb->fcb != NULL) &&
+         (dcb->fcb->state != FSMDEF_S_CONNECTED) &&
+         (dcb->fcb->state != FSMDEF_S_CONNECTED_MEDIA_PEND))) {
+        return (FALSE);
+    }
+
+    return (TRUE);
+}
+
+/*
+ * fsmdef_check_if_ok_to_monitor_update_call
+ *
+ * Description:
+ *    Checks if it is ok to update the monitoring call.
+ *
+ * Parameters:
+ *    line
+ *    call_id
+ *
+ * Returns: TRUE or FALSE
+ */
+boolean
+fsmdef_check_if_ok_to_monitor_update_call (line_t line, callid_t call_id)
+{
+    fsmdef_dcb_t *dcb;
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+    if (dcb == NULL) {
+        return (FALSE);
+    }
+
+    if ((dcb->line != line) ||
+        ((dcb->fcb != NULL) &&
+         (dcb->fcb->state != FSMDEF_S_CONNECTED))) {
+        return (FALSE);
+    }
+
+    return (TRUE);
+}
+
+/*
+ * fsmdef_set_call_info_cc_call_state
+ *
+ * Description:
+ *    Sets the called number to readable localized values
+ *    if it is service URI based feature and invokes cc_call_state()
+ *
+ * Parameters:
+ *    dcb
+ *    state
+ *
+ * Returns: none
+ */
+static void
+fsmdef_set_call_info_cc_call_state (fsmdef_dcb_t *dcb, cc_states_t state, cc_causes_t cause)
+{
+    cc_state_data_t temp_data;
+    char           tmp_str[CALL_BUBBLE_STR_MAX_LEN];
+    int            rc = CPR_FAILURE;
+
+       tmp_str[0] = '\0';
+
+    switch (dcb->active_feature) {
+    case CC_FEATURE_CFWD_ALL:
+        rc = platGetPhraseText(STR_INDEX_CALL_FORWARD,
+                                      (char *) tmp_str,
+                                      CALL_BUBBLE_STR_MAX_LEN);
+        break;
+    default:
+        rc = CPR_FAILURE;
+        break;
+    }
+
+    switch (state) {
+
+    case CC_STATE_DIALING_COMPLETED:
+        temp_data.dialing_completed.caller_id = dcb->caller_id;
+        break;
+
+    case CC_STATE_CALL_SENT:
+        temp_data.call_sent.caller_id = dcb->caller_id;
+        break;
+
+    case CC_STATE_CALL_FAILED:
+        temp_data.call_failed.caller_id = dcb->caller_id;
+        temp_data.call_failed.cause = cause;
+        break;
+
+    default:
+        /* Set the call id for other states */
+        temp_data.offhook.caller_id = dcb->caller_id;
+        break;
+    }
+
+    if ((rc == CPR_SUCCESS) && strlen(tmp_str) > 0) {
+        temp_data.offhook.caller_id.called_number = tmp_str;
+    }
+
+    cc_call_state(dcb->call_id, dcb->line, state, &temp_data);
+}
+
+/*
+ * fsmdef_get_dcb_by_call_instance_id
+ *
+ * Description:
+ *    This function returns fsmdef DCB that has a match for a given call
+ *    instance ID of a given line.
+ *
+ * Parameters:
+ *    line             - the dn line
+ *    call_instance_id - for call instance ID.
+ *
+ * Returns: pointer to fsmdef_dcb_t if a matching dcb is found otherwise
+ *          returns NULL
+ */
+fsmdef_dcb_t *
+fsmdef_get_dcb_by_call_instance_id (line_t line, uint16_t call_instance_id)
+{
+    fsmdef_dcb_t *dcb;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if ((dcb->caller_id.call_instance_id == call_instance_id) &&
+            (dcb->line == line)) {
+            return (dcb);
+        }
+    }
+    return (NULL);
+}
+
+/*
+ * fsmdef_ev_join
+ *
+ * Description:
+ *     The fsmdef_ev_join function sends an offhook event
+ *     to the barging call and sets its state to FSMDEF_S_JOINING.
+ *
+ * Parameters:
+ *     data - pointer to the cc_feature_data_t
+ *
+ * Returns:
+ *     none
+ */
+static void
+fsmdef_ev_join (cc_feature_data_t *data)
+{
+    fsm_fcb_t *fcb = NULL;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    fcb = fsm_get_fcb_by_call_id_and_type(data->newcall.join.join_call_id,
+                                          FSM_TYPE_DEF);
+    if (fcb) {
+        fsmdef_dcb_t *dcb = fcb->dcb;
+
+        cc_int_offhook(CC_SRC_GSM, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE,
+                       dcb->call_id, dcb->line, NULL, CC_MONITOR_NONE,CFWDALL_NONE);
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_JOINING);
+    }
+}
+
+/*
+ * fsmdef_ev_joining_connected_ack
+ *
+ * Description:
+ *     The fsmdef_ev_joining_connected_ack negotiates the sdp in the
+ *     ack(if there is one) and sets the fsm/ccapi state to connected
+ *
+ * Parameters:
+ *     event - pointer to the sm_event_t
+ *
+ * Returns:
+ *     sm_rcs_t value
+ */
+static sm_rcs_t
+fsmdef_ev_joining_connected_ack (sm_event_t *event)
+{
+    fsm_fcb_t          *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t       *dcb = fcb->dcb;
+    cc_connected_ack_t *msg = (cc_connected_ack_t *) event->msg;
+    cc_causes_t         cause;
+    fsmcnf_ccb_t       *ccb;
+    fsm_fcb_t          *join_target_fcb;
+    cc_feature_data_t   data;
+    cc_uint32_t           major_sis_ver = SIS_PROTOCOL_MAJOR_VERSION_SEADRAGON;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    memset(&data, 0, sizeof(data));
+
+    /* Check the remote SDP. The far end may not have included the SDP in an
+     * earlier message, which means that the SDP must be in this message.
+     */
+    if (dcb->remote_sdp_in_ack == TRUE) {
+        cause = gsmsdp_negotiate_answer_sdp(fcb, &msg->msg_body);
+        if (cause != CC_CAUSE_OK) {
+            data.endcall.cause = cause;
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
+                           CC_FEATURE_END_CALL, &data);
+            return (SM_RC_END);
+        }
+    }
+
+    platGetSISProtocolVer(&major_sis_ver, NULL, NULL,NULL);
+
+    ccb = fsmcnf_get_ccb_by_call_id(dcb->call_id);
+
+    if (ccb) {
+        join_target_fcb = fsm_get_fcb_by_call_id_and_type(ccb->cnf_call_id,
+                                                           FSM_TYPE_CNF);
+        if ((gsmsdp_is_media_encrypted(dcb) == FALSE) &&
+            (gsmsdp_is_media_encrypted((join_target_fcb!= NULL)?join_target_fcb->dcb:NULL) == TRUE) &&
+            (major_sis_ver < SIS_PROTOCOL_MAJOR_VERSION_MUSTER)) {
+            /*
+             * Target Call was secure, a non-secure is trying to Barge/Monitor,
+             * fail it
+             */
+            data.endcall.cause = CC_CAUSE_SECURITY_FAILURE;
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
+                           CC_FEATURE_END_CALL, &data);
+            return (SM_RC_END);
+        }
+    }
+
+    cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
+                  (cc_state_data_t *) &(dcb->caller_id));
+    /*
+     * If DSP is not able to start rx/tx channels, release the call
+     */
+    if (dcb->dsp_out_of_resources == TRUE) {
+        data.endcall.cause = CC_CAUSE_NO_MEDIA;
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
+                       CC_FEATURE_END_CALL, &data);
+        return (SM_RC_END);
+    }
+
+    if (ccb) {
+        /*
+         * Bridge the conference legs for the join call, even though
+         * it's done here, ccb should only be accessed from fsmcnf
+         */
+        ccb->active  = TRUE;
+        ccb->bridged = TRUE;
+    }
+
+    /*
+     * Handle media capability changes if there is before transition to
+     * connected state.
+     */
+    return(fsmdef_transition_to_connected(fcb));
+}
+
+/*
+ * fsmdef_ev_joining_offhook
+ *
+ * Description:
+ *     The fsmdef_ev_joining_offhook creates the local sdp
+ *     for the sip stack to send 200 Ok out
+ *
+ * Parameters:
+ *     event - pointer to the sm_event_t
+ *
+ * Returns:
+ *     sm_rcs_t value
+ */
+static sm_rcs_t
+fsmdef_ev_joining_offhook (sm_event_t *event)
+{
+    fsm_fcb_t        *fcb = (fsm_fcb_t *) event->data;
+    fsmdef_dcb_t     *dcb = fcb->dcb;
+    cc_causes_t       cause;
+    cc_msgbody_info_t msg_body;
+    cc_feature_data_t data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    /* Build our response SDP to include in the connected */
+    cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
+    if (cause != CC_CAUSE_OK) {
+        FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
+        memset(&data, 0, sizeof(data));
+        data.endcall.cause = cause;
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
+                       CC_FEATURE_END_CALL, &data);
+        return (SM_RC_END);
+    }
+    cc_int_connected(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
+                     &(dcb->caller_id), NULL, &msg_body);
+
+    FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_CONNECTED);
+
+    return (SM_RC_END);
+}
+
+/*
+ * fsmdef_extract_join_target
+ *
+ * Description:
+ *     The fsmdef_extract_join_target extract join target call id from
+ *     the setup message and sends a JOIN event to that call
+ *
+ * Parameters:
+ *     event - pointer to the sm_event_t
+ *
+ * Returns:
+ *     TRUE or FALSE
+ */
+static boolean
+fsmdef_extract_join_target (sm_event_t *event)
+{
+    static const char  fname[]    = "fsmdef_extract_join_target";
+    fsm_fcb_t      *fcb     = (fsm_fcb_t *) event->data;
+    cc_setup_t     *msg     = (cc_setup_t *) event->msg;
+    callid_t        call_id = msg->call_id;
+    line_t          line    = msg->line;
+    fsmdef_dcb_t   *dcb;
+    fsmdef_dcb_t   *join_dcb;
+    cc_feature_data_t data;
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    dcb = fcb->dcb;
+
+    if ((msg->call_info.type == CC_FEAT_MONITOR) ||
+        (dcb->session == WHISPER_COACHING))  {
+
+        /* For now, null out the ui id */
+        lsm_set_ui_id(dcb->call_id, CC_NO_CALL_ID);
+        join_dcb =
+            fsmdef_get_dcb_by_call_id(msg->call_info.data.join.join_call_id);
+        if (join_dcb) {
+            dcb->group_id = join_dcb->group_id;
+            memset(&data, 0, sizeof(data));
+            data.newcall.join.join_call_id = call_id;
+
+          if (dcb->session == WHISPER_COACHING) {
+               data.newcall.cause = CC_CAUSE_MONITOR;
+            } else if (msg->call_info.type == CC_FEAT_MONITOR) {
+                data.newcall.cause = CC_CAUSE_MONITOR;
+                // set the session leg of this dcb to monitor
+                dcb->session = MONITOR;
+            }
+            FSM_DEBUG_SM(DEB_L_C_F_PREFIX" dcb-session type is = %s \n",
+                DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname),
+                dcb->session == WHISPER_COACHING ? "WHISPER_COACHING" :
+                dcb->session == MONITOR ? "MONITOR" : "PRIMARY");
+
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, join_dcb->call_id, line,
+                           CC_FEATURE_JOIN, &data);
+        } else {
+            FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Unable to find join target dcb\n",
+                               DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname));
+            return (TRUE);
+        }
+    } else {
+        FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Unable to find join target call information\n",
+                       DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname));
+        return (TRUE);
+    }
+    return (FALSE);
+}
+
+
+/*
+ * fsmdef_ev_notify_feature
+ *
+ * Description:
+ *     Handles NOTIFY event in different states.
+ *
+ * Parameters:
+ *     msg - message pointer
+ *     dcb - pointer to the fsmdef_dcb_t
+ *
+ * Returns:
+ *     none
+ */
+static void
+fsmdef_ev_notify_feature (cc_feature_t *msg, fsmdef_dcb_t *dcb)
+{
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+}
+
+/*
+ * fsmdef_notify_hook_event
+ *
+ * Description:
+ *     Send offhook/onhook events to SIP (DialogManager) task.
+ *
+ * Parameters:
+ *     dcb - pointer to the fsmdef_dcb_t
+ *
+ * Returns:
+ *     none
+ */
+static void
+fsmdef_notify_hook_event (fsm_fcb_t *fcb, cc_msgs_t msg, char *global_call_id,
+                          callid_t prim_call_id,
+                          cc_hold_resume_reason_e consult_reason,
+                          monitor_mode_t monitor_mode,
+                          cfwdall_mode_t cfwdall_mode)
+{
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
+
+    if (msg == CC_MSG_OFFHOOK) {
+        cc_int_offhook(CC_SRC_GSM, CC_SRC_SIP, prim_call_id, consult_reason,
+                       fcb->dcb->call_id, fcb->dcb->line,
+                       global_call_id, monitor_mode,cfwdall_mode);
+    } else if (msg == CC_MSG_ONHOOK) {
+        cc_int_onhook(CC_SRC_GSM, CC_SRC_SIP, prim_call_id,
+                      consult_reason, fcb->dcb->call_id, fcb->dcb->line, FALSE, FALSE);
+    }
+    return;
+}
+
+/*
+ * fsmdef_update_callinfo_security_status
+ *
+ * Description:
+ *     The fsmdef_update_callinfo_security_status function updates the call
+ *     information that applies to TNP platform.
+ *
+ * Parameters:
+ *     dcb       - pointer to the fsmdef_dcb_t
+ *     call_info - pointer to the cc_feature_data_call_info_t
+ *
+ * Returns:
+ *     none
+ */
+static void
+fsmdef_update_callinfo_security_status (fsmdef_dcb_t *dcb,
+                                        cc_feature_data_call_info_t *call_info)
+{
+    /*
+     * Update security information
+     */
+    if (call_info->feature_flag & CC_SECURITY) {
+        if (sip_regmgr_get_sec_level(dcb->line) != AUTHENTICATED &&
+            sip_regmgr_get_sec_level(dcb->line) != ENCRYPTED ) {
+            // Signaling security can't be trusted downgrade security level to NOT_AUTHENTICATED
+            call_info->security = CC_SECURITY_NOT_AUTHENTICATED;
+        }
+
+        if (dcb->security != call_info->security) {
+            FSM_SET_SECURITY_STATUS(dcb, call_info->security);
+            if ( call_info->security == CC_SECURITY_ENCRYPTED )
+               ui_update_call_security(dcb->line, lsm_get_ui_id(dcb->call_id), CC_SECURITY_ENCRYPTED);
+            else
+               ui_update_call_security(dcb->line, lsm_get_ui_id(dcb->call_id), CC_SECURITY_UNKNOWN);
+
+            dcb->ui_update_required = TRUE;
+        }
+    }
+}
+
+/*
+ * fsmdef_check_retain_fwd_info_state
+ *
+ * Description:   This function checks the "Retain Forward Information" config
+ *                parameter (only for TNP). It returns TRUE if forward info is
+ *                to be retained; FALSE otherwise. This config parameter is
+ *                is used to change call bubble display from "Forward <DN>" to
+ *                "From <DN>" when an incoming call is answered AND the config
+ *                parameter value is set to TRUE. For non-TNP, this function
+ *                will always return FALSE (i.e. not to retain fwd info).
+ *
+ * Parameters:    none
+ *
+ * Return Value:  TRUE or FALSE
+ */
+boolean
+fsmdef_check_retain_fwd_info_state (void)
+{
+    int retain_fwd_info_cfg = 0;    /* default to Disabled (or 0) for no-op */
+
+    config_get_value(CFGID_RETAIN_FORWARD_INFORMATION,
+                     &retain_fwd_info_cfg,
+                     sizeof(retain_fwd_info_cfg));
+
+    if (!retain_fwd_info_cfg) {
+        return (FALSE);         /* do NOT retain forward information */
+    } else {
+        return (TRUE);          /* retain forward information */
+    }
+}
+
+void
+fsmdef_init (void)
+{
+    static const char fname[] = "fsmdef_init";
+    fsmdef_dcb_t *dcb;
+
+
+    /*
+     *  Initialize the dcbs.
+     */
+    fsmdef_dcbs = (fsmdef_dcb_t *)
+        cpr_calloc(FSMDEF_MAX_DCBS, sizeof(fsmdef_dcb_t));
+    if (fsmdef_dcbs == NULL) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"cpr_calloc returned NULL\n",
+                     DEB_F_PREFIX_ARGS(FSM, fname));
+        return;
+    }
+
+    /* Create free media structure list */
+    if (!gsmsdp_create_free_media_list()) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"Unable to create free media list\n",
+                     DEB_F_PREFIX_ARGS(FSM, fname));
+        return;
+    }
+
+    DEF_DEBUG(DEB_F_PREFIX"Disabling mass registration print", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        fsmdef_init_dcb(dcb, CC_NO_CALL_ID, FSMDEF_CALL_TYPE_NONE,
+                        FSMDEF_NO_NUMBER, LSM_NO_LINE, NULL);
+        /*
+         * Allocate ringback delay timer for each dcb
+         */
+        dcb->ringback_delay_tmr = cprCreateTimer("Ringback Delay",
+                                                 GSM_RINGBACK_DELAY_TIMER,
+                                                 TIMER_EXPIRATION,
+                                                 gsm_msg_queue);
+        if (dcb->ringback_delay_tmr == NULL) {
+            FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
+                         dcb->call_id, dcb->line, fname, "Ringback Delay");
+            return;
+        }
+
+        /*
+         * Allocate auto answer timer for each dcb
+         */
+        dcb->autoAnswerTimer = cprCreateTimer("Auto Answer",
+                                              GSM_AUTOANSWER_TIMER,
+                                              TIMER_EXPIRATION,
+                                              gsm_msg_queue);
+        if (dcb->autoAnswerTimer == NULL) {
+            FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
+                         dcb->call_id, dcb->line, fname, "Auto Answer");
+            (void)cprDestroyTimer(dcb->ringback_delay_tmr);
+            dcb->ringback_delay_tmr = NULL;
+            return;
+        }
+        dcb->revertTimer = cprCreateTimer("Call Reversion",
+                                              GSM_REVERSION_TIMER,
+                                              TIMER_EXPIRATION,
+                                              gsm_msg_queue);
+               dcb->reversionInterval = -1;
+        if (dcb->revertTimer == NULL) {
+            FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
+                         dcb->call_id, dcb->line, fname, "Hold Revertion");
+
+            (void)cprDestroyTimer(dcb->ringback_delay_tmr);
+            dcb->ringback_delay_tmr = NULL;
+            (void)cprDestroyTimer(dcb->autoAnswerTimer);
+            dcb->autoAnswerTimer = NULL;
+            return;
+        }
+        if (dcb == fsmdef_dcbs) {
+            g_disable_mass_reg_debug_print = TRUE;
+        }
+    }
+    g_disable_mass_reg_debug_print = FALSE;
+
+    /*
+     * Initialize the state/event table.
+     */
+    fsmdef_sm_table.min_state = FSMDEF_S_MIN;
+    fsmdef_sm_table.max_state = FSMDEF_S_MAX;
+    fsmdef_sm_table.min_event = CC_MSG_MIN;
+    fsmdef_sm_table.max_event = CC_MSG_MAX;
+    fsmdef_sm_table.table     = (&(fsmdef_function_table[0][0]));
+}
+
+void
+fsmdef_shutdown (void)
+{
+    fsmdef_dcb_t *dcb;
+
+    FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
+        if (dcb->req_pending_tmr) {
+            (void)cprDestroyTimer(dcb->req_pending_tmr);
+        }
+        if (dcb->err_onhook_tmr) {
+            (void)cprDestroyTimer(dcb->err_onhook_tmr);
+        }
+        if (dcb->ringback_delay_tmr) {
+            (void)cprDestroyTimer(dcb->ringback_delay_tmr);
+        }
+        if (dcb->autoAnswerTimer) {
+            (void)cprDestroyTimer(dcb->autoAnswerTimer);
+        }
+        if (dcb->revertTimer) {
+            (void)cprDestroyTimer(dcb->revertTimer);
+        }
+
+        /* clean media list */
+        gsmsdp_clean_media_list(dcb);
+    }
+
+    /* destroy free media structure list */
+    gsmsdp_destroy_free_media_list();
+
+    cpr_free(fsmdef_dcbs);
+    fsmdef_dcbs = NULL;
+}
+
+static void
+fsmdef_update_calltype (fsm_fcb_t *fcb, cc_feature_t *msg) {
+    fsmdef_dcb_t      *dcb       = fcb->dcb;
+    cc_feature_data_t *feat_data = &(msg->data);
+    cc_caller_id_t    *caller_id;
+
+    if (msg->data_valid == FALSE) {
+        /* No data to use for update. Just ignore the event. */
+        return;
+    }
+
+    if (feat_data->call_info.feature_flag & CC_CALLER_ID) {
+        caller_id = &feat_data->call_info.caller_id;
+        if (caller_id->call_type == CC_CALL_FORWARDED) {
+            if (fsmdef_check_retain_fwd_info_state()) {
+                dcb->call_type = FSMDEF_CALL_TYPE_FORWARD;
+            }
+        }
+    }
+}
+
diff --git a/libs/sipcc/core/gsm/fsmxfr.c b/libs/sipcc/core/gsm/fsmxfr.c
new file mode 100755 (executable)
index 0000000..d0d387f
--- /dev/null
@@ -0,0 +1,3041 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "fim.h"
+#include "lsm.h"
+#include "sm.h"
+#include "ccapi.h"
+#include "phone_debug.h"
+#include "text_strings.h"
+#include "fsm.h"
+#include "uiapi.h"
+#include "debug.h"
+#include "regmgrapi.h"
+#include "platform_api.h"
+
+extern fsmdef_dcb_t *fsmdef_dcbs;
+
+#define FSMXFR_NULL_DIALSTRING '\0'
+static fsmxfr_xcb_t *fsmxfr_xcbs;
+
+typedef enum fsmxfr_states_t_ {
+    FSMXFR_S_MIN = -1,
+    FSMXFR_S_IDLE,
+    FSMXFR_S_ACTIVE,
+    FSMXFR_S_MAX
+} fsmxfr_states_t;
+
+static const char *fsmxfr_state_names[] = {
+    "IDLE",
+    "ACTIVE"
+};
+
+
+static sm_rcs_t fsmxfr_ev_idle_setup(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_idle_feature(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_idle_dialstring(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_active_connected_ack(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_active_release(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_active_release_complete(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_active_feature(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_active_feature_ack(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_active_onhook(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_active_dialstring(sm_event_t *event);
+static sm_rcs_t fsmxfr_ev_active_proceeding(sm_event_t *event);
+
+static sm_function_t fsmxfr_function_table[FSMXFR_S_MAX][CC_MSG_MAX] =
+{
+/* FSMXFR_S_IDLE ------------------------------------------------------------ */
+    {
+    /* FSMXFR_E_SETUP            */ fsmxfr_ev_idle_setup,
+    /* FSMXFR_E_SETUP_ACK        */ NULL,
+    /* FSMXFR_E_PROCEEDING       */ NULL,
+    /* FSMXFR_E_ALERTING         */ NULL,
+    /* FSMXFR_E_CONNECTED        */ NULL,
+    /* FSMXFR_E_CONNECTED_ACK    */ NULL,
+    /* FSMXFR_E_RELEASE          */ NULL,
+    /* FSMXFR_E_RELEASE_COMPLETE */ NULL,
+    /* FSMXFR_E_FEATURE          */ fsmxfr_ev_idle_feature,
+    /* FSMXFR_E_FEATURE_ACK      */ NULL,
+    /* FSMXFR_E_OFFHOOK          */ NULL,
+    /* FSMXFR_E_ONHOOK           */ NULL,
+    /* FSMXFR_E_LINE             */ NULL,
+    /* FSMXFR_E_DIGIT_BEGIN      */ NULL,
+    /* FSMXFR_E_DIGIT            */ NULL,
+    /* FSMXFR_E_DIALSTRING       */ fsmxfr_ev_idle_dialstring,
+    /* FSMXFR_E_MWI              */ NULL,
+    /* FSMXFR_E_SESSION_AUDIT    */ NULL
+    },
+
+/* FSMXFR_S_ACTIVE ---------------------------------------------------- */
+    {
+    /* FSMXFR_E_SETUP            */ NULL,
+    /* FSMXFR_E_SETUP_ACK        */ NULL,
+    /* FSMXFR_E_PROCEEDING       */ fsmxfr_ev_active_proceeding,
+    /* FSMXFR_E_ALERTING         */ NULL,
+    /* FSMXFR_E_CONNECTED        */ NULL,
+    /* FSMXFR_E_CONNECTED_ACK    */ fsmxfr_ev_active_connected_ack,
+    /* FSMXFR_E_RELEASE          */ fsmxfr_ev_active_release,
+    /* FSMXFR_E_RELEASE_COMPLETE */ fsmxfr_ev_active_release_complete,
+    /* FSMXFR_E_FEATURE          */ fsmxfr_ev_active_feature,
+    /* FSMXFR_E_FEATURE_ACK      */ fsmxfr_ev_active_feature_ack,
+    /* FSMXFR_E_OFFHOOK          */ NULL,
+    /* FSMXFR_E_ONHOOK           */ fsmxfr_ev_active_onhook,
+    /* FSMXFR_E_LINE             */ NULL,
+    /* FSMXFR_E_DIGIT_BEGIN      */ NULL,
+    /* FSMXFR_E_DIGIT            */ NULL,
+    /* FSMXFR_E_DIALSTRING       */ fsmxfr_ev_active_dialstring,
+    /* FSMXFR_E_MWI              */ NULL,
+    /* FSMXFR_E_SESSION_AUDIT    */ NULL
+    }
+};
+
+static sm_table_t fsmxfr_sm_table;
+sm_table_t *pfsmxfr_sm_table = &fsmxfr_sm_table;
+
+const char *
+fsmxfr_state_name (int state)
+{
+    if ((state <= FSMXFR_S_MIN) || (state >= FSMXFR_S_MAX)) {
+        return (get_debug_string(GSM_UNDEFINED));
+    }
+
+    return (fsmxfr_state_names[state]);
+}
+
+
+static int
+fsmxfr_get_new_xfr_id (void)
+{
+    static int xfr_id = 0;
+
+    if (++xfr_id < 0) {
+        xfr_id = 1;
+    }
+
+    return (xfr_id);
+}
+
+/**
+ *
+ * Sets/rest transfer complete flag in the DCB so it can be used in
+ * UI to indicate if the call is ended because of xfer or endcall.
+ *
+ * @cns_call_id consult call id
+ * @Xfr_call_id transfer call id
+ *
+ * @return  none
+ *
+ * @pre     (none)
+ */
+void fsmxfr_mark_dcb_for_xfr_complete(callid_t cns_call_id, callid_t xfr_call_id,
+        boolean set_flag)
+{
+    fsm_fcb_t    *cns_fcb, *xfr_fcb;
+
+    cns_fcb = fsm_get_fcb_by_call_id_and_type(cns_call_id,
+                                    FSM_TYPE_DEF);
+    xfr_fcb = fsm_get_fcb_by_call_id_and_type(xfr_call_id,
+                                    FSM_TYPE_DEF);
+    if (set_flag) {
+        if (cns_fcb && cns_fcb->dcb) {
+            FSM_SET_FLAGS(cns_fcb->dcb->flags, FSMDEF_F_XFER_COMPLETE);
+        }
+        if (xfr_fcb && xfr_fcb->dcb) {
+            FSM_SET_FLAGS(xfr_fcb->dcb->flags, FSMDEF_F_XFER_COMPLETE);
+        }
+    } else {
+        if (cns_fcb && cns_fcb->dcb) {
+            FSM_RESET_FLAGS(cns_fcb->dcb->flags, FSMDEF_F_XFER_COMPLETE);
+        }
+        if (xfr_fcb && xfr_fcb->dcb) {
+            FSM_RESET_FLAGS(xfr_fcb->dcb->flags, FSMDEF_F_XFER_COMPLETE);
+        }
+    }
+}
+
+/**
+ *
+ * Get active trasnfer state machine information (in active state).
+ *
+ * @param none
+ *
+ * @return  fsm_fcb_t if there is a active trasnfer pending
+ *          else NULL
+ *
+ * @pre     (none)
+ */
+
+
+fsm_fcb_t *fsmxfr_get_active_xfer(void)
+{
+    fsm_fcb_t    *fcb;
+    fsmxfr_xcb_t *xcb;
+
+    FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) {
+        fcb = fsm_get_fcb_by_call_id_and_type(xcb->xfr_call_id,
+                                               FSM_TYPE_XFR);
+        if (fcb && fcb->state == FSMXFR_S_ACTIVE) {
+            return(fcb);
+        }
+    }
+
+    return(NULL);
+}
+
+
+static void
+fsmxfr_init_xcb (fsmxfr_xcb_t *xcb)
+{
+    if (xcb != NULL) {
+        xcb->xfr_id        = FSM_NO_ID;
+        xcb->xfr_call_id   = CC_NO_CALL_ID;
+        xcb->cns_call_id   = CC_NO_CALL_ID;
+        xcb->xfr_line      = CC_NO_LINE;
+        xcb->cns_line      = CC_NO_LINE;
+        xcb->type          = FSMXFR_TYPE_NONE;
+        xcb->method        = CC_XFER_METHOD_NONE;
+        xcb->cnf_xfr       = FALSE;
+        xcb->active        = FALSE;
+        xcb->mode          = FSMXFR_MODE_TRANSFEROR;
+        xcb->xfer_comp_req = FALSE;
+        xcb->xfr_orig      = CC_SRC_MIN;
+
+        if (xcb->xcb2 != NULL) {
+            fsmxfr_init_xcb(xcb->xcb2);
+            xcb->xcb2 = NULL;
+        }
+
+        if (xcb->dialstring != NULL) {
+            cpr_free(xcb->dialstring);
+            xcb->dialstring = NULL;
+        }
+        if (xcb->queued_dialstring != NULL) {
+            cpr_free(xcb->queued_dialstring);
+            xcb->queued_dialstring = NULL;
+        }
+        if (xcb->referred_by != NULL) {
+            cpr_free(xcb->referred_by);
+            xcb->referred_by = NULL;
+        }
+    }
+}
+
+
+static fsmxfr_xcb_t *
+fsmxfr_get_xcb_by_xfr_id (int xfr_id)
+{
+    static const char fname[] = "fsmxfr_get_xcb_by_xfr_id";
+    fsmxfr_xcb_t *xcb;
+    fsmxfr_xcb_t *xcb_found = NULL;
+
+    FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) {
+        if (xcb->xfr_id == xfr_id) {
+            xcb_found = xcb;
+
+            FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_PTR), xcb->xfr_id,
+                         xcb->xfr_call_id, xcb->cns_call_id, fname, xcb);
+
+            break;
+        }
+    }
+
+
+    return (xcb_found);
+}
+
+
+/*
+ *  Function: fsmxfr_get_new_xfr_context
+ *
+ *  Parameters:
+ *      xfr_call_id: call_id for the call initiating the transfer
+ *      type:        attended or unattended transfer
+ *      method:      BYE/ALSO or REFER method of transfer
+ *      local:       local or remote initiated transfer
+ *
+ *  Description: This function creates a new transfer context by:
+ *               - getting a free xcb
+ *               - creating new xfr_id and cns_call_id
+ *
+ *  Returns: xcb
+ *
+ */
+static fsmxfr_xcb_t *
+fsmxfr_get_new_xfr_context (callid_t xfr_call_id, line_t line, fsmxfr_types_t type,
+                           cc_xfer_methods_t method, fsmxfr_modes_t mode)
+{
+    static const char fname[] = "fsmxfr_get_new_xfr_context";
+    fsmxfr_xcb_t *xcb;
+
+    xcb = fsmxfr_get_xcb_by_xfr_id(FSM_NO_ID);
+    if (xcb != NULL) {
+        xcb->xfr_id      = fsmxfr_get_new_xfr_id();
+        xcb->xfr_call_id = xfr_call_id;
+        xcb->cns_call_id = cc_get_new_call_id();
+        xcb->xfr_line    = line;
+        xcb->cns_line    = line;
+        xcb->type        = type;
+        xcb->method      = method;
+        xcb->mode        = mode;
+
+        FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_PTR), xcb->xfr_id,
+                     xcb->xfr_call_id, xcb->cns_call_id, fname, xcb);
+    }
+
+    return (xcb);
+}
+
+
+fsmxfr_xcb_t *
+fsmxfr_get_xcb_by_call_id (callid_t call_id)
+{
+    fsmxfr_xcb_t *xcb;
+    fsmxfr_xcb_t *xcb_found = NULL;
+
+    FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) {
+        if ((xcb->xfr_call_id == call_id) || (xcb->cns_call_id == call_id)) {
+            xcb_found = xcb;
+            break;
+        }
+    }
+
+    return (xcb_found);
+}
+
+/**
+ *
+ * Cancel tranfer operation by sending cancel event to SIP stack.
+ * This routine is used in roundtable phone.
+ *
+ * @param line, call_id, target_call_id, cause (implicit or explicit)
+ *
+ * @return  void
+ *
+ * @pre     (none)
+ */
+void
+fsmxfr_feature_cancel (fsmxfr_xcb_t *xcb, line_t line, callid_t call_id,
+                       callid_t target_call_id,
+                       cc_rcc_skey_evt_type_e cause)
+{
+    static const char fname[] = "fsmxfr_feature_cancel";
+    cc_feature_data_t data;
+    fsm_fcb_t         *fcb_def;
+
+    DEF_DEBUG(DEB_F_PREFIX"Sending cancel call_id = %d, t_id=%d, cause = %d\n",
+            DEB_F_PREFIX_ARGS(GSM, fname), call_id, target_call_id, cause);
+
+    fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+
+    if ((cause == CC_SK_EVT_TYPE_EXPLI) &&
+        (fcb_def != NULL) && ((fcb_def->dcb->selected == FALSE) &&
+        ((fcb_def->state == FSMDEF_S_OUTGOING_ALERTING) ||
+            ((fcb_def->state == FSMDEF_S_CONNECTED) &&
+            (fcb_def->dcb->spoof_ringout_requested == TRUE) &&
+            (fcb_def->dcb->spoof_ringout_applied == TRUE))))) {
+
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, call_id,
+                           line, CC_FEATURE_END_CALL, NULL);
+    }
+
+    fcb_def = fsm_get_fcb_by_call_id_and_type(target_call_id, FSM_TYPE_DEF);
+
+    if ((cause == CC_SK_EVT_TYPE_EXPLI) &&
+        (fcb_def != NULL) && ((fcb_def->dcb->selected == FALSE) &&
+        ((fcb_def->state == FSMDEF_S_OUTGOING_ALERTING) ||
+            ((fcb_def->state == FSMDEF_S_CONNECTED) &&
+            (fcb_def->dcb->spoof_ringout_requested == TRUE) &&
+            (fcb_def->dcb->spoof_ringout_applied == TRUE))))) {
+
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, target_call_id,
+                           line, CC_FEATURE_END_CALL, NULL);
+    }
+
+    data.cancel.target_call_id = target_call_id;
+    data.cancel.call_id = call_id;
+    data.cancel.cause = cause;
+
+    cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, call_id,
+                           line, CC_FEATURE_CANCEL, &data);
+}
+
+
+void
+fsmxfr_update_xfr_context (fsmxfr_xcb_t *xcb, callid_t old_call_id,
+                           callid_t new_call_id)
+{
+    static const char fname[] = "fsmxfr_update_xfr_context";
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_update_xfr_context"));
+
+    if (xcb != NULL) {
+        if (old_call_id == xcb->xfr_call_id) {
+            xcb->xfr_call_id = new_call_id;
+        } else if (old_call_id == xcb->cns_call_id) {
+            xcb->cns_call_id = new_call_id;
+        }
+
+        FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_PTR), xcb->xfr_id,
+                     xcb->xfr_call_id, xcb->cns_call_id, fname, xcb);
+    }
+}
+
+/*
+ *      Function to get line number of other call associated in
+ *      transfer.
+ *
+ *  @param xcb and call_id.
+ *
+ *  @return void
+ *
+ */
+line_t
+fsmxfr_get_other_line (fsmxfr_xcb_t *xcb, callid_t call_id)
+{
+    line_t other_line = CC_NO_LINE;
+
+    if (xcb != NULL) {
+        if (xcb->xfr_call_id == call_id) {
+            other_line = xcb->cns_line;
+        } else if (xcb->cns_call_id == call_id) {
+            other_line = xcb->xfr_line;
+        }
+    }
+
+    return (other_line);
+}
+
+callid_t
+fsmxfr_get_other_call_id (fsmxfr_xcb_t *xcb, callid_t call_id)
+{
+    callid_t other_call_id = CC_NO_CALL_ID;
+
+    if (xcb != NULL) {
+        if (xcb->xfr_call_id == call_id) {
+            other_call_id = xcb->cns_call_id;
+        } else if (xcb->cns_call_id == call_id) {
+            other_call_id = xcb->xfr_call_id;
+        }
+    }
+
+    return (other_call_id);
+}
+
+
+/*
+ *  Function: fsmxfr_remove_fcb
+ *
+ *  Parameters:
+ *      xfr_id:  xfr_id for the transfer
+ *      call_id: call_id that identifies the fcb to be removed
+ *
+ *  Description: This function will remove the fcb identified by the given
+ *               call_id from the xcb. And the function will free the xcb
+ *               if both fcbs have been removed.
+ *
+ *  Returns: none
+ *
+ *  Note: This is a helper function for fsmxfr_cleanup. It allows fsmxfr_cleanup
+ *        to cleanup one fcb (one call involved in the transfer) independent
+ *        of the other involved fcb.
+ */
+static void
+fsmxfr_remove_fcb (fsm_fcb_t *fcb, callid_t call_id)
+{
+    fsmxfr_xcb_t *xcb = fcb->xcb;
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_remove_fcb"));
+
+    if (xcb != NULL) {
+        fsmxfr_update_xfr_context(xcb, call_id, CC_NO_CALL_ID);
+
+        /*
+         * Free the xcb if both fcb references have been removed.
+         */
+        if ((xcb->xfr_call_id == CC_NO_CALL_ID) &&
+            (xcb->cns_call_id == CC_NO_CALL_ID)) {
+            fsmxfr_init_xcb(xcb);
+        }
+    }
+}
+
+
+static void
+fsmxfr_cnf_cleanup (fsmxfr_xcb_t *xcb)
+{
+    fsmdef_dcb_t     *xfr_dcb;
+    fsmdef_dcb_t     *cns_dcb;
+    cc_feature_data_t ftr_data;
+
+    cns_dcb = fsm_get_dcb(xcb->cns_call_id);
+    xfr_dcb = fsm_get_dcb(xcb->xfr_call_id);
+
+    ftr_data.endcall.cause = CC_CAUSE_NORMAL;
+    ftr_data.endcall.dialstring[0] = '\0';
+    /*
+     * This is a conference transfer hence we are cleaning
+     * up both the lines
+     */
+    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM,
+                   cns_dcb->call_id, cns_dcb->line,
+                   CC_FEATURE_END_CALL, &ftr_data);
+    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM,
+                   xfr_dcb->call_id, xfr_dcb->line,
+                   CC_FEATURE_END_CALL, &ftr_data);
+}
+
+
+static void
+fsmxfr_cleanup (fsm_fcb_t *fcb, int fname, boolean both)
+{
+    fsm_fcb_t   *other_fcb = NULL;
+    callid_t     call_id = fcb->call_id;
+    callid_t     other_call_id = CC_NO_CALL_ID;
+    line_t       other_line;
+
+
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_cleanup"));
+    other_call_id = fsmxfr_get_other_call_id(fcb->xcb, call_id);
+    other_line    = fsmxfr_get_other_line(fcb->xcb, call_id);
+
+    if (other_call_id != CC_NO_CALL_ID) {
+        other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                    FSM_TYPE_XFR);
+    }
+
+    if (fcb->xcb && (fcb->xcb->cnf_xfr != TRUE) &&
+        (call_id == fcb->xcb->xfr_call_id)) {
+
+        if (other_call_id != CC_NO_CALL_ID) {
+            /*
+             * Not clearing consulation call, so change consultation call
+             * attribute to display connected softkey set. Do not change
+             * softkey set if it is a transfer o
+             */
+            cc_call_attribute(other_call_id, other_line, NORMAL_CALL);
+        }
+    }
+    /*
+     * Check if the user wanted to cleanup the whole xcb.
+     * If so, then we will grab the other fcb first and call this function
+     * again with this other fcb. The whole xcb will be freed after this block
+     * of code because both call_ids will be -1, which tells
+     * fsmxfr_remove_fcb to free the xcb.
+     */
+    if (both) {
+        FSM_DEBUG_SM(DEB_F_PREFIX"clean both. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_cleanup"));
+
+        if (other_call_id != CC_NO_CALL_ID) {
+            if (other_fcb != NULL) {
+                fsmxfr_cleanup(other_fcb, fname, FALSE);
+            } else {
+                /*
+                 * If there is no other FCB, we need to clean up so that the
+                 * XCB will be deleted.
+                 */
+                fsmxfr_update_xfr_context(fcb->xcb, other_call_id,
+                                          CC_NO_CALL_ID);
+            }
+        }
+    }
+
+    /*
+     * Remove the reference to this fcb from the xcb
+     */
+    fsmxfr_remove_fcb(fcb, fcb->call_id);
+
+    /*
+     * Move this fcb to the IDLE state
+     */
+    fsm_change_state(fcb, fname, FSMXFR_S_IDLE);
+
+    /*
+     * Reset the data for this fcb. The fcb is still included in a call
+     * so set the call_id and dcb values accordingly.
+     */
+    fsm_init_fcb(fcb, fcb->call_id, fcb->dcb, FSM_TYPE_XFR);
+}
+
+
+void
+fsmxfr_free_cb (fim_icb_t *icb, callid_t call_id)
+{
+    fsm_fcb_t *fcb = NULL;
+
+    if (call_id != CC_NO_CALL_ID) {
+        fcb = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_XFR);
+
+        if (fcb != NULL) {
+            fsmxfr_cleanup(fcb, __LINE__, FALSE);
+            fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE);
+        }
+    }
+}
+
+
+fsmxfr_types_t
+fsmxfr_get_xfr_type (callid_t call_id)
+{
+    fsmxfr_xcb_t   *xcb;
+    fsmxfr_types_t  type = FSMXFR_TYPE_BLND_XFR;
+
+    xcb = fsmxfr_get_xcb_by_call_id(call_id);
+    if (xcb != NULL) {
+        type = xcb->type;
+    }
+
+    return (type);
+}
+
+
+cc_features_t
+fsmxfr_type_to_feature (fsmxfr_types_t type)
+{
+    cc_features_t feature;
+
+    if (type == FSMXFR_TYPE_XFR) {
+        feature = CC_FEATURE_XFER;
+    } else {
+        feature = CC_FEATURE_BLIND_XFER;
+    }
+
+    return (feature);
+}
+
+
+/*******************************************************************
+ * event functions
+ */
+
+
+static sm_rcs_t
+fsmxfr_ev_idle_setup (sm_event_t *event)
+{
+    fsm_fcb_t    *fcb     = (fsm_fcb_t *) event->data;
+    cc_setup_t   *msg     = (cc_setup_t *) event->msg;
+    fsmxfr_xcb_t *xcb;
+
+    /*
+     * If this is the consultation call involved in a transfer,
+     * then set the rest of the data required to make the transfer
+     * happen. The data is the xcb in the fcb. The data is set now
+     * because we did not have the fcb when the transfer was
+     * initiated. The fcb is created when a new_call event is
+     * received by the FIM, not when a transfer event is received.
+     */
+
+    /*
+     * Ignore this event if this call is not involved in a transfer.
+     */
+    xcb = fsmxfr_get_xcb_by_call_id(msg->call_id);
+    if (xcb == NULL) {
+        return (SM_RC_DEF_CONT);
+    }
+    fcb->xcb = xcb;
+
+    fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+
+    return (SM_RC_CONT);
+}
+
+
+static boolean
+fsmxfr_copy_dialstring (char **saved_dialstring, char *dialstring)
+{
+    char         *tempstring;
+    int           len;
+
+    if (saved_dialstring == NULL) {
+        return (FALSE);
+    }
+
+    /*
+     * Make sure we have a valid dialstring.
+     */
+    if ((dialstring == NULL) || (dialstring[0] == '\0')) {
+        return (FALSE);
+    }
+
+    /*
+     * Copy the dialstring to the xcb. We will need it later when
+     * SIP sends the RELEASE so that we can send out the
+     * NEWCALL to start the call to the target.
+     */
+    len = (strlen(dialstring) + 1) * sizeof(char);
+    tempstring = (char *) cpr_malloc(len);
+    if (tempstring != NULL) {
+        sstrncpy(tempstring, dialstring, len);
+
+        *saved_dialstring = tempstring;
+    }
+
+    return (TRUE);
+}
+
+static void
+fsmxfr_set_xfer_data (cc_causes_t cause, cc_xfer_methods_t method,
+                      callid_t target_call_id, char *dialstring,
+                      cc_feature_data_xfer_t *xfer)
+{
+    xfer->cause          = cause;
+    xfer->method         = method;
+    xfer->target_call_id = target_call_id;
+    sstrncpy(xfer->dialstring, dialstring, CC_MAX_DIALSTRING_LEN);
+}
+
+
+static boolean
+fsmxfr_remote_transfer (fsm_fcb_t *fcb, cc_features_t ftr_id,
+                        callid_t call_id, line_t line, char *dialstring,
+                        char *referred_by)
+{
+    fsmxfr_types_t    type;
+    int               free_lines;
+    cc_feature_data_t data;
+    fsmxfr_xcb_t     *xcb;
+    fsmxfr_xcb_t     *primary_xcb;
+    callid_t          cns_call_id;
+    line_t            newcall_line = 0;
+
+    memset(&data, 0, sizeof(cc_feature_data_t));
+
+    /*
+     * Make sure we have a free line for the consultation
+     * call if this is an attended transfer. We do not need this
+     * check for an unattended transfer, because the stack will
+     * send up a release for the call being transferred, which will
+     * then trigger the fsmxfr to send out the newcall in the same
+     * plane of the call being released.
+     */
+    if (ftr_id == CC_FEATURE_XFER) {
+        /*
+         * Make sure we have a free line to start the
+         * consultation call.
+         */
+        free_lines = lsm_get_instances_available_cnt(line, TRUE);
+        if (free_lines <= 0) {
+            /*
+             * No free lines - let the user know and end this
+             * request.
+             */
+            fsm_display_no_free_lines();
+
+            return (FALSE);
+        }
+    }
+
+    newcall_line = lsm_get_newcall_line(line);
+    if (newcall_line == NO_LINES_AVAILABLE) {
+        /*
+         * Error Pass Limit - let the user know and end this request.
+         */
+        lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT);
+         return (FALSE);
+    }
+
+    /*
+     * Get a new xcb and new xfr id - This is the handle that will
+     * identify the xfr.
+     */
+    type = ((ftr_id == CC_FEATURE_XFER) ?
+            (FSMXFR_TYPE_XFR) : (FSMXFR_TYPE_BLND_XFR));
+
+    primary_xcb = fsmxfr_get_xcb_by_call_id(call_id);
+
+    xcb = fsmxfr_get_new_xfr_context(call_id, line, type, CC_XFER_METHOD_REFER,
+                                     FSMXFR_MODE_TRANSFEREE);
+    if (xcb == NULL) {
+        return (FALSE);
+    }
+
+    if (primary_xcb) {
+        fcb->xcb->xcb2 = xcb;
+        fcb->xcb->active = TRUE;
+    } else {
+        fcb->xcb = xcb;
+    }
+
+    xcb->cns_line = newcall_line;
+    cns_call_id = xcb->cns_call_id;
+    fsmxfr_set_xfer_data(CC_CAUSE_OK, CC_XFER_METHOD_REFER, cns_call_id,
+                         FSMXFR_NULL_DIALSTRING, &(data.xfer));
+
+    cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id,
+                       line, ftr_id, &data, CC_CAUSE_NORMAL);
+
+    if (ftr_id == CC_FEATURE_XFER) {
+        /*
+         * This call needs to go on hold so we can start the
+         * consultation call. The call may already be on hold,
+         * so the event will just be silently ignored. We set
+         * the line number to 0xFF so that GSM will know this
+         * came from a transfer and we only want to put the call
+         * on local hold. We don't want to send an Invite Hold
+         * to the SIP stack.
+         */
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, call_id,
+                       0xFF, CC_FEATURE_HOLD, NULL);
+
+        /*
+         * Initiate the consultation call.
+         */
+
+        /*
+         * Make sure we have a valid dialstring.
+         */
+        if ((dialstring == NULL) || (dialstring[0] == '\0')) {
+            return (FALSE);
+        }
+
+        /*
+         * memset is done because if redirects[0].number is
+         * corrupted we might think that it is a blind
+         * transfer
+         */
+        memset(data.newcall.redirect.redirects[0].number, 0,
+               sizeof(CC_MAX_DIALSTRING_LEN));
+        data.newcall.cause = CC_CAUSE_XFER_REMOTE;
+        data.newcall.redirect.redirects[0].redirect_reason =
+            CC_REDIRECT_REASON_DEFLECTION;
+        sstrncpy(data.newcall.dialstring, dialstring, CC_MAX_DIALSTRING_LEN);
+
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_call_id, newcall_line,
+                       CC_FEATURE_NEW_CALL, &data);
+
+        FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_XFR_INITIATED),
+                     xcb->xfr_id, call_id, cns_call_id, __LINE__);
+
+        fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+    } else { /* CC_FEATURE_BLIND_XFER */
+        /*
+         * Make sure we have a valid dialstring.
+         */
+        if ((fsmxfr_copy_dialstring(&xcb->dialstring, dialstring) == TRUE) &&
+            (fsmxfr_copy_dialstring(&xcb->referred_by, referred_by) == TRUE)) {
+
+            fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+        } else {
+            fsmxfr_cleanup(fcb, __LINE__, TRUE);
+        }
+    }
+
+    return (TRUE);
+}
+
+
+static sm_rcs_t
+fsmxfr_ev_idle_feature (sm_event_t *event)
+{
+    const char        *fname    = "fsmxfr_ev_idle_feature";
+    fsm_fcb_t         *fcb     = (fsm_fcb_t *) event->data;
+    cc_feature_t      *msg     = (cc_feature_t *) event->msg;
+    callid_t           call_id = msg->call_id;
+    line_t             line    = msg->line;
+    cc_srcs_t          src_id  = msg->src_id;
+    cc_features_t      ftr_id  = msg->feature_id;
+    cc_feature_data_t *ftr_data = &(msg->data);
+    fsmdef_dcb_t      *dcb     = fcb->dcb;
+    fsm_fcb_t         *other_fcb;
+    sm_rcs_t           sm_rc   = SM_RC_CONT;
+    fsmxfr_types_t     type;
+    int                free_lines;
+    cc_feature_data_t  data;
+    cc_causes_t        cause   = msg->data.xfer.cause;
+    cc_xfer_methods_t  method;
+    fsmxfr_xcb_t      *xcb;
+    fsm_fcb_t         *fcb_def;
+    fsm_fcb_t         *cns_fcb, *con_fcb, *sel_fcb;
+    boolean            int_rc = FALSE;
+    callid_t           cns_call_id;
+    line_t             newcall_line = 0;
+
+    memset(&data, 0, sizeof(data));
+    fsm_sm_ftr(ftr_id, src_id);
+
+    /*
+     * Consume the XFER events and don't pass them along to the other FSMs.
+     */
+    if ((ftr_id == CC_FEATURE_BLIND_XFER) || (ftr_id == CC_FEATURE_XFER)) {
+        sm_rc = SM_RC_END;
+    }
+
+    switch (src_id) {
+    case CC_SRC_UI:
+        switch (ftr_id) {
+        case CC_FEATURE_DIRTRXFR:
+
+            /* If there is a active xfer pending
+             * then link this transfer to active transfer
+             */
+            other_fcb = fsmxfr_get_active_xfer();
+            if (other_fcb) {
+                if (other_fcb->xcb == NULL) {
+                    GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname);
+                    return (SM_RC_END);
+                }
+                /* End existing consult call and then link another
+                 * call with the trasfer
+                 */
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_fcb->xcb->cns_call_id,
+                       other_fcb->xcb->cns_line, CC_FEATURE_END_CALL, NULL);
+                other_fcb->xcb->cns_call_id = call_id;
+
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_fcb->xcb->xfr_call_id,
+                               other_fcb->xcb->xfr_line, CC_FEATURE_DIRTRXFR, NULL);
+
+                return(SM_RC_END);
+            }
+            fsm_get_fcb_by_selected_or_connected_call_fcb(call_id, &con_fcb, &sel_fcb);
+
+            /* If there is a call selected use that for direct transfer */
+            if (sel_fcb) {
+                other_fcb = sel_fcb;
+            } else if (con_fcb) {
+                other_fcb = con_fcb;
+            } else {
+                /* No connected call or selected call */
+                return(SM_RC_CONT);
+            }
+
+            /* Make sure atleast one call has been selected, connected and this call
+             * is not in the focus
+             */
+            if ((fsmutil_get_num_selected_calls() > 1) && (dcb->selected == FALSE)) {
+                //return error
+                return(SM_RC_CONT);
+            }
+
+            if (other_fcb->xcb == NULL || other_fcb->xcb->xfr_line != line) {
+                //Not on same line display a message
+                GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname);
+                return(SM_RC_CONT);
+            }
+
+            xcb = fsmxfr_get_new_xfr_context(call_id, line, FSMXFR_TYPE_XFR,
+                                             CC_XFER_METHOD_REFER,
+                                             FSMXFR_MODE_TRANSFEROR);
+            if (xcb == NULL) {
+                break;
+            }
+
+            xcb->xfr_orig = src_id;
+            fcb->xcb = xcb;
+
+            xcb->type = FSMXFR_TYPE_DIR_XFR;
+            xcb->cns_call_id = other_fcb->dcb->call_id;
+
+            other_fcb->xcb = xcb;
+            /*
+             * Emulating user hitting transfer key for second time
+             * in attended transfer
+             */
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                               dcb->line, CC_FEATURE_DIRTRXFR, NULL);
+
+            fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+
+            return(SM_RC_END);
+
+        case CC_FEATURE_BLIND_XFER:
+        case CC_FEATURE_XFER:
+
+            /* Connect the existing call to active trasnfer state
+             * machine. If the UI generates the event with target
+             * call_id in the data then terminate the existing consulatative
+             * call and link that to another call.
+             */
+            if ((cause != CC_CAUSE_XFER_CNF) &&
+                (ftr_data && msg->data_valid) &&
+                (ftr_data->xfer.target_call_id != CC_NO_CALL_ID) &&
+                ((cns_fcb = fsm_get_fcb_by_call_id_and_type(ftr_data->xfer.target_call_id,
+                                                    FSM_TYPE_XFR)) != NULL)) {
+                /* In this case there is no active xcb but upper layer
+                 * wants to complete a trasnfer with 2 different call_ids
+                 */
+                xcb = fsmxfr_get_new_xfr_context(call_id, line, FSMXFR_TYPE_XFR,
+                                                 CC_XFER_METHOD_REFER,
+                                                 FSMXFR_MODE_TRANSFEROR);
+                if (xcb == NULL) {
+                    return(SM_RC_END);
+                }
+
+                fcb->xcb = xcb;
+                cns_fcb->xcb = xcb;
+                xcb->type = FSMXFR_TYPE_DIR_XFR;
+
+                xcb->cns_call_id = ftr_data->xfer.target_call_id;
+
+                fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+
+                /* Find line information for target call.
+                 */
+                if (fcb_def && fcb_def->dcb) {
+
+                    xcb->cns_line = fcb_def->dcb->line;
+
+                } else {
+
+                    return(SM_RC_END);
+                }
+
+                fsm_change_state(cns_fcb, __LINE__, FSMXFR_S_ACTIVE);
+                fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xcb->xfr_call_id,
+                               line, CC_FEATURE_DIRTRXFR, NULL);
+
+                return(SM_RC_END);
+            }
+
+            /*
+             * This call is the transferor and we are initiating a local
+             * transfer. So:
+             * 1. Make sure we have a free line to open a new call plane to
+             *    collect digits (and place call) for the transfer target,
+             * 2. Create a new transfer context,
+             * 3. Place this call on hold,
+             * 4. Send a newcall feature back to the GSM so that the
+             *    consultation call can be initiated.
+             */
+
+            /*
+             * Check for any other active features which may block
+             * the transfer.
+             */
+
+            /*
+             * The call must be in the connected state to initiate a transfer.
+             */
+            fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+            if ((fcb_def != NULL) &&
+                (fcb_def->state != FSMDEF_S_CONNECTED) &&
+                (cause != CC_CAUSE_XFER_CNF)) {
+                break;
+            }
+
+            /*
+             * If it's not a conference transfer make sure we have a free
+             * line to start the consultation call.
+             */
+            if (cause != CC_CAUSE_XFER_CNF) {
+                //CSCsz38962 don't use expline for local-initiated transfer
+                //free_lines = lsm_get_instances_available_cnt(line, TRUE);
+                free_lines = lsm_get_instances_available_cnt(line, FALSE);
+                if (free_lines <= 0) {
+                    /*
+                     * No free lines - let the user know and end this request.
+                     */
+                    fsm_display_no_free_lines();
+
+                    break;
+                }
+
+                newcall_line = lsm_get_newcall_line(line);
+                if (newcall_line == NO_LINES_AVAILABLE) {
+                    /*
+                     * Error Pass Limit - let the user know and end this request.
+                     */
+                    lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT);
+                    return (FALSE);
+                }
+            }
+
+            /*
+             * Get a new xcb and new xfr id - This is the handle that will
+             * identify the xfr.
+             */
+            type = ((ftr_id == CC_FEATURE_XFER) ?
+                    (FSMXFR_TYPE_XFR) : (FSMXFR_TYPE_BLND_XFR));
+
+            xcb = fsmxfr_get_new_xfr_context(call_id, line, type,
+                                             CC_XFER_METHOD_REFER,
+                                             FSMXFR_MODE_TRANSFEROR);
+            if (xcb == NULL) {
+                break;
+            }
+            xcb->xfr_orig = src_id;
+            fcb->xcb = xcb;
+            /*
+             * If not conference transfer initiate the consultation call.
+             * For conference transfer we already have both calls setup.
+             */
+            if (cause != CC_CAUSE_XFER_CNF) {
+                /*
+                 * Set the consultative line to new line id.
+                 */
+                xcb->cns_line = newcall_line;
+                /*
+                 * Record the active feature if it is a blind xfer.
+                 */
+                if (ftr_id == CC_FEATURE_BLIND_XFER) {
+                    dcb->active_feature = ftr_id;
+                }
+
+                /*
+                 * This call needs to go on hold so we can start the
+                 * consultation call. Indicate feature indication should
+                 * be send and set the feature reason.
+                 */
+                data.hold.call_info.type = CC_FEAT_HOLD;
+                data.hold.call_info.data.hold_resume_reason = CC_REASON_XFER;
+                data.hold.msg_body.num_parts = 0;
+                data.hold.call_info.data.call_info_feat_data.protect = TRUE;
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
+                               CC_FEATURE_HOLD, &data);
+
+                cns_call_id = xcb->cns_call_id;
+                if (ftr_data->xfer.cause == CC_CAUSE_XFER_LOCAL_WITH_DIALSTRING) {
+                    cc_int_dialstring(CC_SRC_GSM, CC_SRC_GSM, cns_call_id, newcall_line,
+                                      ftr_data->xfer.dialstring, NULL, 0);
+                } else {
+                    data.newcall.cause = CC_CAUSE_XFER_LOCAL;
+                    if (ftr_data->xfer.dialstring[0] != 0) {
+                        data.newcall.cause = CC_CAUSE_XFER_BY_REMOTE;
+                        sstrncpy(data.newcall.dialstring, ftr_data->xfer.dialstring,
+                                 CC_MAX_DIALSTRING_LEN);
+                    }
+
+                    if (ftr_data->xfer.global_call_id[0] != 0) {
+                        sstrncpy(data.newcall.global_call_id,
+                                 ftr_data->xfer.global_call_id, CC_GCID_LEN);
+                    }
+                    data.newcall.prim_call_id = xcb->xfr_call_id;
+                    data.newcall.hold_resume_reason = CC_REASON_XFER;
+
+                    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_call_id, newcall_line,
+                                   CC_FEATURE_NEW_CALL, &data);
+                }
+                FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_XFR_INITIATED),
+                             xcb->xfr_id, call_id, cns_call_id, __LINE__);
+            } else {
+                other_fcb =
+                    fsm_get_fcb_by_call_id_and_type(msg->data.xfer.target_call_id,
+                                                    FSM_TYPE_XFR);
+                if (other_fcb == NULL) {
+                    GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname);
+                    break;
+                }
+
+                other_fcb->xcb = xcb;
+                /*
+                 * The xfr_call_id is the call that is being replaced by
+                 * the cns_call_id.
+                 */
+                fsmxfr_update_xfr_context(xcb, xcb->cns_call_id,
+                                          msg->data.xfer.target_call_id);
+                xcb->cnf_xfr = TRUE;
+                fsm_change_state(other_fcb, __LINE__, FSMXFR_S_ACTIVE);
+                /*
+                 * Emulating user hitting transfer key for second time
+                 * in attended transfer
+                 */
+                cc_int_feature(CC_SRC_UI, CC_SRC_GSM, dcb->call_id,
+                               dcb->line, CC_FEATURE_XFER, NULL);
+            }
+
+            fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            sm_rc = SM_RC_DEF_CONT;
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_NEW_CALL:
+
+            /*
+             * If this is the consultation call involved in a transfer,
+             * then set the rest of the data required to make the transfer
+             * happen. The data is the xcb in the fcb. The data is set now
+             * because we did not have the fcb when the transfer was
+             * initiated. The fcb is created when a new_call event is
+             * received by the FIM, not when a transfer event is received.
+             *
+             * Or this could be the call that originated the
+             * transfer (the transferor) and
+             * the person he was talking to (the transfer target) has
+             * decided to transfer the transferor to another target.
+             */
+
+            /*
+             * Ignore this event if this call is not involved in a transfer.
+             */
+            xcb = fsmxfr_get_xcb_by_call_id(call_id);
+            if (xcb == NULL) {
+                break;
+            }
+            fcb->xcb = xcb;
+
+            fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            sm_rc = SM_RC_DEF_CONT;
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_BLIND_XFER:
+        case CC_FEATURE_XFER:
+            if (msg->data_valid == FALSE) {
+                break;
+            }
+
+            method = msg->data.xfer.method;
+
+            switch (method) {
+            case CC_XFER_METHOD_BYE:
+                /*
+                 * This is a remote initiated transfer using the
+                 * BYE/ALSO method.
+                 *
+                 * The transferor has sent us, the transferee, a BYE with
+                 * the transfer target.
+                 *
+                 * 1. Create a new transfer context
+                 * 2. Ack the feature request.
+                 *
+                 * We need to wait for the RELEASE from SIP and then we will
+                 * send out the NEWCALL to initiate the call to the target.
+                 */
+
+                /*
+                 * Transfer is valid only for a remote originated transfer.
+                 */
+                if (msg->data.xfer.cause != CC_CAUSE_XFER_REMOTE) {
+                    break;
+                }
+
+                /*
+                 * Check for any other active features which may block
+                 * the transfer.
+                 */
+
+                /*
+                 * Get a new xcb and new xfr id - This is the handle that will
+                 * identify the xfr.
+                 */
+                type = ((ftr_id == CC_FEATURE_XFER) ?
+                        (FSMXFR_TYPE_XFR) : (FSMXFR_TYPE_BLND_XFR));
+
+                xcb = fsmxfr_get_new_xfr_context(call_id, line, type, method,
+                                                 FSMXFR_MODE_TRANSFEREE);
+                if (xcb == NULL) {
+                    break;
+                }
+                xcb->xfr_orig = src_id;
+                fcb->xcb = xcb;
+
+                fsmxfr_set_xfer_data(CC_CAUSE_OK, method,
+                                     xcb->cns_call_id,
+                                     FSMXFR_NULL_DIALSTRING, &(data.xfer));
+
+                cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                                   dcb->line, ftr_id, &data, CC_CAUSE_NORMAL);
+
+                /*
+                 * Make sure we have a valid dialstring.
+                 */
+                if (fsmxfr_copy_dialstring(&xcb->dialstring,
+                                           msg->data.xfer.dialstring) == TRUE) {
+                    fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+                } else {
+                    fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                            xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                    fsmxfr_cleanup(fcb, __LINE__, TRUE);
+                }
+
+                break;
+
+            case CC_XFER_METHOD_REFER:
+                /*
+                 * This event is because:
+                 * 1. we are the target involved in a transfer or
+                 * 2. it is a remote initiated transfer using the REFER method.
+                 *
+                 * Case 1:
+                 * The transferee has attempted to setup a call with us,
+                 * the target. The call has been setup successfully so the
+                 * SIP stack is informing us that we are the target.
+                 *
+                 * 1. Ack the request,
+                 * 2. Create a new transfer context.
+                 *
+                 * Case 2:
+                 * The transferor has sent us, the transferee, a REFER with
+                 * the transfer target. We will attempt to contact the target
+                 * and then report the result to the transferor.
+                 *
+                 * 1. Verify that we have a free line to start the
+                 *    consultation call,
+                 * 2. Create a new transfer context,
+                 * 3. Place this call on hold,
+                 * 4. Send a newcall feature back to the GSM so that the
+                 *    consultation call can be initiated.
+                 */
+
+                /*
+                 * Check if this is case 1.
+                 * We know this because the target_call_id can only be set
+                 * for this case.
+                 */
+                if ((msg->data.xfer.cause == CC_CAUSE_XFER_REMOTE) &&
+                    (msg->data.xfer.target_call_id != CC_NO_CALL_ID)) {
+                    type = ((ftr_id == CC_FEATURE_XFER) ?
+                            (FSMXFR_TYPE_XFR) : (FSMXFR_TYPE_BLND_XFR));
+
+                    xcb = fsmxfr_get_new_xfr_context(call_id, line, type,
+                                                     CC_XFER_METHOD_REFER,
+                                                     FSMXFR_MODE_TARGET);
+                    if (xcb == NULL) {
+                        break;
+                    }
+                    xcb->xfr_orig = src_id;
+                    fcb->xcb = xcb;
+
+                    /*
+                     * The xfr_call_id is the call that is being replaced by
+                     * the cns_call_id (which the stack supplied).
+                     */
+                    fsmxfr_update_xfr_context(xcb, xcb->cns_call_id,
+                                              msg->data.xfer.target_call_id);
+                    cns_call_id = xcb->cns_call_id;
+                    /*
+                     * Set the correct xfer_data. The target_call_id must be
+                     * CC_NO_CALL_ID so that the stack can tell that this is
+                     * a case 1 transfer.
+                     */
+                    fsmxfr_set_xfer_data(CC_CAUSE_XFER_REMOTE,
+                                         method,
+                                         CC_NO_CALL_ID,
+                                         FSMXFR_NULL_DIALSTRING, &(data.xfer));
+
+                    cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                                       dcb->line, ftr_id, &data,
+                                       CC_CAUSE_NORMAL);
+
+                    fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+
+                    break;
+                }
+
+                /*
+                 * I guess we are at case 2...
+                 * Transfer should be done only if call's present state
+                 * is either on hold or connected
+                 */
+                fcb_def =
+                    fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
+                if ((fcb_def->state == FSMDEF_S_CONNECTED) ||
+                    (fcb_def->state == FSMDEF_S_CONNECTED_MEDIA_PEND) ||
+                    (fcb_def->state == FSMDEF_S_RESUME_PENDING) ||
+                    (fcb_def->state == FSMDEF_S_HOLD_PENDING) ||
+                    (fcb_def->state == FSMDEF_S_HOLDING)) {
+                    int_rc =
+                        fsmxfr_remote_transfer(fcb, ftr_id, call_id, dcb->line,
+                                               msg->data.xfer.dialstring,
+                                               msg->data.xfer.referred_by);
+                }
+
+                if (int_rc == FALSE) {
+                    fsmxfr_set_xfer_data(CC_CAUSE_ERROR, CC_XFER_METHOD_REFER,
+                                         CC_NO_CALL_ID,
+                                         FSMXFR_NULL_DIALSTRING, &(data.xfer));
+
+                    cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id,
+                                       line, ftr_id, &data, CC_CAUSE_ERROR);
+                }
+                break;
+
+            default:
+                break;
+            }                   /* switch (xfr_data->method) */
+
+            break;
+        case CC_FEATURE_NOTIFY:
+            if (ftr_data->notify.subscription != CC_SUBSCRIPTIONS_XFER) {
+                /* This notify is not for XFER subscription */
+                break;
+            }
+            data.notify.cause        = msg->data.notify.cause;
+            data.notify.cause_code   = msg->data.notify.cause_code;
+            data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+            data.notify.method       = CC_XFER_METHOD_REFER;
+            data.notify.blind_xferror_gsm_id =
+                msg->data.notify.blind_xferror_gsm_id;
+            data.notify.final        = TRUE;
+
+            if (data.notify.blind_xferror_gsm_id == CC_NO_CALL_ID) {
+                if (msg->data.notify.cause == CC_CAUSE_OK) {
+                    data.endcall.cause = CC_CAUSE_OK;
+                    sm_rc = SM_RC_END;
+                    /*
+                     * If dcb is NULL, we received a final NOTIFY after the
+                     * the dcb was cleared. This can happen when we receive
+                     * BYE before final NOTIFY on the result of the REFER
+                     * sent for xfer. If dcb is NULL, just quietly ignore
+                     * the NOTIFY event. Otherwise, kick off the release of
+                     * the call.
+                     */
+                    if (dcb) {
+                        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM,
+                                       dcb->call_id, dcb->line,
+                                       CC_FEATURE_END_CALL, &data);
+                    }
+                }
+            } else {
+                cc_int_feature(CC_SRC_GSM, CC_SRC_SIP,
+                               data.notify.blind_xferror_gsm_id,
+                               line, CC_FEATURE_NOTIFY, &data);
+            }
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            sm_rc = SM_RC_DEF_CONT;
+            break;
+        }                       /* switch (ftr_id) */
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+        sm_rc = SM_RC_DEF_CONT;
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+static sm_rcs_t
+fsmxfr_ev_idle_dialstring (sm_event_t *event)
+{
+    fsm_fcb_t         *fcb     = (fsm_fcb_t *) event->data;
+    cc_feature_t      *msg     = (cc_feature_t *) event->msg;
+    callid_t           call_id = msg->call_id;
+    fsmxfr_xcb_t      *xcb;
+    sm_rcs_t           sm_rc   = SM_RC_CONT;
+
+    /*
+     * Ignore this event if this call is not involved in a transfer.
+     */
+    xcb = fsmxfr_get_xcb_by_call_id(call_id);
+    if (xcb == NULL) {
+        return sm_rc;
+    }
+    fcb->xcb = xcb;
+
+    fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE);
+    return (fsmxfr_ev_active_dialstring(event));
+}
+
+
+
+
+static sm_rcs_t
+fsmxfr_ev_active_proceeding (sm_event_t *event)
+{
+    cc_proceeding_t   *msg     = (cc_proceeding_t *) event->msg;
+    callid_t           call_id = msg->call_id;
+    line_t             line    = msg->line;
+    cc_feature_data_t  data;
+    fsmxfr_xcb_t      *xcb;
+    fsm_fcb_t         *other_fcb;
+    callid_t           other_call_id;
+    line_t             other_line;
+
+    /*
+     * Ignore this event if this call is not involved in a transfer
+     * or we are not the target of the transfer.
+     */
+    xcb = fsmxfr_get_xcb_by_call_id(call_id);
+    if ((xcb == NULL) || (xcb->mode != FSMXFR_MODE_TARGET)) {
+        return (SM_RC_CONT);
+    }
+
+    other_call_id = fsmxfr_get_other_call_id(xcb, call_id);
+    other_line = fsmxfr_get_other_line(xcb, call_id);
+    other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id, FSM_TYPE_DEF);
+
+    /*
+     * Release the transfer call.
+     *
+     * We need to release the transfer call (which is really
+     * the consultation call from the transferor to us
+     * the target), because we want the next call coming in
+     * to replace this one.
+     */
+    data.endcall.cause = CC_CAUSE_REPLACE;
+    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_call_id,
+                   other_line, CC_FEATURE_END_CALL, &data);
+
+    /*
+     * Only answer the call if the call being replaced was connected
+     * or is currently connected, otherwise just let this call be setup
+     * normally so that it will ring.
+     */
+
+    if (other_fcb && (other_fcb->old_state == FSMDEF_S_CONNECTED ||
+        other_fcb->old_state == FSMDEF_S_CONNECTED_MEDIA_PEND ||
+        other_fcb->old_state == FSMDEF_S_RESUME_PENDING ||
+        other_fcb->state == FSMDEF_S_CONNECTED ||
+        other_fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND ||
+        other_fcb->state == FSMDEF_S_RESUME_PENDING)) {
+        cc_int_feature(CC_SRC_UI, CC_SRC_GSM, call_id,
+                       line, CC_FEATURE_ANSWER, NULL);
+    }
+
+    return (SM_RC_CONT);
+}
+
+
+static sm_rcs_t
+fsmxfr_ev_active_connected_ack (sm_event_t *event)
+{
+    fsm_fcb_t          *fcb = (fsm_fcb_t *) event->data;
+    cc_connected_ack_t *msg = (cc_connected_ack_t *) event->msg;
+    fsmxfr_xcb_t       *xcb;
+
+    /*
+     * If we are the target and this is the call from the transferree
+     * to the target, then we need to cleanup the rest of the transfer.
+     * We need to do this because the consultation call is already cleared
+     * from the transfer but the transfer call was not.
+     */
+
+    /*
+     * Ignore this event if this call is not involved in a transfer.
+     */
+    xcb = fsmxfr_get_xcb_by_call_id(msg->call_id);
+    if ((xcb == NULL) || (xcb->mode != FSMXFR_MODE_TARGET)) {
+        return (SM_RC_CONT);
+    }
+
+    /*
+     * Remove this call from the transfer.
+     */
+    fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+            xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+    fsmxfr_cleanup(fcb, __LINE__, FALSE);
+
+    return (SM_RC_CONT);
+}
+
+
+static sm_rcs_t
+fsmxfr_ev_active_release (sm_event_t *event)
+{
+    static const char fname[] = "fsmxfr_ev_active_release";
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_release_t     *msg     = (cc_release_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    fsmdef_dcb_t     *dcb     = fcb->dcb;
+    callid_t          new_call_id;
+    callid_t          other_call_id;
+    line_t            other_line;
+    fsmxfr_xcb_t     *xcb     = fcb->xcb;
+    cc_feature_data_t data;
+    boolean           secondary = FALSE;
+    cc_action_data_t  action_data;
+    fsm_fcb_t        *other_fcb;
+    FSM_DEBUG_SM(DEB_F_PREFIX"Entered. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_ev_active_release"));
+
+    /*
+     * Complete a transfer if we have a pending transfer.
+     */
+    memset(&data, 0, sizeof(cc_feature_data_t));
+
+    /*
+     * Check if this is a transfer of a transfer.
+     */
+    if (xcb == NULL) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find a transfer call to cancel.\n", fname);
+        return (SM_RC_CONT);
+    }
+
+    if ((xcb->active == TRUE) && (xcb->xcb2 != NULL)) {
+        xcb = xcb->xcb2;
+        secondary = TRUE;
+    }
+
+    if ((xcb->dialstring != NULL) && (xcb->dialstring[0] != '\0')) {
+        /*
+         * Grab the call_id for the call to the target.
+         * This will either already be in the xcb or we will need to
+         * get a new one. The call_id will be in the xcb if we are the
+         * transferee.
+         */
+        if (xcb->active == TRUE) {
+            new_call_id = cc_get_new_call_id();
+            fsmxfr_update_xfr_context(xcb, call_id, new_call_id);
+        } else {
+            new_call_id = fsmxfr_get_other_call_id(xcb, call_id);
+            if (secondary == TRUE) {
+                fsmxfr_update_xfr_context(fcb->xcb, call_id, new_call_id);
+            }
+        }
+
+        data.newcall.cause = CC_CAUSE_XFER_REMOTE;
+        sstrncpy(data.newcall.dialstring, xcb->dialstring,
+                 CC_MAX_DIALSTRING_LEN);
+
+        cpr_free(xcb->dialstring);
+        xcb->dialstring = NULL;
+        memset(data.newcall.redirect.redirects[0].number, 0,
+               sizeof(CC_MAX_DIALSTRING_LEN));
+        if (xcb->referred_by != NULL) {
+            sstrncpy(data.newcall.redirect.redirects[0].number,
+                     xcb->referred_by, CC_MAX_DIALSTRING_LEN);
+
+            cpr_free(xcb->referred_by);
+            xcb->referred_by = NULL;
+        }
+
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, new_call_id,
+                       dcb->line, CC_FEATURE_NEW_CALL, &data);
+
+        FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_XFR_INITIATED), xcb->xfr_id,
+                     xcb->xfr_call_id, xcb->cns_call_id, __LINE__);
+
+        if (secondary == TRUE) {
+            fsmxfr_init_xcb(xcb);
+            fcb->xcb->active = FALSE;
+            fcb->xcb->xcb2 = NULL;
+            fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                    xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+            fsmxfr_cleanup(fcb, __LINE__, FALSE);
+        } else if (xcb->active == TRUE) {
+            fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                    xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+            fsmxfr_cleanup(fcb, __LINE__, FALSE);
+            fcb->xcb->active = FALSE;
+        } else {
+            /*
+             * Reset the xcb call_id that was used for the call to the target.
+             * The value was just temporary and it needs to be reset so that
+             * the cleanup function works properly.
+             */
+            fsmxfr_update_xfr_context(xcb, new_call_id, CC_NO_CALL_ID);
+            fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                    xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+            fsmxfr_cleanup(fcb, __LINE__, TRUE);
+        }
+
+        xcb->active = FALSE;
+    } else {
+        if (secondary == TRUE) {
+            /*
+             * This is the secondary transfer of a primary transfer.
+             * We need to:
+             * 1. update the primary xcb to point to this just transferred
+             *    call,
+             * 2. mark the primary xcb as inactive since we just completed the
+             *    secondary transfer,
+             * 3. point the newly transferred call to the primary xcb and
+             *    update the UI to show that this is a local transfer (the
+             *    UI was set as though the call was a remote transfer).
+             * 4. blow away this secondary xcb and cleanup the fcb,
+             */
+            new_call_id = fsmxfr_get_other_call_id(xcb, call_id);
+            fsmxfr_update_xfr_context(fcb->xcb, call_id, new_call_id);
+
+            fcb->xcb->active = FALSE;
+
+            other_fcb = fsm_get_fcb_by_call_id_and_type(new_call_id,
+                                                        FSM_TYPE_XFR);
+            if (other_fcb == NULL) {
+                return (SM_RC_CONT);
+            }
+            other_fcb->xcb = fcb->xcb;
+
+            fsmxfr_init_xcb(xcb);
+
+            action_data.update_ui.action = CC_UPDATE_XFER_PRIMARY;
+            (void)cc_call_action(other_fcb->dcb->call_id, dcb->line,
+                                 CC_ACTION_UPDATE_UI, &action_data);
+
+            fsmxfr_cleanup(fcb, __LINE__, FALSE);
+        } else {
+            /*
+             * One of the parties in the transfer has decided to release
+             * the call, so go ahead and cleanup this transfer.
+             */
+            other_call_id = fsmxfr_get_other_call_id(xcb, call_id);
+            other_line = fsmxfr_get_other_line(xcb, call_id);
+
+            other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                        FSM_TYPE_XFR);
+            if (xcb->cnf_xfr) {
+                /*
+                 * This is the transfer for bridging a transfer call so
+                 * clear the second line also.
+                 */
+                xcb->cnf_xfr = FALSE;
+                if (other_fcb == NULL) {
+                    return (SM_RC_CONT);
+                }
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_call_id,
+                               other_line, CC_FEATURE_END_CALL, NULL);
+            }
+
+            fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id,
+                        CC_SK_EVT_TYPE_IMPLI);
+            fsmxfr_cleanup(fcb, __LINE__, TRUE);
+
+        }
+    }
+
+    return (SM_RC_CONT);
+}
+
+
+static sm_rcs_t
+fsmxfr_ev_active_release_complete (sm_event_t *event)
+{
+    fsmxfr_cleanup((fsm_fcb_t *) event->data, __LINE__, TRUE);
+
+    return (SM_RC_CONT);
+}
+
+static char *fsmxfr_get_dialed_num (fsmdef_dcb_t *dcb)
+{
+    static const char fname[] = "fsmxfr_get_dialed_num";
+     char             *tmp_called_number;
+    /* Get the dialed number only if the call is outgoing type */
+
+    tmp_called_number = lsm_get_gdialed_digits();
+
+    DEF_DEBUG(DEB_F_PREFIX"called_dialed_num = %s\n",
+                DEB_F_PREFIX_ARGS(GSM, fname), tmp_called_number);
+
+    /* Get dialed number to put in the refer-to header. If there
+     * is no dialed number then use RPID or from header value
+     */
+    if (tmp_called_number == NULL || (*tmp_called_number) == NUL) {
+
+        if (dcb->caller_id.called_number[0] != NUL) {
+            DEF_DEBUG(DEB_F_PREFIX"called_dcb_num = %s\n",
+                DEB_F_PREFIX_ARGS(GSM, fname), (char *)dcb->caller_id.called_number);
+            return((char *)dcb->caller_id.called_number);
+
+        } else {
+            DEF_DEBUG(DEB_F_PREFIX"calling_dcb_num = %s\n",
+                DEB_F_PREFIX_ARGS(GSM, fname), (char *)dcb->caller_id.calling_number);
+            return((char *)dcb->caller_id.calling_number);
+        }
+    }
+
+    /*
+     * if tmp_called_number is same as what we receive in RPID,
+     * then get the called name from RPID if provided.
+     */
+    if (dcb->caller_id.called_number != NULL &&
+        dcb->caller_id.called_number[0] != NUL) {
+        /* if Cisco PLAR string is used, use the RPID value */
+        if (strncmp(tmp_called_number, CC_CISCO_PLAR_STRING, sizeof(CC_CISCO_PLAR_STRING)) == 0) {
+            tmp_called_number = (char *)dcb->caller_id.called_number;
+        }
+    }
+
+    return(tmp_called_number);
+}
+
+static void
+fsmxfr_initiate_xfr (sm_event_t *event)
+{
+    static const char fname[] = "fsmxfr_initiate_xfr";
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    fsm_fcb_t        *cns_fcb = NULL;
+    fsmdef_dcb_t     *dcb     = fcb->dcb;
+    fsmdef_dcb_t     *xfr_dcb;
+    fsmdef_dcb_t     *cns_dcb;
+    cc_feature_data_t data;
+    fsmxfr_xcb_t     *xcb     = fcb->xcb;
+    char             *called_num = NULL;
+
+    /*
+     * Place the consultation call on hold.
+     */
+    if (xcb == NULL) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname);
+        return;
+    }
+
+    cns_dcb = fsm_get_dcb(xcb->cns_call_id);
+    cns_fcb = fsm_get_fcb_by_call_id_and_type(xcb->cns_call_id,
+                                              FSM_TYPE_DEF);
+    xfr_dcb = fsm_get_dcb(xcb->xfr_call_id);
+
+    /*
+     * If the consultation call is not connected
+     * treat it like a blind xfer.
+     */
+    if (cns_fcb != NULL) {
+        /*
+         * If the transfer key is pressed twice, before the sofkey gets
+         * updated in response to first transfer key, the 2nd transfer key
+         * press is treated as a transfer complete and we try to initate
+         * the transfer to the connected call itself. To prevent this, check
+         * the state of the call to see if we should
+         * ignore the 2nd transfer key press.
+         */
+        if ((cns_fcb->state == FSMDEF_S_COLLECT_INFO) ||
+            (cns_fcb->state == FSMDEF_S_OUTGOING_PROCEEDING) ||
+            (cns_fcb->state == FSMDEF_S_KPML_COLLECT_INFO)) {
+            FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Ignore the xfer xid %d cid %d %d\n",
+                               DEB_L_C_F_PREFIX_ARGS(FSM, xcb->xfr_line, xcb->xfr_call_id, "fsmxfr_initiate_xfr"),
+                         xcb->xfr_id, xcb->xfr_call_id, xcb->cns_call_id);
+            return;
+        }
+
+        /*
+         * Indicate that xfer completion has been requested
+         */
+        xcb->xfer_comp_req = TRUE;
+
+        if (cns_fcb->state < FSMDEF_S_CONNECTED) {
+            data.endcall.cause = CC_CAUSE_NO_USER_RESP;
+            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_dcb->call_id,
+                           cns_dcb->line, CC_FEATURE_END_CALL, &data);
+            /*
+             * Instruct the stack to transfer the call.
+             */
+            called_num = fsmxfr_get_dialed_num(cns_dcb);
+            if (called_num && called_num[0] != '\0') {
+
+                fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL,
+                                     xcb->method, cns_dcb->call_id,
+                                     called_num,
+                                     &(data.xfer));
+
+                cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xfr_dcb->call_id,
+                               xfr_dcb->line, CC_FEATURE_XFER, &data);
+            } else {
+                /*
+                 * Can't transfer the call without a dialstring, so
+                 * just cleanup the transfer.
+                 */
+                fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                                    xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                fsmxfr_cleanup(fcb, __LINE__, TRUE);
+                if (xcb->cnf_xfr) {
+                    /*
+                     * If it is a conference transfer clear up the
+                     * calls.
+                     */
+                    fsmxfr_cnf_cleanup(xcb);
+                }
+            }
+        } else {
+            /*
+             * If the consulation call is already on hold and
+             * the intial call isn't then place ourselves on
+             * hold (user hit the rocker arm switch). Otherwise,
+             * place the consulation call on hold. Make sure we
+             * do not send feature indication by setting the
+             * call info type to none.
+             */
+            data.hold.call_info.type = CC_FEAT_NONE;
+            data.hold.msg_body.num_parts = 0;
+            if (((cns_fcb->state == FSMDEF_S_HOLDING) ||
+                (cns_fcb->state == FSMDEF_S_HOLD_PENDING)) &&
+                ((fcb->state != FSMDEF_S_HOLDING) &&
+                (fcb->state != FSMDEF_S_HOLD_PENDING))) {
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
+                               CC_FEATURE_HOLD, &data);
+            } else {
+                /* just place on hold */
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM,
+                               cns_dcb->call_id, cns_dcb->line,
+                               CC_FEATURE_HOLD, &data);
+            }
+        }
+    }
+}
+
+static sm_rcs_t
+fsmxfr_ev_active_feature (sm_event_t *event)
+{
+    static const char fname[] = "fsmxfr_ev_active_feature";
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_feature_t     *msg     = (cc_feature_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    line_t            line    = msg->line;
+    fsmdef_dcb_t     *dcb     = fcb->dcb;
+    cc_srcs_t         src_id  = msg->src_id;
+    cc_features_t     ftr_id  = msg->feature_id;
+    cc_feature_data_t *feat_data   = &(msg->data);
+    fsmdef_dcb_t     *xfr_dcb, *cns_dcb;
+    cc_feature_data_t data;
+    sm_rcs_t          sm_rc   = SM_RC_CONT;
+    fsmxfr_xcb_t     *xcb     = fcb->xcb;
+    boolean           int_rc;
+    char              tmp_str[STATUS_LINE_MAX_LEN];
+    char             *called_num = NULL;
+    fsm_fcb_t        *cns_fcb = NULL;
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    if (xcb == NULL) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname);
+        return (SM_RC_CONT);
+    }
+
+    /*
+     * Consume the XFER and NOTIFY events and don't pass them along
+     * to the other FSMs.
+     */
+    if ((ftr_id == CC_FEATURE_BLIND_XFER) ||
+        (ftr_id == CC_FEATURE_XFER) || (ftr_id == CC_FEATURE_NOTIFY)) {
+        if (ftr_id == CC_FEATURE_NOTIFY) {
+            if (msg->data_valid &&
+                (msg->data.notify.subscription != CC_SUBSCRIPTIONS_XFER)) {
+                /* The subscription is not XFER, let the event flow through */
+                return (SM_RC_CONT);
+            }
+        }
+        sm_rc = SM_RC_END;
+    }
+
+    switch (src_id) {
+    case CC_SRC_UI:
+        switch (ftr_id) {
+        case CC_FEATURE_CANCEL:
+            sm_rc = SM_RC_END;
+            fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id,
+                            CC_SK_EVT_TYPE_EXPLI);
+            fsmxfr_cleanup(fcb, __LINE__, TRUE);
+            break;
+
+        case CC_FEATURE_XFER:
+            /* Connect the existing call to active trasnfer state
+             * machine. If the UI generates the event with target
+             * call_id in the data then terminate the existing consulatative
+             * call and link that to another call.
+            */
+            DEF_DEBUG(DEB_F_PREFIX"ACTIVE XFER call_id = %d, cns_id = %d, t_id=%d\n",
+                DEB_F_PREFIX_ARGS(GSM, fname), xcb->xfr_call_id,
+                    feat_data->xfer.target_call_id, xcb->cns_call_id);
+
+            if (feat_data && msg->data_valid &&
+                (xcb->cns_call_id != feat_data->xfer.target_call_id)) {
+
+                cns_fcb = fsm_get_fcb_by_call_id_and_type(xcb->cns_call_id,
+                        FSM_TYPE_DEF);
+
+                if (cns_fcb != NULL) {
+                    DEF_DEBUG(DEB_F_PREFIX"INVOKE ACTIVE XFER call_id = %d, t_id=%d\n",
+                        DEB_F_PREFIX_ARGS(GSM, fname), xcb->xfr_call_id,
+                        feat_data->xfer.target_call_id);
+                    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xcb->xfr_call_id,
+                               xcb->xfr_line, CC_FEATURE_DIRTRXFR, NULL);
+                }
+
+                return(SM_RC_END);
+            }
+            switch (xcb->method) {
+            case CC_XFER_METHOD_BYE:
+            case CC_XFER_METHOD_REFER:
+                /*
+                 * This is the second transfer event for a local
+                 * attended transfer with consultation.
+                 *
+                 * The user is attempting to complete the transfer, so
+                 * 1. place the consultation call on hold,
+                 * 2. instruct the stack to transfer the call.
+                 */
+                fsmxfr_initiate_xfr(event);
+                lsm_set_hold_ringback_status(xcb->cns_call_id, FALSE);
+                break;
+
+            default:
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                break;
+            }                   /* switch (ftr_id) { */
+            break;
+        case CC_FEATURE_RESUME:
+            break;
+
+        case CC_FEATURE_END_CALL:
+            if (xcb->mode == FSMXFR_MODE_TRANSFEREE) {
+                xfr_dcb = fsm_get_dcb(xcb->xfr_call_id);
+                if (call_id == xcb->cns_call_id) {
+                    /*
+                     * Transferee ended call before transfer was completed.
+                     * Notify the transfer call of the status of the
+                     * transfer.
+                     */
+                    data.notify.cause        = CC_CAUSE_ERROR;
+                    data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+                    data.notify.method       = CC_XFER_METHOD_REFER;
+                    data.notify.final        = TRUE;
+                    cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xfr_dcb->call_id,
+                                   xfr_dcb->line, CC_FEATURE_NOTIFY, &data);
+                }
+            }
+            lsm_set_hold_ringback_status(xcb->cns_call_id, TRUE);
+            fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id,
+                                CC_SK_EVT_TYPE_IMPLI);
+            fsmxfr_cleanup(fcb, __LINE__, TRUE);
+            break;
+
+        case CC_FEATURE_HOLD:
+            if ((msg->data_valid) &&
+                (feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_SWAP ||
+                feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_XFER ||
+                feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_INTERNAL))
+            {
+                feat_data->hold.call_info.data.call_info_feat_data.protect = TRUE;
+            } else {
+                DEF_DEBUG(DEB_F_PREFIX"Invoke hold call_id = %d t_call_id=%d\n",
+                        DEB_F_PREFIX_ARGS(GSM, fname), xcb->xfr_call_id, xcb->cns_call_id);
+                //Actual hold to this call, so break the feature layer.
+                ui_terminate_feature(xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id);
+                fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                            xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                fsmxfr_cleanup(fcb, __LINE__, TRUE);
+            }
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_DIRTRXFR:
+            xfr_dcb = fsm_get_dcb(xcb->xfr_call_id);
+            called_num = fsmxfr_get_dialed_num(xfr_dcb);
+
+            if (called_num && called_num[0] != '\0') {
+                fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL,
+                        xcb->method, xcb->cns_call_id,
+                        called_num,
+                        &(data.xfer));
+
+                data.xfer.method = CC_XFER_METHOD_DIRXFR;
+
+                cc_int_feature(CC_SRC_GSM, CC_SRC_SIP,
+                        call_id, line,
+                        CC_FEATURE_XFER, &data);
+            } else {
+                /*
+                * Can't transfer the call without a dialstring, so
+                * just cleanup the transfer.
+                */
+                fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                                    xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                fsmxfr_cleanup(fcb, __LINE__, TRUE);
+            }
+            return(SM_RC_END);
+        case CC_FEATURE_END_CALL:
+            /*
+             * Only cleanup the whole xfer if we know that we don't have
+             * an outstanding blind transfer and if we are not the target.
+             * We need the xcb to hang around because other users (lsm,...)
+             * may still want to do something special and the xcb is the only
+             * way they know that the call is involved in a transfer.
+             */
+            if (xcb->type == FSMXFR_TYPE_BLND_XFR) {
+                fsmxfr_cleanup(fcb, __LINE__, FALSE);
+            } else if ((xcb->type == FSMXFR_TYPE_XFR) &&
+                       (msg->data.endcall.cause == CC_CAUSE_NO_USER_RESP)) {
+
+                DEF_DEBUG(DEB_F_PREFIX"Xfer type =%d\n",
+                        DEB_F_PREFIX_ARGS(GSM, fname), xcb->type);
+
+                if ((platGetPhraseText(STR_INDEX_TRANSFERRING,
+                                             (char *) tmp_str,
+                                             STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) {
+                    lsm_ui_display_status(tmp_str, xcb->xfr_line, xcb->xfr_call_id);
+                }
+
+                if (xcb->xfer_comp_req == FALSE) {
+                    fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                                    xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                } else {
+                    // Mark it as transfer complete.
+                    fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id,
+                                    xcb->xfr_call_id, TRUE);
+                }
+                fsmxfr_cleanup(fcb, __LINE__, FALSE);
+            } else if (xcb->mode == FSMXFR_MODE_TARGET) {
+                break;
+            } else {
+                /* Early attended transfer generates internal END_CALL event
+                 * do not send cancel in that case
+                 */
+                if (xcb->xfer_comp_req == FALSE) {
+                    fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                                    xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                }
+                fsmxfr_cleanup(fcb, __LINE__, TRUE);
+            }
+            break;
+
+        case CC_FEATURE_HOLD:
+            ui_set_local_hold(dcb->line, dcb->call_id);
+            if(msg->data_valid) {
+                feat_data->hold.call_info.data.call_info_feat_data.protect = TRUE;
+            }
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+        break;
+
+    case CC_SRC_SIP:
+        switch (ftr_id) {
+        case CC_FEATURE_CALL_PRESERVATION:
+            DEF_DEBUG(DEB_F_PREFIX"Preservation call_id = %d t_call_id=%d\n",
+                        DEB_F_PREFIX_ARGS(GSM, fname), xcb->xfr_call_id, xcb->cns_call_id);
+            ui_terminate_feature(xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id);
+            fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                            xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+            fsmxfr_cleanup(fcb, __LINE__, TRUE);
+            break;
+
+        case CC_FEATURE_BLIND_XFER:
+        case CC_FEATURE_XFER:
+            if (msg->data_valid == FALSE) {
+                break;
+            }
+
+            /*
+             * Transfer is valid only for a remote originated transfer.
+             */
+            if (msg->data.xfer.cause != CC_CAUSE_XFER_REMOTE) {
+                break;
+            }
+
+            /*
+             * This call is already involved in a transfer as the transferor,
+             * but one of the parties, the transferee or target has decided to
+             * transfer that leg also. So, now we have a transfer of a transfer.
+             */
+            switch (msg->data.xfer.method) {
+            case CC_XFER_METHOD_BYE:
+                /*
+                 * Check for any other active features which may block
+                 * the transfer.
+                 */
+
+                fsmxfr_set_xfer_data(CC_CAUSE_OK, CC_XFER_METHOD_BYE,
+                                     CC_NO_CALL_ID,
+                                     FSMXFR_NULL_DIALSTRING, &(data.xfer));
+
+                cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                                   dcb->line, ftr_id, &data, CC_CAUSE_NORMAL);
+
+                /*
+                 * Make sure we have a valid dialstring.
+                 */
+                if (fsmxfr_copy_dialstring(&xcb->dialstring,
+                                           msg->data.xfer.dialstring) == FALSE) {
+                    fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id,
+                                CC_SK_EVT_TYPE_IMPLI);
+                    fsmxfr_cleanup(fcb, __LINE__, TRUE);
+                    break;
+                }
+
+                /*
+                 * Mark the active flag in the xcb. This flag is used later
+                 * when the RELEASE comes from SIP, so that the GSM
+                 * knows that this call is still involved in a transfer.
+                 */
+                xcb->active = TRUE;
+
+                break;
+
+            case CC_XFER_METHOD_REFER:
+                int_rc = fsmxfr_remote_transfer(fcb, ftr_id, call_id, dcb->line,
+                                                msg->data.xfer.dialstring,
+                                                msg->data.xfer.referred_by);
+
+                if (int_rc == FALSE) {
+                    fsmxfr_set_xfer_data(CC_CAUSE_ERROR, CC_XFER_METHOD_REFER,
+                                         CC_NO_CALL_ID,
+                                         FSMXFR_NULL_DIALSTRING, &(data.xfer));
+
+                    cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id,
+                                       dcb->line, ftr_id, &data,
+                                       CC_CAUSE_ERROR);
+                }
+                break;
+
+            default:
+                break;
+            }                   /* switch (msg->data.xfer.method) */
+
+            break;
+
+        case CC_FEATURE_NOTIFY:
+            /*
+             * This could be:
+             * 1. for an unattended transfer.
+             *    The transferee is notifying us, the transferor, of the status
+             *    of the transfer, ie. was the transferee able to connect to
+             *    the target?
+             *
+             * 2. Or this is an attended transfer, and this is the call from
+             *    the transferee to the target. The stack will NOTIFY the GSM
+             *    of the status of the call after it receives a message
+             *    from the network indicating success of failure.
+             */
+            if (msg->data_valid == FALSE) {
+                break;
+            }
+
+            /*
+             * Ack the request.
+             */
+            cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
+                               dcb->line, CC_FEATURE_NOTIFY, NULL,
+                               CC_CAUSE_NORMAL);
+
+            switch (xcb->type) {
+            case FSMXFR_TYPE_BLND_XFR:
+                switch (msg->data.notify.method) {
+                case CC_XFER_METHOD_BYE:
+                    /*
+                     * This notification is really from the SIP stack.
+                     * The network will not send a NOTIFY for a BYE/Also
+                     * transfer, so the SIP stack just sends one up when
+                     * it uses that method.
+                     */
+
+                    /*
+                     * Release the transfer call.
+                     */
+                    data.endcall.cause = CC_CAUSE_OK;
+                    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                                   dcb->line, CC_FEATURE_END_CALL, &data);
+                    break;
+
+                case CC_XFER_METHOD_REFER:
+                    if (msg->data.notify.cause == CC_CAUSE_OK) {
+                        /*
+                         * Release the transfer call.
+                         */
+                        /* Set the dcb flag to indicate transfer is complete, so that
+                         * it won't display endcall in this case
+                         */
+                        fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id,
+                                    xcb->xfr_call_id, TRUE);
+
+                        data.endcall.cause = CC_CAUSE_OK;
+                        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                                       dcb->line, CC_FEATURE_END_CALL, &data);
+                    } else {
+                        fsmxfr_cleanup(fcb, __LINE__, TRUE);
+                        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                                       dcb->line, CC_FEATURE_RESUME, NULL);
+                    }
+                    break;
+
+                default:
+                    break;
+                }               /* switch (msg->data.notify.method) { */
+
+                break;
+            case FSMXFR_TYPE_DIR_XFR:
+                /*
+                 * Clear the transfer call if the transfer was OK,
+                 * else just cleanup the transfer. The consultation
+                 * call will be released by the target.
+                */
+                xfr_dcb = fsm_get_dcb(xcb->xfr_call_id);
+                if (msg->data.notify.cause == CC_CAUSE_OK) {
+                        data.endcall.cause = CC_CAUSE_OK;
+
+                    fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id,
+                                    xcb->xfr_call_id, TRUE);
+                    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM,
+                        xfr_dcb->call_id,
+                        xfr_dcb->line, CC_FEATURE_END_CALL,
+                        &data);
+                } else {
+                    lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED),
+                            dcb->line, xcb->xfr_call_id);
+                    lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED),
+                            dcb->line, xcb->cns_call_id);
+                }
+                if (xcb->cnf_xfr) {
+                    /*
+                     * If it is a conference transfer clear up the
+                     * calls.
+                     */
+                    fsmxfr_cnf_cleanup(xcb);
+                }
+                break;
+
+            case FSMXFR_TYPE_XFR:
+                switch (msg->data.notify.method) {
+                case CC_XFER_METHOD_BYE:
+                    /*
+                     * Release the consultation call.
+                     */
+                    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xcb->cns_call_id,
+                                   dcb->line, CC_FEATURE_END_CALL, NULL);
+
+                    /*
+                     * Release the call being transferred.
+                     */
+                    cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xcb->xfr_call_id,
+                                   dcb->line, CC_FEATURE_END_CALL, NULL);
+                    break;
+
+                case CC_XFER_METHOD_REFER:
+                    /*
+                     * This notification is from either the target (which is
+                     * the consultation call) or the transferee (which is the
+                     * transfer call).
+                     *
+                     * So, do the following based on each case:
+                     *
+                     * 1. consultation call: this is the transferee receiving
+                     *    notification of the status of the call from the
+                     *    transferee to the target.
+                     *    - notify the transfer call.
+                     *    - the transfer call will be released by the transferor
+                     *      after receiving the above notification and the
+                     *      consultation call will remain.
+                     *
+                     * 2. transfer call: this is the transferor receiving
+                     *    notification of the status of the call from the
+                     *    transferee to the target.
+                     *    - release the transfer call.
+                     *    - the consultation call will be released by the target.
+                     */
+                    xfr_dcb = fsm_get_dcb(xcb->xfr_call_id);
+                    if (call_id == xcb->cns_call_id) {
+                        /*
+                         * Notify the transfer call of the status of the
+                         * transfer.
+                         */
+                        data.notify.cause        = msg->data.notify.cause;
+                        data.notify.cause_code   = msg->data.notify.cause_code;
+                        data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+                        data.notify.method       = CC_XFER_METHOD_REFER;
+                        data.notify.blind_xferror_gsm_id =
+                            msg->data.notify.blind_xferror_gsm_id;
+                        data.notify.final        = TRUE;
+                        if (data.notify.blind_xferror_gsm_id == CC_NO_CALL_ID) {
+                            cc_int_feature(CC_SRC_GSM, CC_SRC_SIP,
+                                           xfr_dcb->call_id, xfr_dcb->line,
+                                           CC_FEATURE_NOTIFY, &data);
+                        } else {
+                            cc_int_feature(CC_SRC_GSM, CC_SRC_SIP,
+                                           data.notify.blind_xferror_gsm_id,
+                                           msg->line, CC_FEATURE_NOTIFY, &data);
+                        }
+                    } else {
+                        /*
+                         * Clear the transfer call if the transfer was OK,
+                         * else just cleanup the transfer. The consultation
+                         * call will be released by the target.
+                         */
+                        if (xcb == NULL) {
+                            GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname);
+                            break;
+                        }
+
+                        if (msg->data.notify.cause == CC_CAUSE_OK) {
+                            data.endcall.cause = CC_CAUSE_OK;
+
+                            fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id,
+                                        xcb->xfr_call_id, TRUE);
+                            cc_int_feature(CC_SRC_GSM, CC_SRC_GSM,
+                                           xfr_dcb->call_id,
+                                           xfr_dcb->line, CC_FEATURE_END_CALL,
+                                           &data);
+                        } else {
+                            lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED),
+                                                  dcb->line, xcb->xfr_call_id);
+                            fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id,
+                                        xcb->xfr_call_id, FALSE);
+                            fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                                                      xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                            /*
+                             * Resume the consultation call.
+                             * if xcb->cns_call_id == 0, then cns call is already ended.
+                             */
+                            if (xcb->cns_call_id != CC_NO_CALL_ID) {
+                                       lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED),
+                                                      dcb->line, xcb->cns_call_id);
+                                cns_dcb = fsm_get_dcb (xcb->cns_call_id);
+                                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM,
+                                               xcb->cns_call_id, cns_dcb->line,
+                                               CC_FEATURE_RESUME, NULL);
+                            }
+
+                            if (xcb->cnf_xfr) {
+                                /*
+                                 * If it is a conference transfer clear up the
+                                 * calls.
+                                 */
+                                fsmxfr_cnf_cleanup(xcb);
+                            }
+                        }
+                        fsmxfr_cleanup(fcb, __LINE__, TRUE);
+                    }           /* if (call_id == xcb->cns_call_id)  */
+                    break;
+
+                default:
+                    break;
+                }               /* switch (msg->data.notify.method) { */
+
+                break;
+
+            default:
+                break;
+            }                   /* switch (xcb->type) { */
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            break;
+        }                       /* switch (ftr_id) */
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+static void
+fsmxfr_requeue_blind_xfer_dialstring (fsmdef_dcb_t *dcb, fsmxfr_xcb_t *xcb)
+{
+    /*
+     * This is the result of the feature hold so we
+     * can clear the active feature.
+     */
+    dcb->active_feature = CC_FEATURE_NONE;
+
+    /*
+     * If there is a dialstring on the xcb, it is because the
+     * dialstring event for the consultative call has already
+     * been received. We delayed handling the dialstring until
+     * the result of the hold request has been received. We
+     * are now ready to process the dial string so requeue it
+     * for processing
+     */
+    if (xcb->queued_dialstring && xcb->queued_dialstring[0] != '\0') {
+        cc_dialstring(CC_SRC_UI, xcb->cns_call_id, dcb->line,
+                      xcb->queued_dialstring);
+        cpr_free(xcb->queued_dialstring);
+        xcb->queued_dialstring = NULL;
+    }
+}
+
+static sm_rcs_t
+fsmxfr_ev_active_feature_ack (sm_event_t *event)
+{
+    static const char fname[] = "fsmxfr_ev_active_feature_ack";
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_feature_ack_t *msg     = (cc_feature_ack_t *) event->msg;
+    cc_srcs_t         src_id  = msg->src_id;
+    cc_features_t     ftr_id  = msg->feature_id;
+    fsmdef_dcb_t     *dcb     = fcb->dcb;
+    cc_feature_data_t data;
+    sm_rcs_t          sm_rc   = SM_RC_CONT;
+    fsmxfr_xcb_t     *xcb     = fcb->xcb;
+    fsmdef_dcb_t     *xfr_dcb = NULL;
+    char             *called_num = NULL;
+
+    fsm_sm_ftr(ftr_id, src_id);
+
+    /*
+     * Consume the XFER events and don't pass them along to the other FSMs.
+     */
+    if ((ftr_id == CC_FEATURE_BLIND_XFER) || (ftr_id == CC_FEATURE_XFER)) {
+        sm_rc = SM_RC_END;
+    }
+
+    if (xcb == NULL) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname);
+        return (sm_rc);
+    }
+
+    switch (src_id) {
+    case CC_SRC_SIP:
+    case CC_SRC_GSM:
+        switch (ftr_id) {
+        case CC_FEATURE_BLIND_XFER:
+            switch (xcb->type) {
+            case FSMXFR_TYPE_BLND_XFR:
+                switch (msg->data.xfer.method) {
+                case CC_XFER_METHOD_REFER:
+                    /*
+                     * Clear the call if the transfer was OK, else just cleanup
+                     * the transfer.
+                     */
+                    if (msg->cause == CC_CAUSE_OK) {
+                        // This does not indicate that transfer was successful. So wait for the NOTIFYs.
+                        //data.endcall.cause = CC_CAUSE_OK;
+
+                        //cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
+                                       //dcb->line, CC_FEATURE_END_CALL, &data);
+                    } else {
+                        fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                            xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                        fsmxfr_cleanup(fcb, __LINE__, TRUE);
+                    }
+                    break;
+
+                default:
+                    fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                    break;
+                }               /* switch (msg->data.xfer.method) { */
+
+                break;
+
+            default:
+                break;
+            }                   /* switch (xcb->type) { */
+            break;
+
+        case CC_FEATURE_HOLD:
+            if (msg->cause == CC_CAUSE_REQUEST_PENDING) {
+                /*
+                 * HOLD request is pending. Let this event drop through
+                 * to the default sm for handling.
+                 */
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+                break;
+            }
+
+            /*
+             * If this is the hold response during a blind transfer,
+             * check to see if a dialstring has been queued for processing.
+             */
+            if (xcb->type == FSMXFR_TYPE_BLND_XFR &&
+                xcb->xfr_call_id == fcb->call_id) {
+                fsmxfr_requeue_blind_xfer_dialstring(dcb, xcb);
+                break;
+            }
+
+            /*
+             * If xfer soft key has not been pressed a second time then
+             * don't transfer the call.
+             */
+            if (!xcb->xfer_comp_req) {
+                break;
+            }
+
+
+            /* If there is any error reported from SIP stack then clear the
+             * transfer data. One such case is where digest authentication
+             * fails due to maximum retry (of 2). SIP stack generates a valid
+             * ACK event by setting cause code to ERROR
+             */
+            if (msg->cause == CC_CAUSE_ERROR) {
+
+                lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED),
+                                      xcb->xfr_line, xcb->xfr_call_id);
+                lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED),
+                                      xcb->cns_line, xcb->cns_call_id);
+                fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id,
+                                CC_SK_EVT_TYPE_IMPLI);
+                fsmxfr_cleanup(fcb, __LINE__, TRUE);
+
+                break;
+            }
+
+            /*
+             * Instruct the stack to transfer the call.
+             */
+            if (xcb->type == FSMXFR_TYPE_XFR) {
+                /*
+                 * Check to see which side of the transfer
+                 * the request is coming from. if it is from
+                 * the XFR side, make sure the CNS side is
+                 * indeed in the Held state.
+                 */
+                if (xcb->cns_call_id == fcb->call_id) {
+                    xfr_dcb = fsm_get_dcb(xcb->xfr_call_id);
+
+                    called_num = fsmxfr_get_dialed_num(fcb->dcb);
+
+                    if (called_num && called_num[0] != '\0') {
+                        fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL,
+                                             xcb->method, fcb->dcb->call_id,
+                                             called_num,
+                                             &(data.xfer));
+                        cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xfr_dcb->call_id,
+                                       xfr_dcb->line, CC_FEATURE_XFER, &data);
+                    } else {
+                        /*
+                         * Can't transfer the call without a dialstring, so
+                         * just cleanup the transfer.
+                         */
+                        fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                                                xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                        fsmxfr_cleanup(fcb, __LINE__, TRUE);
+                    }
+                } else {
+                    /*
+                     * This is hold response on the xfer call leg.
+                     *
+                     * Check to see if dialstring has been queued for
+                     * processing.  If so, it is requeued to GSM and
+                     * we delay until the consultative call is setup.
+                     */
+                    //if (fsmxfr_requeue_blind_xfer_dialstring(dcb, xcb)) {
+                    //    break;
+                    //}
+
+                    /*
+                     * Get the dcb of the consultative call.
+                     */
+                    xfr_dcb = fsm_get_dcb(xcb->cns_call_id);
+
+                    /*
+                     * We must wait for the hold request on the consultative
+                     * call to complete before completing the xfer. The state
+                     * of the consultative call must be FSMDEF_S_HOLDING.
+                     * FSMDEF_S_HOLDING is not a completed hold request which
+                     * is why it is not checked for here.
+                     */
+                    if (xfr_dcb->fcb->state != FSMDEF_S_HOLDING) {
+                        break;
+                    } else {
+
+                        called_num = fsmxfr_get_dialed_num(xfr_dcb);
+
+                        if (called_num && called_num[0] != '\0') {
+                            fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL,
+                                                 xcb->method, xfr_dcb->call_id,
+                                                 called_num,
+                                                 &(data.xfer));
+                            cc_int_feature(CC_SRC_GSM, CC_SRC_SIP,
+                                           fcb->dcb->call_id, fcb->dcb->line,
+                                           CC_FEATURE_XFER, &data);
+                        } else {
+                            /*
+                             * Can't transfer the call without a dialstring, so
+                             * just cleanup the transfer.
+                             */
+                            fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                                                    xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                            fsmxfr_cleanup(fcb, __LINE__, TRUE);
+                        }
+                    }
+                }
+            }
+            break;
+
+        case CC_FEATURE_XFER:
+
+            if (msg->cause != CC_CAUSE_OK) {
+                lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED),
+                                      xcb->xfr_line, xcb->xfr_call_id);
+                lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED),
+                                      xcb->cns_line, xcb->cns_call_id);
+                fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id,
+                                    xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+                fsmxfr_cleanup(fcb, __LINE__, TRUE);
+            } else {
+                fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+            }
+            break;
+
+        default:
+            fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
+
+            break;
+        }                       /* switch (ftr_id) */
+
+        break;
+
+    default:
+        fsm_sm_ignore_src(fcb, __LINE__, src_id);
+
+        break;
+    }                           /* switch (src_id) */
+
+    return (sm_rc);
+}
+
+
+static sm_rcs_t
+fsmxfr_ev_active_onhook (sm_event_t *event)
+{
+    static const char fname[] = "fsmxfr_ev_active_onhook";
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_onhook_t      *msg     = (cc_onhook_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    callid_t          other_call_id;
+    fsmxfr_xcb_t     *xcb     = fcb->xcb;
+    fsm_fcb_t        *other_fcb;
+    fsmdef_dcb_t     *xfr_dcb;
+    cc_feature_data_t data;
+    fsm_fcb_t        *cns_fcb, *xfr_fcb;
+    int               onhook_xfer = 0;
+
+    if (xcb == NULL) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname);
+        return (SM_RC_CONT);
+    }
+
+    cns_fcb = fsm_get_fcb_by_call_id_and_type(xcb->cns_call_id, FSM_TYPE_DEF);
+    xfr_fcb = fsm_get_fcb_by_call_id_and_type(xcb->xfr_call_id, FSM_TYPE_DEF);
+
+    if (xcb->cnf_xfr) {
+        /*
+         * This is the conference transfer so clear the
+         * second line also.
+         */
+        xcb->cnf_xfr = FALSE;
+        other_call_id = fsmxfr_get_other_call_id(xcb, call_id);
+        other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
+                                                    FSM_TYPE_XFR);
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_call_id,
+                       other_fcb ? other_fcb->dcb->line:CC_NO_LINE, CC_FEATURE_END_CALL, NULL);
+        fsmxfr_cleanup(fcb, __LINE__, TRUE);
+        return (SM_RC_CONT);
+    }
+
+    if (xcb->mode == FSMXFR_MODE_TRANSFEREE) {
+        xfr_dcb = fsm_get_dcb(xcb->xfr_call_id);
+        if (call_id == xcb->cns_call_id) {
+            /*
+             * Transferee ended call before transfer was completed.
+             * Notify the transfer call of the status of the
+             * transfer.
+             *
+             * Note: This must be changed when fix is put in for configurable
+             *       onhook xfer (CSCsb86757) so that 200 NOTIFY is sent when
+             *       xfer is completed. fsmxfr_initiate_xfr will take care
+             *       of this for us so just need to make sure the NOTIFY is
+             *       sent to SIP stack only when xfer is abandoned due to
+             *       onhook.
+             */
+            data.notify.cause        = CC_CAUSE_ERROR;
+            data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+            data.notify.method       = CC_XFER_METHOD_REFER;
+            data.notify.final        = TRUE;
+            cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xfr_dcb->call_id,
+                           xfr_dcb->line, CC_FEATURE_NOTIFY, &data);
+            if (cns_fcb && cns_fcb->state != FSMDEF_S_HOLDING &&
+                cns_fcb->state != FSMDEF_S_HOLD_PENDING) {
+                fsmxfr_feature_cancel(xcb, xfr_dcb->line,
+                                    xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+
+                fsmxfr_cleanup(fcb, __LINE__, TRUE);
+            }
+            /*
+             * fix bug CSCtb23681.
+            */
+            if( xfr_dcb->fcb->state == FSMDEF_S_HOLDING )
+                cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xfr_dcb->call_id,xfr_dcb->line, CC_FEATURE_END_CALL, NULL);
+            return (SM_RC_CONT);
+        }
+    }
+
+    if (msg->softkey) {
+        /*
+         * Softkey set to TRUE indicates endcall softkey was pressed.
+         * This causes the call with focus to release.
+         */
+        if ((call_id == xcb->cns_call_id) &&
+            (cns_fcb->state == FSMDEF_S_HOLDING ||
+             cns_fcb->state == FSMDEF_S_HOLD_PENDING)) {
+            /* ignore the onhook event for the held consultation call */
+        } if (msg->active_list == CC_REASON_ACTIVECALL_LIST) {
+            /* Active call list has been requested also
+             * existing consult call is canceled
+             * But transfer state machine will remain
+             * intact as feature layer is still running.*/
+            xcb->cns_call_id = CC_NO_CALL_ID;
+            xcb->cns_line = CC_NO_LINE;
+
+        }else {
+            fsmxfr_feature_cancel(xcb, xcb->xfr_line,
+                                    xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+
+            fsmxfr_cleanup(fcb, __LINE__, TRUE);
+        }
+        return (SM_RC_CONT);
+    } else {
+        /*
+         * Softkey set to FALSE indicates handset, speaker button, or
+         * headset went onhook.  This causes the transfer to complete
+         * if onhook xfer is enabled by config.
+         */
+        config_get_value(CFGID_XFR_ONHOOK_ENABLED, &onhook_xfer,
+                         sizeof(onhook_xfer));
+        if (onhook_xfer && ((cns_fcb->state == FSMDEF_S_OUTGOING_ALERTING)||
+            (cns_fcb->state == FSMDEF_S_CONNECTED))) {
+            fsmxfr_initiate_xfr(event);
+            return (SM_RC_END);
+        } else if (onhook_xfer && xfr_fcb &&
+                ((xfr_fcb->state == FSMDEF_S_OUTGOING_ALERTING)||
+                (xfr_fcb->state == FSMDEF_S_CONNECTED))) {
+            fsmxfr_initiate_xfr(event);
+            return (SM_RC_END);
+        } else {
+            fsmxfr_feature_cancel(xcb, xcb->xfr_line,
+                                    xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI);
+
+            fsmxfr_cleanup(fcb, __LINE__, TRUE);
+            return (SM_RC_CONT);
+        }
+    }
+}
+
+
+/*
+ * This event can only happen if the user initiated a blind transfer.
+ */
+static sm_rcs_t
+fsmxfr_ev_active_dialstring (sm_event_t *event)
+{
+    static const char fname[] = "fsmxfr_ev_active_dialstring";
+    fsm_fcb_t        *fcb     = (fsm_fcb_t *) event->data;
+    cc_dialstring_t  *msg     = (cc_dialstring_t *) event->msg;
+    callid_t          call_id = msg->call_id;
+    line_t             line    = msg->line;
+    fsmdef_dcb_t     *xfr_dcb;
+    fsmxfr_xcb_t     *xcb     = fcb->xcb;
+    cc_feature_data_t data;
+    char             *dialstring;
+
+    /*
+     * Make sure we have a valid dialstring.
+     */
+    dialstring = msg->dialstring;
+    if ((dialstring == NULL) || (dialstring[0] == '\0')) {
+        FSM_DEBUG_SM(DEB_L_C_F_PREFIX"dialstring= %c\n",
+                       DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, call_id, fname), '\0');
+        return (SM_RC_END);
+    }
+
+    FSM_DEBUG_SM(DEB_L_C_F_PREFIX"dialstring= %s\n",
+               DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, call_id, fname), dialstring);
+
+    /*
+     * If this is a blind xfer and we have received the dialstring
+     * before the original call has received a response to the hold
+     * request, defer processing the dialstring event. active_feature
+     * will be set to CC_FEATURE_BLIND_XFER if we are still waiting
+     * for the hold response. The dial string is saved on the xcb
+     * until needed.
+     */
+    if (xcb == NULL) {
+        return (SM_RC_END);
+    }
+
+    xfr_dcb = fsm_get_dcb(xcb->xfr_call_id);
+    if (xfr_dcb == NULL) {
+        return (SM_RC_END);
+    }
+
+    if (xfr_dcb->active_feature == CC_FEATURE_BLIND_XFER) {
+        if (!fsmxfr_copy_dialstring(&xcb->queued_dialstring, dialstring)) {
+            GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX"unable to copy dialstring\n",
+                            msg->line, call_id, fname);
+        }
+        return (SM_RC_END);
+    }
+
+    if (xcb->type == FSMXFR_TYPE_BLND_XFR) {
+        lsm_set_hold_ringback_status(xcb->cns_call_id, FALSE);
+    }
+    /*
+     * Make sure that this event came from the consultation call and that
+     * this is a blind transfer. We ignore the event if this is a transfer
+     * with consultation, because we want to talk to the target. For an
+     * unattended transfer this event is the final event needed to complete
+     * the transfer.
+     */
+    if ((xcb->cns_call_id != call_id) || (xcb->type != FSMXFR_TYPE_BLND_XFR)) {
+        return (SM_RC_CONT);
+    }
+
+    switch (xcb->method) {
+    case CC_XFER_METHOD_BYE:
+    case CC_XFER_METHOD_REFER:
+        /*
+         * This is an unattended transfer so:
+         * 1. clear the consultation call,
+         * 2. instruct the stack to initiate the transfer.
+         */
+
+        /*
+         * Release this call because it was only used to collect digits.
+         */
+        data.endcall.cause = CC_CAUSE_NORMAL;
+        cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, call_id,
+                       line, CC_FEATURE_END_CALL, &data);
+
+
+        /*
+         * Send an event to the transferer call leg so it can send the called
+         * number to the transferee.
+         */
+        fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL, xcb->method, CC_NO_CALL_ID,
+                             dialstring, &(data.xfer));
+
+        cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xcb->xfr_call_id,
+                       line, fsmxfr_type_to_feature(xcb->type), &data);
+
+        FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_XFR_INITIATED),
+                     xcb->xfr_id, xcb->xfr_call_id, xcb->cns_call_id, __LINE__);
+
+        break;
+
+    default:
+        break;
+    }                           /* switch (xcb->method) */
+
+    return (SM_RC_END);
+}
+
+
+cc_int32_t
+fsmxfr_show_cmd (cc_int32_t argc, const char *argv[])
+{
+    fsmxfr_xcb_t   *xcb;
+    int             i = 0;
+
+    /*
+     * Check if need help.
+     */
+    if ((argc == 2) && (argv[1][0] == '?')) {
+        debugif_printf("show fsmxfr\n");
+        return (0);
+    }
+
+    debugif_printf("\n------------------------ FSMXFR xcbs -------------------------");
+    debugif_printf("\ni   xfr_id  xcb         type  method  xfr_call_id  cns_call_id");
+    debugif_printf("\n--------------------------------------------------------------\n");
+
+    FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) {
+        debugif_printf("%-2d  %-6d  0x%8p  %-4d  %-6d  %-11d  %-11d\n",
+                       i++, xcb->xfr_id, xcb, xcb->type, xcb->method,
+                       xcb->xfr_call_id, xcb->cns_call_id);
+    }
+
+    return (0);
+}
+
+
+void
+fsmxfr_init (void)
+{
+    fsmxfr_xcb_t *xcb;
+
+
+    /*
+     * Initialize the xcbs.
+     */
+    fsmxfr_xcbs = (fsmxfr_xcb_t *)
+        cpr_calloc(FSMXFR_MAX_XCBS, sizeof(fsmxfr_xcb_t));
+
+    FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) {
+        fsmxfr_init_xcb(xcb);
+    }
+
+    /*
+     * Initialize the state/event table.
+     */
+    fsmxfr_sm_table.min_state = FSMXFR_S_MIN;
+    fsmxfr_sm_table.max_state = FSMXFR_S_MAX;
+    fsmxfr_sm_table.min_event = CC_MSG_MIN;
+    fsmxfr_sm_table.max_event = CC_MSG_MAX;
+    fsmxfr_sm_table.table     = (&(fsmxfr_function_table[0][0]));
+}
+
+cc_transfer_mode_e
+cc_is_xfr_call (callid_t call_id)
+{
+    static const char fname[] = "cc_is_xfr_call";
+    int mode;
+
+    if (call_id == CC_NO_CALL_ID) {
+        return CC_XFR_MODE_NONE;
+    }
+    mode = fsmutil_is_xfr_leg(call_id, fsmxfr_xcbs, FSMXFR_MAX_XCBS);
+
+    switch (mode) {
+    case FSMXFR_MODE_TRANSFEROR:
+        FSM_DEBUG_SM(DEB_F_PREFIX"xfer mode is transferor for call id = %d\n", DEB_F_PREFIX_ARGS(FSM, fname), call_id);
+        return CC_XFR_MODE_TRANSFEROR;
+    case FSMXFR_MODE_TRANSFEREE:
+        FSM_DEBUG_SM(DEB_F_PREFIX"xfer mode is transferee for call id = %d\n", DEB_F_PREFIX_ARGS(FSM, fname), call_id);
+        return CC_XFR_MODE_TRANSFEREE;
+    case FSMXFR_MODE_TARGET:
+        FSM_DEBUG_SM(DEB_F_PREFIX"xfer mode is target for call id = %d\n", DEB_F_PREFIX_ARGS(FSM, fname), call_id);
+        return CC_XFR_MODE_TARGET;
+    default:
+        FSM_DEBUG_SM(DEB_F_PREFIX"invalid xfer mode %d for call id = %d\n", DEB_F_PREFIX_ARGS(FSM, fname), mode, call_id);
+        return CC_XFR_MODE_NONE;
+    }
+}
+
+void
+fsmxfr_shutdown (void)
+{
+    cpr_free(fsmxfr_xcbs);
+    fsmxfr_xcbs = NULL;
+}
+
+int
+fsmutil_is_xfr_consult_call (callid_t call_id)
+{
+    return fsmutil_is_xfr_consult_leg(call_id, fsmxfr_xcbs, FSMXFR_MAX_XCBS);
+}
+
+callid_t
+fsmxfr_get_consult_call_id (callid_t call_id)
+{
+    fsmxfr_xcb_t *xcb;
+
+    xcb = fsmxfr_get_xcb_by_call_id(call_id);
+
+    if (xcb && call_id == xcb->xfr_call_id) {
+        return (fsmxfr_get_other_call_id(xcb, call_id));
+    } else {
+        return (CC_NO_CALL_ID);
+    }
+}
+
+callid_t
+fsmxfr_get_primary_call_id (callid_t call_id)
+{
+    fsmxfr_xcb_t *xcb;
+
+    xcb = fsmxfr_get_xcb_by_call_id(call_id);
+
+    if (xcb && (xcb->cns_call_id == call_id)) {
+        return (fsmxfr_get_other_call_id(xcb, call_id));
+    } else {
+        return (CC_NO_CALL_ID);
+    }
+}
diff --git a/libs/sipcc/core/gsm/gsm.c b/libs/sipcc/core/gsm/gsm.c
new file mode 100755 (executable)
index 0000000..561aefb
--- /dev/null
@@ -0,0 +1,608 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_memory.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_ipc.h"
+#include "cpr_errno.h"
+#include "cpr_time.h"
+#include "cpr_rand.h"
+#include "cpr_timers.h"
+#include "cpr_threads.h"
+#include "phone.h"
+#include "phntask.h"
+#include "gsm.h"
+#include "lsm.h"
+#include "vcm.h"
+#include "fsm.h"
+#include "phone_debug.h"
+#include "debug.h"
+#include "fim.h"
+#include "gsm_sdp.h"
+#include "ccsip_subsmanager.h"
+#include "dialplanint.h"
+#include "kpmlmap.h"
+#include "subapi.h"
+#include "platform_api.h"
+
+static void sub_process_feature_msg(uint32_t cmd, void *msg);
+static void sub_process_feature_notify(ccsip_sub_not_data_t *msg, callid_t call_id,
+                            callid_t other_call_id);
+static void sub_process_b2bcnf_msg(uint32_t cmd, void *msg);
+void fsmb2bcnf_get_sub_call_id_from_ccb(fsmcnf_ccb_t *ccb, callid_t *cnf_call_id,
+                callid_t *cns_call_id);
+cprMsgQueue_t gsm_msg_queue;
+void destroy_gsm_thread(void);
+void dp_shutdown();
+extern void dcsm_process_jobs(void);
+extern void dcsm_init(void);
+extern void dcsm_shutdown(void);
+
+/* Flag to see whether we can start processing events */
+
+static boolean gsm_initialized = FALSE;
+extern cprThread_t gsm_thread;
+static media_timer_callback_fp* media_timer_callback = NULL;
+
+/**
+ * Add media falsh one time timer call back. It's for ROUNDTABLE only.
+ */
+void
+gsm_set_media_callback(media_timer_callback_fp* callback) {
+    media_timer_callback = callback;
+}
+
+void
+gsm_set_initialized (void)
+{
+    gsm_initialized = TRUE;
+}
+
+boolean
+gsm_get_initialize_state (void)
+{
+    return gsm_initialized;
+}
+
+cprBuffer_t
+gsm_get_buffer (uint16_t size)
+{
+    return cpr_malloc(size);
+}
+
+
+cpr_status_e
+gsm_send_msg (uint32_t cmd, cprBuffer_t buf, uint16_t len)
+{
+    phn_syshdr_t *syshdr;
+
+    syshdr = (phn_syshdr_t *) cprGetSysHeader(buf);
+    if (!syshdr) {
+        return CPR_FAILURE;
+    }
+    syshdr->Cmd = cmd;
+    syshdr->Len = len;
+
+    if (cprSendMessage(gsm_msg_queue, buf, (void **) &syshdr) == CPR_FAILURE) {
+        cprReleaseSysHeader(syshdr);
+        return CPR_FAILURE;
+    }
+    return CPR_SUCCESS;
+}
+
+
+boolean
+gsm_process_msg (uint32_t cmd, void *msg)
+{
+    static const char fname[] = "gsm_process_msg";
+    boolean release_msg = TRUE;
+    cc_msgs_t       msg_id   = ((cc_setup_t *)msg)->msg_id;
+    int             event_id = msg_id;
+
+    GSM_DEBUG(DEB_F_PREFIX"cmd= 0x%x\n", DEB_F_PREFIX_ARGS(GSM, fname), cmd);
+
+    switch (cmd) {
+    case GSM_GSM:
+    case GSM_SIP:
+        if (gsm_initialized) {
+
+            if (event_id == CC_MSG_FEATURE &&
+                (((cc_feature_t *) msg)->feature_id == CC_FEATURE_CAC_RESP_PASS)) {
+
+                fsm_cac_process_bw_avail_resp ();
+
+                /* Release all memory for CC_FEATURE_CAC_..message */
+                release_msg = TRUE;
+
+                GSM_DEBUG(DEB_F_PREFIX"CAC Message Processed: 0x%x\n", DEB_F_PREFIX_ARGS(GSM, fname), cmd);
+            } else  if (event_id == CC_MSG_FEATURE &&
+                (((cc_feature_t *) msg)->feature_id == CC_FEATURE_CAC_RESP_FAIL)) {
+
+                fsm_cac_process_bw_failed_resp ();
+
+                /* Release all memory for CC_FEATURE_CAC_..message */
+                release_msg = TRUE;
+
+                GSM_DEBUG(DEB_F_PREFIX"CAC Message Processed: 0x%x\n", DEB_F_PREFIX_ARGS(GSM, fname), cmd);
+            } else {
+
+                release_msg = fim_process_event(msg, FALSE);
+                GSM_DEBUG(DEB_F_PREFIX"Message Processed: 0x%x\n", DEB_F_PREFIX_ARGS(GSM, fname), cmd);
+            }
+        }
+        if (release_msg == TRUE) {
+            fim_free_event(msg);
+        }
+        break;
+
+    default:
+        GSM_DEBUG(DEB_F_PREFIX"Unknown Cmd received: 0x%x\n", DEB_F_PREFIX_ARGS(GSM, fname), cmd);
+        break;
+    }
+
+    return(release_msg);
+}
+
+
+void
+gsm_process_timer_expiration (void *msg)
+{
+    static const char fname[] = "gsm_process_timer_expiration";
+    cprCallBackTimerMsg_t *timerMsg;
+    void *timeout_msg = NULL;
+
+    timerMsg = (cprCallBackTimerMsg_t *) msg;
+    TMR_DEBUG(DEB_F_PREFIX"Timer %s expired\n", DEB_F_PREFIX_ARGS(GSM, fname), timerMsg->expiredTimerName);
+
+    switch (timerMsg->expiredTimerId) {
+
+    case GSM_MULTIPART_TONES_TIMER:
+    case GSM_CONTINUOUS_TONES_TIMER:
+        lsm_tmr_tones_callback(timerMsg->usrData);
+        break;
+
+    case GSM_ERROR_ONHOOK_TIMER:
+        fsmdef_error_onhook_timeout(timerMsg->usrData);
+        break;
+
+    case GSM_AUTOANSWER_TIMER:
+        fsmdef_auto_answer_timeout(timerMsg->usrData);
+        break;
+
+    case GSM_REVERSION_TIMER:
+        fsmdef_reversion_timeout((callid_t)(long)timerMsg->usrData);
+        break;
+
+    case GSM_CAC_FAILURE_TIMER:
+        fsm_cac_process_bw_fail_timer(timerMsg->usrData);
+        break;
+
+    case GSM_DIAL_TIMEOUT_TIMER:
+        dp_dial_timeout(timerMsg->usrData);
+        break;
+
+    case GSM_KPML_INTER_DIGIT_TIMER:
+        kpml_inter_digit_timer_callback(timerMsg->usrData);
+        break;
+    case GSM_KPML_CRITICAL_DIGIT_TIMER:
+    case GSM_KPML_EXTRA_DIGIT_TIMER:
+        break;
+
+    case GSM_KPML_SUBSCRIPTION_TIMER:
+        kpml_subscription_timer_callback(timerMsg->usrData);
+        break;
+
+    case GSM_REQ_PENDING_TIMER:
+        timeout_msg = fsmdef_feature_timer_timeout(
+                          CC_FEATURE_REQ_PEND_TIMER_EXP,
+                          timerMsg->usrData);
+        break;
+
+    case GSM_RINGBACK_DELAY_TIMER:
+        timeout_msg = fsmdef_feature_timer_timeout(
+                          CC_FEATURE_RINGBACK_DELAY_TIMER_EXP,
+                          timerMsg->usrData);
+        break;
+    case GSM_FLASH_ONCE_TIMER:
+        if (media_timer_callback != NULL) {
+            (* ((media_timer_callback_fp)(media_timer_callback)))();
+        }
+        break;
+       case GSM_TONE_DURATION_TIMER:
+               lsm_tone_duration_tmr_callback(timerMsg->usrData);
+               break;
+    default:
+        GSM_ERR_MSG(GSM_F_PREFIX"unknown timer %d\n", fname,
+                    timerMsg->expiredTimerName);
+        break;
+    }
+
+    /*
+     * If there is a timer message to be processed by state machine,
+     * hands it to GSM state machine here.
+     */
+    if (timeout_msg != NULL) {
+        /* Let state machine handle glare timer expiration */
+        gsm_process_msg(GSM_GSM, timeout_msg);
+        cpr_free(timeout_msg);
+    }
+}
+
+static void
+gsm_init (void)
+{
+    /* Placeholder for any initialization tasks */
+}
+
+void
+gsm_shutdown (void)
+{
+    gsm_initialized = FALSE;
+
+    lsm_shutdown();
+    fsm_shutdown();
+    fim_shutdown();
+    dcsm_shutdown();
+}
+
+void
+gsm_reset (void)
+{
+    dp_reset();
+    lsm_reset();
+    fsmutil_free_all_shown_calls_ci_map();
+}
+
+void
+GSMTask (void *arg)
+{
+    static const char fname[] = "GSMTask";
+    void           *msg;
+    phn_syshdr_t   *syshdr;
+    boolean        release_msg = TRUE;
+
+    /*
+     * Get the GSM message queue handle
+     * A hack until the tasks in irx are
+     * CPRized.
+     */
+    gsm_msg_queue = (cprMsgQueue_t) arg;
+    if (!gsm_msg_queue) {
+        GSM_ERR_MSG(GSM_F_PREFIX"invalid input, exiting\n", fname);
+        return;
+    }
+
+    if (platThreadInit("GSMTask") != 0) {
+        return;
+    }
+    /*
+     * Adjust relative priority of GSM thread.
+     */
+    (void) cprAdjustRelativeThreadPriority(GSM_THREAD_RELATIVE_PRIORITY);
+
+    /*
+     * Initialize all the GSM modules
+     */
+    lsm_init();
+    fsm_init();
+    fim_init();
+    gsm_init();
+    dcsm_init();
+
+    cc_init();
+
+    fsmutil_init_shown_calls_ci_map();
+    /*
+     * On Win32 platform, the random seed is stored per thread; therefore,
+     * each thread needs to seed the random number.  It is recommended by
+     * MS to do the following to ensure randomness across application
+     * restarts.
+     */
+    cpr_srand((unsigned int)time(NULL));
+
+    /*
+     * Cache random numbers for SRTP keys
+     */
+    gsmsdp_cache_crypto_keys();
+
+    while (1) {
+
+        release_msg = TRUE;
+
+        msg = cprGetMessage(gsm_msg_queue, TRUE, (void **) &syshdr);
+        if (msg) {
+            switch (syshdr->Cmd) {
+            case TIMER_EXPIRATION:
+                gsm_process_timer_expiration(msg);
+                break;
+
+            case GSM_SIP:
+            case GSM_GSM:
+                release_msg = gsm_process_msg(syshdr->Cmd, msg);
+                break;
+
+            case DP_MSG_INIT_DIALING:
+            case DP_MSG_DIGIT_STR:
+            case DP_MSG_STORE_DIGIT:
+            case DP_MSG_DIGIT:
+            case DP_MSG_DIAL_IMMEDIATE:
+            case DP_MSG_REDIAL:
+            case DP_MSG_ONHOOK:
+            case DP_MSG_OFFHOOK:
+            case DP_MSG_UPDATE:
+            case DP_MSG_DIGIT_TIMER:
+            case DP_MSG_CANCEL_OFFHOOK_TIMER:
+                dp_process_msg(syshdr->Cmd, msg);
+                break;
+
+            case SUB_MSG_B2BCNF_SUBSCRIBE_RESP:
+            case SUB_MSG_B2BCNF_NOTIFY:
+            case SUB_MSG_B2BCNF_TERMINATE:
+                sub_process_b2bcnf_msg(syshdr->Cmd, msg);
+                break;
+
+            case SUB_MSG_FEATURE_SUBSCRIBE_RESP:
+            case SUB_MSG_FEATURE_NOTIFY:
+            case SUB_MSG_FEATURE_TERMINATE:
+                sub_process_feature_msg(syshdr->Cmd, msg);
+                break;
+
+            case SUB_MSG_KPML_SUBSCRIBE:
+            case SUB_MSG_KPML_TERMINATE:
+            case SUB_MSG_KPML_NOTIFY_ACK:
+            case SUB_MSG_KPML_SUBSCRIBE_TIMER:
+            case SUB_MSG_KPML_DIGIT_TIMER:
+                kpml_process_msg(syshdr->Cmd, msg);
+                break;
+
+            case REG_MGR_STATE_CHANGE:
+                gsm_reset();
+                break;
+            case THREAD_UNLOAD:
+                destroy_gsm_thread();
+                break;
+
+            default:
+                GSM_ERR_MSG(GSM_F_PREFIX"Unknown message\n", fname);
+                break;
+            }
+
+            cprReleaseSysHeader(syshdr);
+            if (release_msg == TRUE) {
+                cpr_free(msg);
+            }
+
+            /* Check if there are pending messages for dcsm
+             * if it in the right state perform its operation
+             */
+            dcsm_process_jobs();
+        }
+    }
+}
+
+/**
+ * This function will process SUBSCRIBED feature NOTIFY messages.
+ *
+ * @param[in] msg - pointer to ccsip_sub_not_data_t
+ *
+ * @return none
+ *
+ * @pre (msg != NULL)
+ */
+static void sub_process_b2bcnf_sub_resp (ccsip_sub_not_data_t *msg)
+{
+    static const char fname[] = "sub_process_b2bcnf_sub_resp";
+
+    callid_t                 call_id = CC_NO_CALL_ID;
+    callid_t                other_call_id = CC_NO_CALL_ID;
+    cc_causes_t            cause;
+
+    fsmb2bcnf_get_sub_call_id_from_ccb ((fsmcnf_ccb_t *)(msg->request_id),
+                                        &call_id, &other_call_id);
+
+    if (msg->u.subs_result_data.status_code == 200 ||
+        msg->u.subs_result_data.status_code == 202 )  {
+
+        cause = CC_CAUSE_OK;
+
+        GSM_DEBUG(DEB_F_PREFIX"B2BCNF subs response  = OK\n",
+                DEB_F_PREFIX_ARGS(GSM,fname));
+
+    } else {
+
+        GSM_DEBUG(DEB_F_PREFIX"B2BCNF subs response  = ERROR\n",
+                DEB_F_PREFIX_ARGS(GSM,fname));
+
+        cause = CC_CAUSE_ERROR;
+    }
+
+    cc_feature_ack(CC_SRC_GSM, call_id, msg->line_id, CC_FEATURE_B2BCONF, NULL, cause);
+}
+
+/**
+ * This function will process b2bcnf feature NOTIFY messages.
+ *
+ * @param[in] cmd - command
+ * @param[in] msg - pointer to ccsip_sub_not_data_t
+ *
+ * @return none
+ *
+ * @pre (msg != NULL)
+ */
+static void sub_process_b2bcnf_msg (uint32_t cmd, void *msg)
+{
+    static const char fname[] = "sub_process_b2bcnf_msg";
+    cc_feature_data_t data;
+    callid_t          call_id, other_call_id = CC_NO_CALL_ID;
+
+    fsmb2bcnf_get_sub_call_id_from_ccb((fsmcnf_ccb_t *)((ccsip_sub_not_data_t *)msg)->request_id,
+                                &call_id, &other_call_id);
+    switch (cmd) {
+    case SUB_MSG_B2BCNF_SUBSCRIBE_RESP:
+        GSM_DEBUG(DEB_F_PREFIX"B2BCNF subs response\n",
+                DEB_F_PREFIX_ARGS(GSM,fname));
+        sub_process_b2bcnf_sub_resp((ccsip_sub_not_data_t *)msg);
+        break;
+
+    case SUB_MSG_B2BCNF_NOTIFY:
+        GSM_DEBUG(DEB_F_PREFIX"B2BCNF subs notify\n",
+                DEB_F_PREFIX_ARGS(GSM,fname));
+        sub_process_feature_notify((ccsip_sub_not_data_t *)msg, call_id, other_call_id);
+        break;
+    case SUB_MSG_B2BCNF_TERMINATE:
+         /*
+          * This is posted by SIP stack if it is shutting down or rolling over.
+          * if so, notify b2bcnf to cleanup state machine.
+          */
+        GSM_DEBUG(DEB_F_PREFIX"B2BCNF subs terminate\n",
+                DEB_F_PREFIX_ARGS(GSM,fname));
+
+        data.notify.subscription = CC_SUBSCRIPTIONS_REMOTECC;
+        data.notify.method = CC_RCC_METHOD_REFER;
+        data.notify.data.rcc.feature = CC_FEATURE_B2BCONF;
+
+        data.notify.cause = CC_CAUSE_ERROR;
+
+        cc_feature(CC_SRC_GSM, call_id, 0, CC_FEATURE_NOTIFY, &data);
+
+        break;
+    default:
+        GSM_DEBUG(DEB_F_PREFIX"B2BCNF subs unknown event\n",
+                DEB_F_PREFIX_ARGS(GSM,fname));
+         break;
+    }
+}
+
+/**
+ * This function will process SUBSCRIBED feature NOTIFY messages.
+ *
+ * @param[in] cmd - command
+ * @param[in] msg - pointer to ccsip_sub_not_data_t
+ *
+ * @return none
+ *
+ * @pre (msg != NULL)
+ */
+static void sub_process_feature_msg (uint32_t cmd, void *msg)
+{
+    callid_t   call_id;
+    cc_feature_ack_t temp_msg;
+
+     switch (cmd) {
+     case SUB_MSG_FEATURE_SUBSCRIBE_RESP:
+         /*
+          * if the response in non-2xx final, we should reset the active feature.
+          */
+         call_id = (callid_t)(((ccsip_sub_not_data_t *)msg)->request_id);
+         if (((ccsip_sub_not_data_t *)msg)->u.subs_result_data.status_code > 299) {
+             memset(&temp_msg, 0, sizeof(temp_msg));
+             temp_msg.msg_id     = CC_MSG_FEATURE_ACK;
+             temp_msg.src_id     = CC_SRC_GSM;
+             temp_msg.call_id    = call_id;
+             fim_process_event((void *)&temp_msg, FALSE);
+         }
+         break;
+     case SUB_MSG_FEATURE_NOTIFY:
+         call_id = (callid_t)(((ccsip_sub_not_data_t *)msg)->request_id);
+         sub_process_feature_notify((ccsip_sub_not_data_t *)msg, call_id,
+                        CC_NO_CALL_ID);
+         break;
+     case SUB_MSG_FEATURE_TERMINATE:
+         /*
+          * This is posted by SIP stack if it is shutting down or rolling over.
+          * if so, sip stack already cleaned up the subscription. so do nothing.
+          */
+         break;
+     }
+}
+
+/**
+ * This function will process SUBSCRIBED feature NOTIFY messages.
+ *
+ * @param[in] msg - pointer to ccsip_sub_not_data_t
+ *
+ * @return none
+ *
+ * @pre (msg != NULL)
+ */
+static void sub_process_feature_notify (ccsip_sub_not_data_t *msg, callid_t call_id,
+                                        callid_t other_call_id)
+{
+    static const char fname[] = "sub_process_feature_notify";
+    ccsip_event_data_t      *ev_data;
+    cc_feature_ack_t temp_msg;
+
+
+    /*
+     * send response to NOTIFY.
+     */
+    (void)sub_int_notify_ack(msg->sub_id, SIP_STATUS_SUCCESS, msg->u.notify_ind_data.cseq);
+
+    /*
+     * if the subscription state is terminated, clean up the subscription.
+     */
+    if (msg->u.notify_ind_data.subscription_state == SUBSCRIPTION_STATE_TERMINATED) {
+        /*
+         * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED.
+         * do not force SUB/NOT mgr to cleanup SCB immediately, because we may have to handle digest
+         * challenges to terminating SUBSCRIBE sent.
+         */
+       (void)sub_int_subscribe_term(msg->sub_id, FALSE, msg->request_id, CC_SUBSCRIPTIONS_REMOTECC);
+
+    }
+    ev_data     = msg->u.notify_ind_data.eventData;
+    msg->u.notify_ind_data.eventData = NULL;
+    if (ev_data == NULL) {
+        GSM_ERR_MSG(DEB_F_PREFIX"No body in the NOTIFY message\n",
+                    DEB_F_PREFIX_ARGS(GSM, fname));
+        /*
+         * if (no content & subscription state is TERMINATED
+         * then reset active_feature to NONE.
+         */
+        if (msg->u.notify_ind_data.subscription_state == SUBSCRIPTION_STATE_TERMINATED) {
+            memset(&temp_msg, 0, sizeof(temp_msg));
+            temp_msg.msg_id     = CC_MSG_FEATURE_ACK;
+            temp_msg.src_id     = CC_SRC_GSM;
+            temp_msg.call_id    = call_id;
+            fim_process_event((void *)&temp_msg, FALSE);
+        }
+        return;
+    }
+
+    // other types of event data is not supported as of now.
+    free_event_data(ev_data);
+}
+
+/*
+ * return TRUE if GSM is considered idle,
+ *   currently this means lsm is idle
+ * FALSE otherwise.
+ */
+boolean
+gsm_is_idle (void)
+{
+    if (lsm_is_phone_idle()) {
+        return (TRUE);
+    }
+    return (FALSE);
+}
+
+/*
+ *  Function: destroy_gsm_thread
+ *  Description:  shutdown gsm and kill gsm thread
+ *  Parameters:   none
+ *  Returns: none
+ */
+void destroy_gsm_thread()
+{
+    static const char fname[] = "destroy_gsm_thread";
+    DEF_DEBUG(DEB_F_PREFIX"Unloading GSM and destroying GSM thread\n",
+        DEB_F_PREFIX_ARGS(SIP_CC_INIT, fname));
+    gsm_shutdown();
+    dp_shutdown();
+    kpml_shutdown();
+    (void) cprDestroyThread(gsm_thread);
+}
diff --git a/libs/sipcc/core/gsm/gsm_sdp.c b/libs/sipcc/core/gsm/gsm_sdp.c
new file mode 100644 (file)
index 0000000..53d442c
--- /dev/null
@@ -0,0 +1,6610 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_in.h"
+#include "cpr_rand.h"
+#include "cpr_stdlib.h"
+#include "lsm.h"
+#include "fsm.h"
+#include "ccapi.h"
+#include "ccsip_sdp.h"
+#include "sdp.h"
+#include "gsm.h"
+#include "gsm_sdp.h"
+#include "util_string.h"
+#include "rtp_defs.h"
+#include "debug.h"
+#include "dtmf.h"
+#include "prot_configmgr.h"
+#include "dns_utils.h"
+#include "sip_interface_regmgr.h"
+#include "platform_api.h"
+#include "vcm.h"
+#include "prlog.h"
+#include "plstr.h"
+#include "sdp_private.h"
+
+//TODO Need to place this in a portable location
+#define MULTICAST_START_ADDRESS 0xe1000000
+#define MULTICAST_END_ADDRESS   0xefffffff
+
+/* Only first octet contains codec type */
+#define GET_CODEC_TYPE(a)    ((uint8_t)((a) & 0XFF))
+
+#define GSMSDP_SET_MEDIA_DIABLE(media) \
+     (media->src_port = 0)
+
+#define CAST_DEFAULT_BITRATE 320000
+/*
+ * The maximum number of media lines per call. This puts the upper limit
+ * on * the maximum number of media lines per call to resource hogging.
+ * The value of 8 is intended up to 2 audio and 2 video streams with
+ * each stream can offer IPV4 and IPV6 alternate network address type
+ * in ANAT group (RFC-4091).
+ */
+#define GSMSDP_MAX_MLINES_PER_CALL  (8)
+
+/*
+ * Permanent number of free media structure elements for media structure
+ * that represents media line in the SDP. The maximum number of elements
+ * is set to equal number of call or LSM_MAX_CALLS. This should be enough
+ * to minimumly allow typical a single audio media stream per call scenario
+ * without using dynamic memory.
+ *
+ * If more media structures are needed than this number, the addition
+ * media structures are allocated from heap and they will be freed back
+ * from heap after thehy are not used. The only time where the heap
+ * is used when phone reaches the maximum call capacity and each one
+ * of the call is using more than one media lines.
+ */
+#define GSMSDP_PERM_MEDIA_ELEMS   (LSM_MAX_CALLS)
+
+/*
+ * The permanent free media structure elements use static array.
+ * It is to ensure a low overhead for this a typical single audio call.
+ */
+static fsmdef_media_t gsmsdp_free_media_chunk[GSMSDP_PERM_MEDIA_ELEMS];
+static sll_lite_list_t gsmsdp_free_media_list;
+
+typedef enum {
+    MEDIA_TABLE_GLOBAL,
+    MEDIA_TABLE_SESSION
+} media_table_e;
+
+/* Forward references */
+static cc_causes_t
+gsmsdp_init_local_sdp (const char *peerconnection, cc_sdp_t **sdp_pp);
+
+static void
+gsmsdp_set_media_capability(fsmdef_media_t *media,
+                            const cc_media_cap_t *media_cap);
+static fsmdef_media_t *
+gsmsdp_add_media_line(fsmdef_dcb_t *dcb_p, const cc_media_cap_t *media_cap,
+                      uint8_t cap_index, uint16_t level,
+                      cpr_ip_type addr_type, boolean offer);
+
+
+extern cc_media_cap_table_t g_media_table;
+
+extern boolean g_disable_mass_reg_debug_print;
+/**
+ * A wraper function to return the media capability supported by
+ * the platform and session. This is a convient place if policy
+ * to get the capability table as it applies to the session
+ * updates the media_cap_tbl ptr in dcb
+ *
+ * @param[in]dcb     - pointer to the fsmdef_dcb_t
+
+ *
+ * @return           - pointer to the the media capability table for session
+ */
+static const cc_media_cap_table_t *gsmsdp_get_media_capability (fsmdef_dcb_t *dcb_p)
+{
+    static const char *fname = "gsmsdp_get_media_capability";
+    int                sdpmode = 0;
+
+    if (g_disable_mass_reg_debug_print == FALSE) {
+        GSM_DEBUG(DEB_F_PREFIX"dcb video pref %x\n",
+                               DEB_F_PREFIX_ARGS(GSM, fname), dcb_p->video_pref);
+    }
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    if ( dcb_p->media_cap_tbl == NULL ) {
+         dcb_p->media_cap_tbl = (cc_media_cap_table_t*) cpr_malloc(sizeof(cc_media_cap_table_t));
+         if ( dcb_p->media_cap_tbl == NULL ) {
+             GSM_ERR_MSG(GSM_L_C_F_PREFIX"media table malloc failed.\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+             return NULL;
+         }
+    }
+
+    *(dcb_p->media_cap_tbl) = g_media_table;
+
+    /*
+     * Turn off two default streams, this is temporary
+     * until we can handle multiple streams properly
+     */
+    if (sdpmode) {
+        dcb_p->media_cap_tbl->cap[CC_AUDIO_1].enabled = TRUE;
+        dcb_p->media_cap_tbl->cap[CC_VIDEO_1].enabled = TRUE;
+        dcb_p->media_cap_tbl->cap[CC_AUDIO_1].support_direction = SDP_DIRECTION_RECVONLY;
+        dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_RECVONLY;
+        dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = TRUE;
+    } else {
+        dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = FALSE;
+
+        if ( dcb_p->video_pref == SDP_DIRECTION_INACTIVE) {
+            // do not enable video
+            dcb_p->media_cap_tbl->cap[CC_VIDEO_1].enabled = FALSE;
+        }
+
+        if ( dcb_p->video_pref == SDP_DIRECTION_RECVONLY ) {
+            if ( dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_direction == SDP_DIRECTION_SENDRECV ) {
+                dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_direction = dcb_p->video_pref;
+            }
+
+            if ( dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_direction == SDP_DIRECTION_SENDONLY ) {
+                dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_INACTIVE;
+                DEF_DEBUG(GSM_L_C_F_PREFIX"video capability disabled to SDP_DIRECTION_INACTIVE from sendonly\n",
+                dcb_p->line, dcb_p->call_id, fname);
+            }
+        } else if ( dcb_p->video_pref == SDP_DIRECTION_SENDONLY ) {
+            if ( dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_direction == SDP_DIRECTION_SENDRECV ) {
+                dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_direction = dcb_p->video_pref;
+            }
+
+            if ( dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_direction == SDP_DIRECTION_RECVONLY ) {
+               dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_INACTIVE;
+                DEF_DEBUG(GSM_L_C_F_PREFIX"video capability disabled to SDP_DIRECTION_INACTIVE from recvonly\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+            }
+        } // else if requested is SENDRECV just go by capability
+    }
+
+    return (dcb_p->media_cap_tbl);
+}
+
+/*
+ * Process a single constraint for one media capablity
+ */
+void gsmsdp_process_cap_constraint(cc_media_cap_t *cap,
+                                   const char *constraint) {
+  /* Check constraint string for values "TRUE" or "FALSE"
+     (currently set in PeerConnectionImpl.cpp, with only
+     two possible hardcoded values).
+     TODO -- The values that constraints can take are
+     fairly narrow and enumerated; they should probably
+     use an enumeration rather than a string. See bug 811360.
+  */
+  if (constraint[0] == 'F') {
+    cap->support_direction &= ~SDP_DIRECTION_FLAG_RECV;
+  } else if (constraint[0] == 'T') {
+    cap->support_direction |= SDP_DIRECTION_FLAG_RECV;
+  }
+}
+
+/*
+ * Process constraints only related to media capabilities., i.e
+ * OfferToReceiveAudio, OfferToReceiveVideo
+ */
+void gsmsdp_process_cap_constraints(fsmdef_dcb_t *dcb,
+                                    const cc_media_constraints_t* constraints) {
+  int i = 0;
+
+  for (i=0; i<constraints->constraint_count; i++) {
+    if (strcmp(constraints_table[OfferToReceiveAudio].name,
+               constraints->constraints[i]->name) == 0) {
+      gsmsdp_process_cap_constraint(&dcb->media_cap_tbl->cap[CC_AUDIO_1],
+                                    constraints->constraints[i]->value);
+    } else if (strcmp(constraints_table[OfferToReceiveVideo].name,
+               constraints->constraints[i]->name) == 0) {
+      gsmsdp_process_cap_constraint(&dcb->media_cap_tbl->cap[CC_VIDEO_1],
+                                    constraints->constraints[i]->value);
+    }
+  }
+}
+
+/**
+ * Copy an fsmdef_media_t's payload list to its previous_sdp's payload list
+ *
+ * @param[in]media   - pointer to the fsmdef_media_t to update
+ */
+void gsmsdp_copy_payloads_to_previous_sdp (fsmdef_media_t *media)
+{
+    static const char *fname = "gsmsdp_copy_payloads_to_previous_sdp";
+
+    if ((!media->payloads) && (NULL != media->previous_sdp.payloads))
+    {
+      cpr_free(media->previous_sdp.payloads);
+      media->previous_sdp.payloads = NULL;
+      media->previous_sdp.num_payloads = 0;
+    }
+
+    /* Ensure that there is enough space to hold all the payloads */
+    if (media->num_payloads > media->previous_sdp.num_payloads)
+    {
+      media->previous_sdp.payloads =
+        cpr_realloc(media->previous_sdp.payloads,
+            media->num_payloads * sizeof(vcm_payload_info_t));
+    }
+
+    /* Copy the payloads over */
+    media->previous_sdp.num_payloads = media->num_payloads;
+    memcpy(media->previous_sdp.payloads, media->payloads,
+        media->num_payloads * sizeof(vcm_payload_info_t));
+    media->previous_sdp.num_payloads = media->num_payloads;
+}
+
+/**
+ * Find an entry for the specified codec type in the vcm_payload_info_t
+ * list array.
+ *
+ * @param[in]codec            - Codec type to find
+ * @param[in]payload_info     - Array of payload info entries
+ * @param[in]num_payload_info - Total number of elements in payload_info
+ * @param[in]instance         - Which instance of the codec to find
+ *                              (e.g., if more than one entry for a codec type)
+ *
+ * @return           Pointer to the payload info if found;
+ *                   NULL when no match is found.
+ */
+vcm_payload_info_t *gsmsdp_find_info_for_codec(rtp_ptype codec,
+                                         vcm_payload_info_t *payload_info,
+                                         int num_payload_info,
+                                         int instance) {
+    int i;
+    for (i = 0; i < num_payload_info; i++) {
+        if (payload_info[i].codec_type == codec)
+        {
+            instance--;
+            if (instance == 0) {
+                return &(payload_info[i]);
+            }
+        }
+    }
+    return NULL;
+}
+
+
+/**
+ * Sets up the media track table
+ *
+ * @param[in]dcb     - pointer to the fsmdef_dcb_t
+ *
+ * @return           - pointer to the the media track table for session
+ */
+static const cc_media_remote_stream_table_t *gsmsdp_get_media_stream_table (fsmdef_dcb_t *dcb_p)
+{
+    static const char *fname = "gsmsdp_get_media_stream_table";
+    if ( dcb_p->remote_media_stream_tbl == NULL ) {
+        dcb_p->remote_media_stream_tbl = (cc_media_remote_stream_table_t*) cpr_malloc(sizeof(cc_media_remote_stream_table_t));
+        memset(dcb_p->remote_media_stream_tbl, 0, sizeof(cc_media_remote_stream_table_t));
+
+        if ( dcb_p->remote_media_stream_tbl == NULL ) {
+
+             GSM_ERR_MSG(GSM_L_C_F_PREFIX"media track table malloc failed.\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+             return NULL;
+         }
+    }
+
+    return (dcb_p->remote_media_stream_tbl);
+}
+
+/**
+ * The function creates a free media structure elements list. The
+ * free list is global for all calls. The function must be called once
+ * during GSM initializtion.
+ *
+ * @param            None.
+ *
+ * @return           TRUE  - free media structure list is created
+ *                           successfully.
+ *                   FALSE - failed to create free media structure
+ *                           list.
+ */
+boolean
+gsmsdp_create_free_media_list (void)
+{
+    uint32_t i;
+    fsmdef_media_t *media;
+
+    /* initialize free media_list structure */
+    (void)sll_lite_init(&gsmsdp_free_media_list);
+
+    /*
+     * Populate the free list:
+     * Break the entire chunk into multiple free elements and link them
+     * onto to the free media list.
+     */
+    media = &gsmsdp_free_media_chunk[0];    /* first element */
+    for (i = 0; i < GSMSDP_PERM_MEDIA_ELEMS; i++) {
+        (void)sll_lite_link_head(&gsmsdp_free_media_list,
+                                 (sll_lite_node_t *)media);
+        media = media + 1; /* next element */
+    }
+
+    /* Successful create media free list */
+    return (TRUE);
+}
+
+/**
+ * The function destroys the free media structure list. It should be
+ * call during GSM shutdown.
+ *
+ * @param            None.
+ *
+ * @return           None.
+ */
+void
+gsmsdp_destroy_free_media_list (void)
+{
+    /*
+     * Although the free chunk is not allocated but,
+     * NULL out the free list header to indicate that the
+     * there is not thing from the free chunk.
+     */
+    (void)sll_lite_init(&gsmsdp_free_media_list);
+}
+
+/**
+ * The function allocates a media structure. The function
+ * attempts to obtain a free media structure from the free media
+ * structure list first. If free list is empty then the media structure
+ * is allocated from a memory pool.
+ *
+ * @param            None.
+ *
+ * @return           pointer to the fsmdef_media_t if successful or
+ *                   NULL when there is no free media structure
+ *                   is available.
+ */
+static fsmdef_media_t *
+gsmsdp_alloc_media (void)
+{
+    static const char fname[] = "gsmsdp_alloc_media";
+    fsmdef_media_t *media = NULL;
+
+    /* Get a media element from the free list */
+    media = (fsmdef_media_t *)sll_lite_unlink_head(&gsmsdp_free_media_list);
+    if (media == NULL) {
+        /* no free element from cache, allocate it from the pool */
+        media = cpr_malloc(sizeof(fsmdef_media_t));
+        GSM_DEBUG(DEB_F_PREFIX"get from dynamic pool, media %x\n",
+                               DEB_F_PREFIX_ARGS(GSM, fname), media);
+    }
+    return (media);
+}
+
+/**
+ * The function frees a media structure back to the free list or
+ * heap. If the media structure is from the free list then it
+ * is put back to the free list otherwise it will be freed
+ * back to the dynamic pool.
+ *
+ * @param[in]media   - pointer to fsmdef_media_t to free back to
+ *                   free list.
+ *
+ * @return           pointer to the fsmdef_media_t if successful
+ *                   NULL when there is no free media structure
+ *                   is available.
+ */
+static void
+gsmsdp_free_media (fsmdef_media_t *media)
+{
+    static const char fname[] = "gsmsdp_free_media";
+
+    if (media == NULL) {
+        return;
+    }
+
+    if (media-> video != NULL ) {
+      vcmFreeMediaPtr(media->video);
+    }
+
+    if(media->payloads != NULL) {
+        cpr_free(media->payloads);
+        media->payloads = NULL;
+        media->num_payloads = 0;
+    }
+    /*
+     * Check to see if the element is part of the
+     * free chunk space.
+     */
+    if ((media >= &gsmsdp_free_media_chunk[0]) &&
+        (media <= &gsmsdp_free_media_chunk[GSMSDP_PERM_MEDIA_ELEMS-1])) {
+        /* the element is part of free chunk, put it back to the list */
+        (void)sll_lite_link_head(&gsmsdp_free_media_list,
+                                 (sll_lite_node_t *)media);
+    } else {
+        /* this element is from the dynamic pool, free it back */
+        cpr_free(media);
+        GSM_DEBUG(DEB_F_PREFIX"free media 0x%x to dynamic pool\n",
+                  DEB_F_PREFIX_ARGS(GSM, fname), media);
+    }
+}
+
+/**
+ * Initialize the media entry. The function initializes media
+ * entry.
+ *
+ * @param[in]media - pointer to fsmdef_media_t of the media entry to be
+ *                   initialized.
+ *
+ * @return  none
+ *
+ * @pre     (media not_eq NULL)
+ */
+static void
+gsmsdp_init_media (fsmdef_media_t *media)
+{
+    media->refid = CC_NO_MEDIA_REF_ID;
+    media->type = SDP_MEDIA_INVALID; /* invalid (free entry) */
+    media->packetization_period = ATTR_PTIME;
+    media->max_packetization_period = ATTR_MAXPTIME;
+    media->mode = (uint16_t)vcmGetILBCMode();
+    media->vad = VCM_VAD_OFF;
+    /* Default to audio codec */
+    media->level = 0;
+    media->dest_port = 0;
+    media->dest_addr = ip_addr_invalid;
+    media->is_multicast = FALSE;
+    media->multicast_port = 0;
+    media->avt_payload_type = RTP_NONE;
+    media->src_port = 0;
+    media->src_addr = ip_addr_invalid;
+    media->rcv_chan = FALSE;
+    media->xmit_chan = FALSE;
+
+    media->direction = SDP_DIRECTION_INACTIVE;
+    media->direction_set = FALSE;
+    media->transport = SDP_TRANSPORT_INVALID;
+    media->tias_bw = SDP_INVALID_VALUE;
+    media->profile_level = 0;
+
+    media->previous_sdp.avt_payload_type = RTP_NONE;
+    media->previous_sdp.dest_addr = ip_addr_invalid;
+    media->previous_sdp.dest_port = 0;
+    media->previous_sdp.direction = SDP_DIRECTION_INACTIVE;
+    media->previous_sdp.packetization_period = media->packetization_period;
+    media->previous_sdp.max_packetization_period = media->max_packetization_period;
+    media->previous_sdp.payloads = NULL;
+    media->previous_sdp.num_payloads = 0;
+    media->previous_sdp.tias_bw = SDP_INVALID_VALUE;
+    media->previous_sdp.profile_level = 0;
+
+    media->hold  = FSM_HOLD_NONE;
+    media->flags = 0;                    /* clear all flags      */
+    media->cap_index = CC_MAX_MEDIA_CAP; /* max is invalid value */
+    media->video = NULL;
+    media->candidate_ct = 0;
+    media->rtcp_mux = FALSE;
+    media->protocol = NULL;
+    media->payloads = NULL;
+    media->num_payloads = 0;
+}
+
+/**
+ *
+ * Returns a pointer to a new the fsmdef_media_t for a given dcb.
+ * The default media parameters will be intialized for the known or
+ * supported media types. The new media is also added to the media list
+ * in the dcb.
+ *
+ * @param[in]dcb_p      - pointer to the fsmdef_dcb_t
+ * @param[in]media_type - sdp_media_e.
+ * @param[in]level      - uint16_t for media line level.
+ *
+ * @return           pointer to the fsmdef_media_t of the corresponding
+ *                   media entry in the dcb.
+ * @pre              (dcb not_eq NULL)
+ */
+static fsmdef_media_t *
+gsmsdp_get_new_media (fsmdef_dcb_t *dcb_p, sdp_media_e media_type,
+                      uint16_t level)
+{
+    static const char fname[] = "gsmsdp_get_new_media";
+    fsmdef_media_t *media;
+    static media_refid_t media_refid = CC_NO_MEDIA_REF_ID;
+    sll_lite_return_e sll_lite_ret;
+
+    /* check to ensue we do not handle too many media lines */
+    if (GSMSDP_MEDIA_COUNT(dcb_p) >= GSMSDP_MAX_MLINES_PER_CALL) {
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"exceeding media lines per call\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+        return (NULL);
+    }
+
+    /* allocate new media entry */
+    media = gsmsdp_alloc_media();
+    if (media != NULL) {
+        /* initialize the media entry */
+        gsmsdp_init_media(media);
+
+        /* assigned media reference id */
+        if (++media_refid == CC_NO_MEDIA_REF_ID) {
+            media_refid = 1;
+        }
+        media->refid = media_refid;
+        media->type  = media_type;
+        media->level = level;
+
+        /* append the media to the active list */
+        sll_lite_ret = sll_lite_link_tail(&dcb_p->media_list,
+                           (sll_lite_node_t *)media);
+        if (sll_lite_ret != SLL_LITE_RET_SUCCESS) {
+            /* fails to put the new media entry on to the list */
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"error %d when add media to list\n",
+                        dcb_p->line, dcb_p->call_id, fname, sll_lite_ret);
+            gsmsdp_free_media(media);
+            media = NULL;
+        }
+    }
+    return (media);
+}
+
+/**
+ * The function removes the media entry from the list of a given call and
+ * then deallocates the media entry.
+ *
+ * @param[in]dcb   - pointer to fsmdef_def_t for the dcb whose
+ *                   media to be removed from.
+ * @param[in]media - pointer to fsmdef_media_t for the media
+ *                   entry to be removed.
+ *
+ * @return  none
+ *
+ * @pre     (dcb not_eq NULL)
+ */
+static void gsmsdp_remove_media (fsmdef_dcb_t *dcb_p, fsmdef_media_t *media)
+{
+    static const char fname[] = "gsmsdp_remove_media";
+    cc_action_data_t data;
+
+    if (media == NULL) {
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"removing NULL media\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+        return;
+    }
+
+    if (media->rcv_chan || media->xmit_chan) {
+        /* stop media, if it is opened */
+        data.stop_media.media_refid = media->refid;
+        (void)cc_call_action(dcb_p->call_id, dcb_p->line, CC_ACTION_STOP_MEDIA,
+                             &data);
+    }
+    /* remove this media off the list */
+    (void)sll_lite_remove(&dcb_p->media_list, (sll_lite_node_t *)media);
+
+    /* Release the port */
+    vcmRxReleasePort(media->cap_index, dcb_p->group_id, media->refid,
+                 lsm_get_ms_ui_call_handle(dcb_p->line, dcb_p->call_id, CC_NO_CALL_ID), media->src_port);
+
+    /* free media structure */
+    gsmsdp_free_media(media);
+}
+
+/**
+ * The function performs cleaning media list of a given call. It walks
+ * through the list and deallocates each media entries.
+ *
+ * @param[in]dcb   - pointer to fsmdef_def_t for the dcb whose
+ *                   media list to be cleaned.
+ *
+ * @return  none
+ *
+ * @pre     (dcb not_eq NULL)
+ */
+void gsmsdp_clean_media_list (fsmdef_dcb_t *dcb_p)
+{
+    fsmdef_media_t *media = NULL;
+
+    while (TRUE) {
+        /* unlink head and free the media */
+        media = (fsmdef_media_t *)sll_lite_unlink_head(&dcb_p->media_list);
+        if (media != NULL) {
+            gsmsdp_free_media(media);
+        } else {
+            break;
+        }
+    }
+}
+
+/**
+ *
+ * The function is used for per call media list initialization. It is
+ * an interface function to other module for initializing the media list
+ * used during a call.
+ *
+ * @param[in]dcb_p   - pointer to the fsmdef_dcb_t where the media list
+ *                   will be attached to.
+ *
+ * @return           None.
+ * @pre              (dcb not_eq NULL)
+ */
+void gsmsdp_init_media_list (fsmdef_dcb_t *dcb_p)
+{
+    const cc_media_cap_table_t *media_cap_tbl;
+    const cc_media_remote_stream_table_t *media_track_tbl;
+    const char                 fname[] = "gsmsdp_init_media_list";
+
+    /* do the actual media element list initialization */
+    (void)sll_lite_init(&dcb_p->media_list);
+
+    media_cap_tbl = gsmsdp_get_media_capability(dcb_p);
+
+    if (media_cap_tbl == NULL) {
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"no media capbility available\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+    }
+
+    media_track_tbl = gsmsdp_get_media_stream_table(dcb_p);
+
+    if (media_track_tbl == NULL) {
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"no media tracks available\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+    }
+}
+
+/**
+ *
+ * Returns a pointer to the fsmdef_media_t in the dcb for the
+ * correspoinding media level in the SDP.
+ *
+ * @param[in]dcb_p      - pointer to the fsmdef_dcb_t
+ * @param[in]level      - uint16_t for media line level.
+ *
+ * @return           pointer to the fsmdef_media_t of the corresponding
+ *                   media entry in the dcb.
+ * @pre              (dcb not_eq NULL)
+ */
+static fsmdef_media_t *
+gsmsdp_find_media_by_level (fsmdef_dcb_t *dcb_p, uint16_t level)
+{
+    fsmdef_media_t *media = NULL;
+
+    /*
+     * search the all entries that has a valid media and matches
+     * the level.
+     */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+        if (media->level == level) {
+            /* found a match */
+            return (media);
+        }
+    }
+    return (NULL);
+}
+
+/**
+ *
+ * Returns a pointer to the fsmdef_media_t in the dcb for the
+ * correspoinding reference ID.
+ *
+ * @param[in]dcb_p      - pointer to the fsmdef_dcb_t
+ * @param[in]refid      - media reference ID to look for.
+ *
+ * @return           pointer to the fsmdef_media_t of the corresponding
+ *                   media entry in the dcb.
+ * @pre              (dcb not_eq NULL)
+ */
+fsmdef_media_t *
+gsmsdp_find_media_by_refid (fsmdef_dcb_t *dcb_p, media_refid_t refid)
+{
+    fsmdef_media_t *media = NULL;
+
+    /*
+     * search the all entries that has a valid media and matches
+     * the reference ID.
+     */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+        if (media->refid == refid) {
+            /* found a match */
+            return (media);
+        }
+    }
+    return (NULL);
+}
+
+/**
+ *
+ * Returns a pointer to the fsmdef_media_t in the dcb for the
+ * correspoinding capability index.
+ *
+ * @param[in]dcb_p      - pointer to the fsmdef_dcb_t
+ * @param[in]cap_index  - capability table index.
+ *
+ * @return           pointer to the fsmdef_media_t of the corresponding
+ *                   media entry in the dcb.
+ * @pre              (dcb not_eq NULL)
+ */
+static fsmdef_media_t *
+gsmsdp_find_media_by_cap_index (fsmdef_dcb_t *dcb_p, uint8_t cap_index)
+{
+    fsmdef_media_t *media = NULL;
+
+    /*
+     * search the all entries that has a valid media and matches
+     * the reference ID.
+     */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+        if (media->cap_index == cap_index) {
+            /* found a match */
+            return (media);
+        }
+    }
+    return (NULL);
+
+}
+
+/**
+ *
+ * Returns a pointer to the fsmdef_media_t in the dcb for the
+ * first audio type in the SDP.
+ *
+ * @param[in]dcb_p      - pointer to the fsmdef_dcb_t.
+ *
+ * @return           pointer to the fsmdef_media_t of the corresponding
+ *                   media entry in the dcb.
+ * @pre              (dcb not_eq NULL)
+ */
+fsmdef_media_t *gsmsdp_find_audio_media (fsmdef_dcb_t *dcb_p)
+{
+    fsmdef_media_t *media = NULL;
+
+    /*
+     * search the all entries that has a valid media and matches
+     * SDP_MEDIA_AUDIO type.
+     */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+        if (media->type == SDP_MEDIA_AUDIO) {
+            /* found a match */
+            return (media);
+        }
+    }
+    return (NULL);
+}
+
+/**
+ *
+ * The function finds an unused media line given type.
+ *
+ * @param[in]sdp        - void pointer of thd SDP libray handle.
+ * @param[in]media_type - media type of the unused line.
+ *
+ * @return           level (line) of the unused one if found or
+ *                   0 if there is no unused one found.
+ */
+static uint16_t
+gsmsdp_find_unused_media_line_with_type (void *sdp, sdp_media_e media_type)
+{
+    uint16_t num_m_lines, level;
+    int32_t  port;
+
+    num_m_lines  = sdp_get_num_media_lines(sdp);
+    for (level = 1; level <= num_m_lines; level++) {
+        port = sdp_get_media_portnum(sdp, level);
+        if (port == 0) {
+            /* This slot is not used, check the type */
+            if (sdp_get_media_type(sdp, level) == media_type) {
+                /* Found an empty slot that has the same media type */
+                return (level);
+            }
+        }
+    }
+    /* no unused media line of the given type found */
+    return (0);
+}
+
+/**
+ *
+ * The function returns the media cap entry pointer to the caller based
+ * on the index.
+ *
+ * @param[in]cap_index  - uint8_t for index of the media cap table.
+ *
+ * @return           pointer to the media cap entry if one is available.
+ *                   NULL if none is available.
+ *
+ */
+static const cc_media_cap_t *
+gsmsdp_get_media_cap_entry_by_index (uint8_t cap_index, fsmdef_dcb_t *dcb_p)
+{
+    const cc_media_cap_table_t *media_cap_tbl;
+
+    media_cap_tbl = dcb_p->media_cap_tbl;
+
+    if (media_cap_tbl == NULL) {
+        return (NULL);
+    }
+
+    if (cap_index >= CC_MAX_MEDIA_CAP) {
+        return (NULL);
+    }
+    return (&media_cap_tbl->cap[cap_index]);
+}
+
+/**
+ *
+ * Returns a pointer to the fsmdef_media_t in the dcb for the
+ * corresponding media line. It looks for another media line
+ * with the same type and cap_index but different level
+ *
+ * @param[in]dcb_p      - pointer to the fsmdef_dcb_t
+ * @param[in]media      - current media level.
+ *
+ * @return           pointer to the fsmdef_media_t of the corresponding
+ *                   media entry in the dcb.
+ * @pre              (dcb not_eq NULL)
+ */
+fsmdef_media_t *
+gsmsdp_find_anat_pair (fsmdef_dcb_t *dcb_p, fsmdef_media_t *media)
+{
+    fsmdef_media_t *searched_media = NULL;
+
+    /*
+     * search the all entries that has a the same capability index
+     * but at a different level. The only time that this is true is
+     * both media are in the same ANAT group.
+     */
+    GSMSDP_FOR_ALL_MEDIA(searched_media, dcb_p) {
+        if ((searched_media->cap_index == media->cap_index) &&
+            (searched_media->level != media->level)) {
+            /* found a match */
+            return (searched_media);
+        }
+    }
+    return (NULL);
+}
+
+/**
+ *
+ * The function queries platform to see if the platform is capable
+ * of handle mixing additional media or not.
+ *
+ * P2: This may go away when integrate with the platform.
+ *
+ * @param[in]dcb_p      - pointer to the fsmdef_dcb_t structure.
+ * @param[in]media_type - media type to be mixed.
+ *
+ * @return          TRUE the media can be mixed.
+ *                  FALSE the media can not be mixed
+ *
+ * @pre            (dcb_p not_eq NULL)
+ */
+static boolean
+gsmsdp_platform_addition_mix (fsmdef_dcb_t *dcb_p, sdp_media_e media_type)
+{
+    return (FALSE);
+}
+
+
+/**
+ *
+ * The function updates the local time stamp during SDP offer/answer
+ * processing.
+ *
+ * @param[in]dcb_p         - pointer to the fsmdef_dcb_t
+ * @param[in]offer         - boolean indicates this is procssing an offered
+ *                           SDP
+ * @param[in]initial_offer - boolean indicates this is processin an
+ *                           initial offered SDP.
+ *
+ * @return           none.
+ * @pre              (dcb not_eq NULL)
+ */
+static void
+gsmsdp_update_local_time_stamp (fsmdef_dcb_t *dcb_p, boolean offer,
+                                boolean initial_offer)
+{
+    const char                 fname[] = "gsmsdp_update_local_time_stamp";
+    void           *local_sdp_p;
+    void           *remote_sdp_p;
+
+    local_sdp_p  = dcb_p->sdp->src_sdp;
+    remote_sdp_p = dcb_p->sdp->dest_sdp;
+
+    /*
+     * If we are processing an offer sdp, need to set the
+     * start time and stop time based on the remote SDP
+     */
+    if (initial_offer) {
+        /*
+         * Per RFC3264, time description of answer must equal that
+         * of the offer.
+         */
+        (void) sdp_set_time_start(local_sdp_p,
+                                  sdp_get_time_start(remote_sdp_p));
+        (void) sdp_set_time_stop(local_sdp_p, sdp_get_time_stop(remote_sdp_p));
+    } else if (offer) {
+        /*
+         * Set t= line based on remote SDP
+         */
+        if (sdp_timespec_valid(remote_sdp_p) != TRUE) {
+            GSM_DEBUG(DEB_L_C_F_PREFIX"\nTimespec is invalid.\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+            (void) sdp_set_time_start(local_sdp_p, "0");
+            (void) sdp_set_time_stop(local_sdp_p, "0");
+        } else {
+            if (sdp_get_time_start(local_sdp_p) !=
+                sdp_get_time_start(remote_sdp_p)) {
+                (void) sdp_set_time_start(local_sdp_p,
+                                          sdp_get_time_start(remote_sdp_p));
+            }
+            if (sdp_get_time_stop(local_sdp_p) !=
+                sdp_get_time_stop(remote_sdp_p)) {
+                (void) sdp_set_time_stop(local_sdp_p,
+                                         sdp_get_time_stop(remote_sdp_p));
+            }
+        }
+    }
+}
+
+/**
+ *
+ * The function gets the local source address address and puts it into
+ * the media entry.
+ *
+ * @param[in]media   - pointer to fsmdef_media_t structure to
+ *                     get the local address into.
+ *
+ * @return           none.
+ * @pre              (media not_eq NULL)
+ */
+static void
+gsmsdp_get_local_source_v4_address (fsmdef_media_t *media)
+{
+    int              nat_enable = 0;
+    char             curr_media_ip[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t    addr;
+    const char       fname[] = "gsmsdp_get_local_source_v4_address";
+
+    /*
+     * Get device address.
+     */;
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        init_empty_str(curr_media_ip);
+        config_get_value(CFGID_MEDIA_IP_ADDR, curr_media_ip,
+                        MAX_IPADDR_STR_LEN);
+        if (is_empty_str(curr_media_ip) == FALSE) {
+
+                str2ip(curr_media_ip, &addr);
+             util_ntohl(&addr, &addr);
+             if (util_check_if_ip_valid(&media->src_addr) == FALSE)  {
+                 // Update the media Src address only if it is invalid
+                 media->src_addr = addr;
+                 GSM_ERR_MSG("%s:  Update IP %s", fname, curr_media_ip);
+             }
+        } else {
+            sip_config_get_net_device_ipaddr(&media->src_addr);
+        }
+    } else {
+        sip_config_get_nat_ipaddr(&media->src_addr);
+    }
+}
+
+/*
+ *
+ * The function gets the local source address address and puts it into
+ * the media entry.
+ *
+ * @param[in]media   - pointer to fsmdef_media_t structure to
+ *                     get the local address into.
+ *
+ * @return           none.
+ * @pre              (media not_eq NULL)
+ */
+static void
+gsmsdp_get_local_source_v6_address (fsmdef_media_t *media)
+{
+    int      nat_enable = 0;
+
+    /*
+     * Get device address.
+     */
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        sip_config_get_net_ipv6_device_ipaddr(&media->src_addr);
+    } else {
+        sip_config_get_nat_ipaddr(&media->src_addr);
+    }
+}
+
+/**
+ * Set the connection address into the SDP.
+ *
+ * @param[in]sdp_p   - pointer to SDP (type void)
+ * @param[in]level   - media level or line.
+ * @param[in]addr    - string representation of IP address.
+ *                     Assumed to be IPV6 if larger than 15.
+ *
+ * @return           none.
+ * @pre              (sdp_p not_eq NULL) and (addr not_eq NULL)
+ *
+ */
+static void
+gsmsdp_set_connection_address (void *sdp_p, uint16_t level, char *addr)
+{
+    /*
+     * c= line <network type><address type><connection address>
+     */
+
+    (void) sdp_set_conn_nettype(sdp_p, level, SDP_NT_INTERNET);
+
+    if (addr && (strlen(addr) > strlen("123.123.123.123")))
+    {
+      // Long IP address, must be IPV6
+      (void) sdp_set_conn_addrtype(sdp_p, level, SDP_AT_IP6);
+    }
+    else
+    {
+      (void) sdp_set_conn_addrtype(sdp_p, level, SDP_AT_IP4);
+    }
+
+    (void) sdp_set_conn_address(sdp_p, level, addr);
+}
+
+/*
+ * gsmsdp_set_2543_hold_sdp
+ *
+ * Description:
+ *
+ * Manipulates the local SDP of the specified DCB to indicate hold
+ * to the far end using 2543 style signaling.
+ *
+ * Parameters:
+ *
+ * dcb_p - Pointer to the DCB whose SDP is to be manipulated.
+ *
+ */
+static void
+gsmsdp_set_2543_hold_sdp (fsmdef_dcb_t *dcb_p, uint16 level)
+{
+    (void) sdp_set_conn_nettype(dcb_p->sdp->src_sdp, level, SDP_NT_INTERNET);
+    (void) sdp_set_conn_addrtype(dcb_p->sdp->src_sdp, level, SDP_AT_IP4);
+    (void) sdp_set_conn_address(dcb_p->sdp->src_sdp, level, "0.0.0.0");
+}
+
+
+/*
+ * gsmsdp_set_video_media_attributes
+ *
+ * Description:
+ *
+ * Add the specified video media format to the SDP.
+ *
+ * Parameters:
+ *
+ * media_type - The media type (format) to add to the specified SDP.
+ * sdp_p - Pointer to the SDP the media attribute is to be added to.
+ * level - The media level of the SDP where the media attribute is to be added.
+ * payload_number - AVT payload type if the media attribute being added is
+ *                  RTP_AVT.
+ *
+ */
+static void
+gsmsdp_set_video_media_attributes (uint32_t media_type, void *cc_sdp_p, uint16_t level,
+                             uint16_t payload_number)
+{
+    uint16_t a_inst;
+    void *sdp_p = ((cc_sdp_t*)cc_sdp_p)->src_sdp;
+
+    switch (media_type) {
+        case RTP_H263:
+        case RTP_H264_P0:
+        case RTP_H264_P1:
+        case RTP_VP8:
+        /*
+         * add a=rtpmap line
+         */
+        if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_RTPMAP, &a_inst)
+                != SDP_SUCCESS) {
+            return;
+        }
+
+        (void) sdp_attr_set_rtpmap_payload_type(sdp_p, level, 0, a_inst,
+                                                payload_number);
+
+        switch (media_type) {
+        case RTP_H263:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_H263v2);
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                             RTPMAP_VIDEO_CLOCKRATE);
+            break;
+        case RTP_H264_P0:
+        case RTP_H264_P1:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_H264);
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                             RTPMAP_VIDEO_CLOCKRATE);
+            break;
+        case RTP_VP8:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_VP8);
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                             RTPMAP_VIDEO_CLOCKRATE);
+            break;
+        }
+    GSM_DEBUG("gsmsdp_set_video_media_attributes- populate attribs %d\n", payload_number );
+
+        vcmPopulateAttribs(cc_sdp_p, level, media_type, payload_number, FALSE);
+
+        break;
+
+        default:
+            break;
+    }
+}
+
+/*
+ * gsmsdp_set_media_attributes
+ *
+ * Description:
+ *
+ * Add the specified media format to the SDP.
+ *
+ * Parameters:
+ *
+ * media_type - The media type (format) to add to the specified SDP.
+ * sdp_p - Pointer to the SDP the media attribute is to be added to.
+ * level - The media level of the SDP where the media attribute is to be added.
+ * payload_number - AVT payload type if the media attribute being added is
+ *                  RTP_AVT.
+ *
+ */
+static void
+gsmsdp_set_media_attributes (uint32_t media_type, void *sdp_p, uint16_t level,
+                             uint16_t payload_number)
+{
+    uint16_t a_inst, a_inst2, a_inst3, a_inst4;
+    int      maxavbitrate = 0;
+    int      maxcodedaudiobw = 0;
+    int      usedtx = 0;
+    int      stereo = 0;
+    int      useinbandfec = 0;
+    int      cbr = 0;
+    int      maxptime = 0;
+
+
+    config_get_value(CFGID_MAXAVBITRATE, &maxavbitrate, sizeof(maxavbitrate));
+    config_get_value(CFGID_MAXCODEDAUDIOBW, &maxcodedaudiobw, sizeof(maxcodedaudiobw));
+    config_get_value(CFGID_USEDTX, &usedtx, sizeof(usedtx));
+    config_get_value(CFGID_STEREO, &stereo, sizeof(stereo));
+    config_get_value(CFGID_USEINBANDFEC, &useinbandfec, sizeof(useinbandfec));
+    config_get_value(CFGID_CBR, &cbr, sizeof(cbr));
+    config_get_value(CFGID_MAXPTIME, &maxptime, sizeof(maxptime));
+
+
+
+    switch (media_type) {
+    case RTP_PCMU:             // type 0
+    case RTP_PCMA:             // type 8
+    case RTP_G729:             // type 18
+    case RTP_G722:             // type 9
+    case RTP_ILBC:
+    case RTP_L16:
+    case RTP_ISAC:
+    case RTP_OPUS:
+        /*
+         * add a=rtpmap line
+         */
+        if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_RTPMAP, &a_inst)
+                != SDP_SUCCESS) {
+            return;
+        }
+
+        (void) sdp_attr_set_rtpmap_payload_type(sdp_p, level, 0, a_inst,
+                                                payload_number);
+
+        switch (media_type) {
+        case RTP_PCMU:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_PCMU);
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                                 RTPMAP_CLOCKRATE);
+            break;
+        case RTP_PCMA:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_PCMA);
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                                 RTPMAP_CLOCKRATE);
+            break;
+        case RTP_G729:
+            {
+
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_G729);
+            if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst2)
+                    != SDP_SUCCESS) {
+                return;
+            }
+            (void) sdp_attr_set_fmtp_payload_type(sdp_p, level, 0, a_inst2,
+                                                  payload_number);
+            (void) sdp_attr_set_fmtp_annexb(sdp_p, level, 0, a_inst2, FALSE);
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                                 RTPMAP_CLOCKRATE);
+            }
+            break;
+
+        case RTP_G722:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_G722);
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                                 RTPMAP_CLOCKRATE);
+            break;
+
+        case RTP_L16:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_L16_256K);
+
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                                 RTPMAP_L16_CLOCKRATE);
+            break;
+
+        case RTP_ILBC:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_ILBC);
+            if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst2)
+                    != SDP_SUCCESS) {
+                return;
+            }
+            (void) sdp_attr_set_fmtp_payload_type(sdp_p, level, 0, a_inst2,
+                                                  payload_number);
+            (void) sdp_attr_set_fmtp_mode(sdp_p, level, 0, a_inst2, vcmGetILBCMode());
+
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                                RTPMAP_CLOCKRATE);
+            break;
+
+        case RTP_ISAC:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_ISAC);
+
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                                 RTPMAP_ISAC_CLOCKRATE);
+            break;
+
+        case RTP_OPUS:
+            (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                               SIPSDP_ATTR_ENCNAME_OPUS);
+
+            (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                       RTPMAP_OPUS_CLOCKRATE);
+            (void) sdp_attr_set_rtpmap_num_chan (sdp_p, level, 0, a_inst, 2);
+
+            /* a=fmtp options */
+            if (maxavbitrate || maxcodedaudiobw || usedtx || stereo || useinbandfec || cbr) {
+                if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst2)
+                    != SDP_SUCCESS) {
+                    return;
+                }
+
+                (void) sdp_attr_set_fmtp_payload_type (sdp_p, level, 0, a_inst2, payload_number);
+
+                if (maxavbitrate)
+                    sdp_attr_set_fmtp_max_average_bitrate (sdp_p, level, 0, a_inst2, FMTP_MAX_AVERAGE_BIT_RATE);
+
+                if(usedtx)
+                    sdp_attr_set_fmtp_usedtx (sdp_p, level, 0, a_inst2, FALSE);
+
+                if(stereo)
+                    sdp_attr_set_fmtp_stereo (sdp_p, level, 0, a_inst2, FALSE);
+
+                if(useinbandfec)
+                    sdp_attr_set_fmtp_useinbandfec (sdp_p, level, 0, a_inst2, FALSE);
+
+                if(maxcodedaudiobw) {
+                    sdp_attr_set_fmtp_maxcodedaudiobandwidth (sdp_p, level, 0, a_inst2,
+                            max_coded_audio_bandwidth_table[opus_fb].name);
+                }
+
+                if(cbr)
+                    sdp_attr_set_fmtp_cbr (sdp_p, level, 0, a_inst2, FALSE);
+            }
+
+            /* a=ptime attribute */
+            if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_PTIME, &a_inst3)
+                    != SDP_SUCCESS) {
+                return;
+            }
+
+            sdp_attr_set_simple_u32(sdp_p, SDP_ATTR_PTIME, level, 0, a_inst3, ATTR_PTIME);
+
+            if(maxptime) {
+                /* a=maxptime attribute */
+                if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_MAXPTIME, &a_inst4)
+                        != SDP_SUCCESS) {
+                    return;
+                }
+
+                sdp_attr_set_simple_u32(sdp_p, SDP_ATTR_MAXPTIME, level, 0, a_inst4, ATTR_MAXPTIME);
+            }
+
+            break;
+        }
+        break;
+
+    case RTP_AVT:
+        /*
+         * add a=rtpmap line
+         */
+        if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_RTPMAP, &a_inst)
+                != SDP_SUCCESS) {
+            return;
+        }
+        (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
+                                           SIPSDP_ATTR_ENCNAME_TEL_EVENT);
+        (void) sdp_attr_set_rtpmap_payload_type(sdp_p, level, 0, a_inst,
+                                                payload_number);
+        (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
+                                             RTPMAP_CLOCKRATE);
+
+        /*
+         * Malloc the mediainfo structure
+         */
+        if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst)
+                != SDP_SUCCESS) {
+            return;
+        }
+        (void) sdp_attr_set_fmtp_payload_type(sdp_p, level, 0, a_inst,
+                                              payload_number);
+        (void) sdp_attr_set_fmtp_range(sdp_p, level, 0, a_inst,
+                                       SIPSDP_NTE_DTMF_MIN,
+                                       SIPSDP_NTE_DTMF_MAX);
+
+
+        break;
+
+    default:
+        /* The remaining coded types aren't supported, but are listed below
+         * as a reminder
+         *   RTP_CELP         = 1,
+         *   RTP_GSM          = 3,
+         *   RTP_G726         = 2,
+         *   RTP_G723         = 4,
+         *   RTP_DVI4         = 5,
+         *   RTP_DVI4_II      = 6,
+         *   RTP_LPC          = 7,
+         *   RTP_G722         = 9,
+         *   RTP_G728         = 15,
+         *   RTP_JPEG         = 26,
+         *   RTP_NV           = 28,
+         *   RTP_H261         = 31
+         */
+
+        break;
+    }
+}
+
+/*
+ * gsmsdp_set_sctp_attributes
+ *
+ * Description:
+ *
+ * Add the specified SCTP media format to the SDP.
+ *
+ * Parameters:
+ *
+ * sdp_p - Pointer to the SDP the media attribute is to be added to.
+ * level - The media level of the SDP where the media attribute is to be added.
+ */
+static void
+gsmsdp_set_sctp_attributes (void *sdp_p, uint16_t level, fsmdef_media_t *media)
+{
+    uint16_t a_inst;
+
+    if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst)
+        != SDP_SUCCESS) {
+         return;
+    }
+
+    /* Use SCTP port in place of fmtp payload type */
+    (void) sdp_attr_set_fmtp_payload_type(sdp_p, level, 0, a_inst, media->sctp_port);
+
+    sdp_attr_set_fmtp_data_channel_protocol (sdp_p, level, 0, a_inst, WEBRTC_DATA_CHANNEL_PROT);
+
+    sdp_attr_set_fmtp_streams (sdp_p, level, 0, a_inst, 16);
+}
+
+/*
+ * gsmsdp_set_remote_sdp
+ *
+ * Description:
+ *
+ * Sets the specified SDP as the remote SDP in the DCB.
+ *
+ * Parameters:
+ *
+ * dcb_p - Pointer to the DCB.
+ * sdp_p - Pointer to the SDP to be set as the remote SDP in the DCB.
+ */
+static void
+gsmsdp_set_remote_sdp (fsmdef_dcb_t *dcb_p, cc_sdp_t *sdp_p)
+{
+    dcb_p->remote_sdp_present = TRUE;
+}
+
+/*
+ * gsmsdp_get_sdp_direction_attr
+ *
+ * Description:
+ *
+ * Given a sdp_direction_e enumerated type, returns a sdp_attr_e
+ * enumerated type.
+ *
+ * Parameters:
+ *
+ * direction - The SDP direction used to determine which sdp_attr_e to return.
+ *
+ */
+static sdp_attr_e
+gsmsdp_get_sdp_direction_attr (sdp_direction_e direction)
+{
+    sdp_attr_e sdp_attr = SDP_ATTR_SENDRECV;
+
+    switch (direction) {
+    case SDP_DIRECTION_INACTIVE:
+        sdp_attr = SDP_ATTR_INACTIVE;
+        break;
+    case SDP_DIRECTION_SENDONLY:
+        sdp_attr = SDP_ATTR_SENDONLY;
+        break;
+    case SDP_DIRECTION_RECVONLY:
+        sdp_attr = SDP_ATTR_RECVONLY;
+        break;
+    case SDP_DIRECTION_SENDRECV:
+        sdp_attr = SDP_ATTR_SENDRECV;
+        break;
+    default:
+        GSM_ERR_MSG("\nFSMDEF ERROR: replace with formal error text");
+    }
+
+    return sdp_attr;
+}
+
+/*
+ * gsmsdp_set_sdp_direction
+ *
+ * Description:
+ *
+ * Adds a direction attribute to the given media line in the
+ * specified SDP.
+ *
+ * Parameters:
+ *
+ * media     - pointer to the fsmdef_media_t for the media entry.
+ * direction - The direction to use when setting the direction attribute.
+ * sdp_p - Pointer to the SDP to set the direction attribute against.
+ */
+static void
+gsmsdp_set_sdp_direction (fsmdef_media_t *media,
+                          sdp_direction_e direction, void *sdp_p)
+{
+    sdp_attr_e    sdp_attr = SDP_ATTR_SENDRECV;
+    uint16_t      a_instance = 0;
+
+    /*
+     * Convert the direction to an SDP direction attribute.
+     */
+    sdp_attr = gsmsdp_get_sdp_direction_attr(direction);
+    if (media->level) {
+       (void) sdp_add_new_attr(sdp_p, media->level, 0, sdp_attr, &a_instance);
+    } else {
+       /* Just in case that there is no level defined, add to the session */
+       (void) sdp_add_new_attr(sdp_p, SDP_SESSION_LEVEL, 0, sdp_attr,
+                               &a_instance);
+    }
+}
+
+/*
+ * gsmsdp_get_ice_attributes
+ *
+ * Description:
+ *
+ * Returns the ice attribute strings at a given level
+ *
+ * Parameters:
+ *
+ * session         - true = session level attributes, false = media line attribs
+ * level           - The media level of the SDP where the media attribute exists.
+ * sdp_p           - Pointer to the SDP whose ice candidates are being searched.
+ * ice_attribs     - return ice attribs at this level in an array
+ * attributes_ctp  - count of array of media line attributes
+ */
+
+static boolean
+gsmsdp_get_ice_attributes (sdp_attr_e sdp_attr, uint16_t level, void *sdp_p, char ***ice_attribs, int *attributes_ctp)
+{
+    uint16_t        num_a_lines = 0;
+    uint16_t        i;
+    sdp_result_e    result;
+    char*           ice_attrib;
+
+    result = sdp_attr_num_instances(sdp_p, level, 0, sdp_attr, &num_a_lines);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG("enumerating ICE attributes failed\n");
+        return FALSE;
+    }
+
+    if (num_a_lines < 1) {
+       GSM_ERR_MSG("enumerating ICE attributes returned 0 attributes\n");
+       return TRUE;
+    }
+
+    *ice_attribs = (char **)cpr_malloc(num_a_lines * sizeof(char *));
+
+    if (!(*ice_attribs))
+      return FALSE;
+
+    *attributes_ctp = 0;
+
+    for (i = 0; i < num_a_lines; i++) {
+        result = sdp_attr_get_ice_attribute (sdp_p, level, 0, sdp_attr, (uint16_t) (i + 1),
+          &ice_attrib);
+        if (result != SDP_SUCCESS) {
+               GSM_ERR_MSG("Failed to retrieve ICE attribute\n");
+               cpr_free(*ice_attribs);
+               return FALSE;
+       }
+        (*ice_attribs)[i] = (char *) cpr_calloc(1, strlen(ice_attrib) + 1);
+        if(!(*ice_attribs)[i])
+               return FALSE;
+
+        sstrncpy((*ice_attribs)[i], ice_attrib, strlen(ice_attrib) + 1);
+        (*attributes_ctp)++;
+    }
+
+    return TRUE;
+}
+
+/*
+ * gsmsdp_set_attributes
+ *
+ * Description:
+ *
+ * Adds an ice attribute attributes to the specified SDP.
+ *
+ * Parameters:
+ *
+ * session      - true = session level attribute, false = media line attribute
+ * level        - The media level of the SDP where the media attribute exists.
+ * sdp_p        - Pointer to the SDP to set the ice candidate attribute against.
+ * ice_attrib   - ice attribute to set
+ */
+static void
+gsmsdp_set_ice_attribute (sdp_attr_e sdp_attr, uint16_t level, void *sdp_p, char *ice_attrib)
+{
+    uint16_t      a_instance = 0;
+    sdp_result_e  result;
+
+    result = sdp_add_new_attr(sdp_p, level, 0, sdp_attr, &a_instance);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG("Failed to add attribute\n");
+        return;
+    }
+
+    result = sdp_attr_set_ice_attribute(sdp_p, level, 0, sdp_attr, a_instance, ice_attrib);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG("Failed to set attribute\n");
+    }
+}
+
+/*
+ * gsmsdp_set_rtcp_mux_attribute
+ *
+ * Description:
+ *
+ * Adds an ice attribute attributes to the specified SDP.
+ *
+ * Parameters:
+ *
+ * session      - true = session level attribute, false = media line attribute
+ * level        - The media level of the SDP where the media attribute exists.
+ * sdp_p        - Pointer to the SDP to set the ice candidate attribute against.
+ * rtcp_mux     - ice attribute to set
+ */
+static void
+gsmsdp_set_rtcp_mux_attribute (sdp_attr_e sdp_attr, uint16_t level, void *sdp_p, boolean rtcp_mux)
+{
+    uint16_t      a_instance = 0;
+    sdp_result_e  result;
+
+    result = sdp_add_new_attr(sdp_p, level, 0, sdp_attr, &a_instance);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG("Failed to add attribute\n");
+        return;
+    }
+
+    result = sdp_attr_set_rtcp_mux_attribute(sdp_p, level, 0, sdp_attr, a_instance, rtcp_mux);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG("Failed to set attribute\n");
+    }
+}
+
+/*
+ * gsmsdp_set_dtls_fingerprint_attribute
+ *
+ * Description:
+ *
+ * Adds an dtls fingerprint attribute attributes to the specified SDP.
+ *
+ * Parameters:
+ *
+ * session      - true = session level attribute, false = media line attribute
+ * level        - The media level of the SDP where the media attribute exists.
+ * sdp_p        - Pointer to the SDP to set the ice candidate attribute against.
+ * hash_func    - hash function string, e.g. "sha-1"
+ * hash_func_len   - string len
+ * fingerprint     - fingerprint attribute to set
+ * fingerprint_len - string len of fingerprint
+ */
+static void
+gsmsdp_set_dtls_fingerprint_attribute (sdp_attr_e sdp_attr, uint16_t level, void *sdp_p,
+  char *hash_func,char *fingerprint)
+{
+    uint16_t      a_instance = 0;
+    sdp_result_e  result;
+    char hash_and_fingerprint[FSMDEF_MAX_DIGEST_ALG_LEN + FSMDEF_MAX_DIGEST_LEN + 2];
+
+    snprintf(hash_and_fingerprint, sizeof(hash_and_fingerprint),
+         "%s %s", hash_func, fingerprint);
+
+    result = sdp_add_new_attr(sdp_p, level, 0, sdp_attr, &a_instance);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG("Failed to add attribute\n");
+        return;
+    }
+
+    result = sdp_attr_set_dtls_fingerprint_attribute(sdp_p, level, 0, sdp_attr, a_instance, hash_and_fingerprint);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG("Failed to set dtls fingerprint attribute\n");
+    }
+}
+
+/*
+ * gsmsdp_remove_sdp_direction
+ *
+ * Description:
+ *
+ * Removes the direction attribute corresponding to the passed in direction
+ * from the media line of the specified SDP.
+ *
+ * Parameters:
+ *
+ * media     - pointer to the fsmdef_media_t for the media entry.
+ * direction - The direction whose corresponding direction attribute
+ *             is to be removed.
+ * sdp_p - Pointer to the SDP where the direction attribute is to be
+ *         removed.
+ */
+static void
+gsmsdp_remove_sdp_direction (fsmdef_media_t *media,
+                             sdp_direction_e direction, void *sdp_p)
+{
+    sdp_attr_e    sdp_attr = SDP_ATTR_SENDRECV;
+
+    sdp_attr = gsmsdp_get_sdp_direction_attr(direction);
+    (void) sdp_delete_attr(sdp_p, media->level, 0, sdp_attr, 1);
+}
+
+/*
+ * gsmsdp_set_local_sdp_direction
+ *
+ * Description:
+ *
+ * Sets the direction attribute for the local SDP.
+ *
+ * Parameters:
+ *
+ * dcb_p - The DCB where the local SDP is located.
+ * media - Pointer to fsmdef_media_t for the media entry of the SDP.
+ * direction - The media direction to set into the local SDP.
+ */
+void
+gsmsdp_set_local_sdp_direction (fsmdef_dcb_t *dcb_p,
+                                fsmdef_media_t *media,
+                                sdp_direction_e direction)
+{
+    /*
+     * If media direction was previously set, remove the direction attribute
+     * before adding the specified direction. Save the direction in previous
+     * direction before clearing it.
+     */
+    if (media->direction_set) {
+        media->previous_sdp.direction = media->direction;
+        gsmsdp_remove_sdp_direction(media, media->direction,
+                                    dcb_p->sdp ? dcb_p->sdp->src_sdp : NULL );
+        media->direction_set = FALSE;
+    }
+    gsmsdp_set_sdp_direction(media, direction, dcb_p->sdp ? dcb_p->sdp->src_sdp : NULL);
+    /*
+     * We could just get the direction from the local SDP when we need it in
+     * GSM, but setting the direction in the media structure gives a quick way
+     * to access the media direction.
+     */
+    media->direction = direction;
+    media->direction_set = TRUE;
+}
+
+/*
+ * gsmsdp_get_remote_sdp_direction
+ *
+ * Description:
+ *
+ * Returns the media direction from the specified SDP. We will check for the
+ * media direction attribute at the session level and the first AUDIO media line.
+ * If the direction attribute is specified at the media level, the media level setting
+ * overrides the session level attribute.
+ *
+ * Parameters:
+ *
+ * dcb_p - pointer to the fsmdef_dcb_t.
+ * level - media line level.
+ * dest_addr - pointer to the remote address.
+ */
+static sdp_direction_e
+gsmsdp_get_remote_sdp_direction (fsmdef_dcb_t *dcb_p, uint16_t level,
+                                 cpr_ip_addr_t *dest_addr)
+{
+    sdp_direction_e direction = SDP_DIRECTION_SENDRECV;
+    cc_sdp_t       *sdp_p = dcb_p->sdp;
+    uint16_t       media_attr;
+    uint16_t       i;
+    static const sdp_attr_e  dir_attr_array[] = {
+        SDP_ATTR_INACTIVE,
+        SDP_ATTR_RECVONLY,
+        SDP_ATTR_SENDONLY,
+        SDP_ATTR_SENDRECV,
+        SDP_MAX_ATTR_TYPES
+    };
+
+    if (!sdp_p->dest_sdp) {
+        return direction;
+    }
+
+    media_attr = 0; /* media level attr. count */
+    /*
+     * Now check for direction as a media attribute. If found, the
+     * media attribute takes precedence over direction specified
+     * as a session attribute.
+     *
+     * In order to find out whether there is a direction attribute
+     * associated with a media line (or even at the
+     * session level) or not is to get the number of instances of
+     * that attribute via the sdp_attr_num_instances() first. The is
+     * because the sdp_get_media_direction() always returns a valid
+     * direction value even when there is no direction attribute with
+     * the media line (or session level).
+     *
+     * Note: there is no single attribute value to pass to the
+     * sdp_attr_num_instances() to get number a direction attribute that
+     * represents inactive, recvonly, sendonly, sendrcv. We have to
+     * look for each one of them individually.
+     */
+    for (i = 0; (dir_attr_array[i] != SDP_MAX_ATTR_TYPES); i++) {
+        if (sdp_attr_num_instances(sdp_p->dest_sdp, level, 0,
+                                   dir_attr_array[i], &media_attr) ==
+            SDP_SUCCESS) {
+            if (media_attr) {
+                /* There is direction attribute in the media line */
+                direction = sdp_get_media_direction(sdp_p->dest_sdp,
+                                                    level, 0);
+                break;
+            }
+        }
+    }
+
+    /*
+     * Check for the direction attribute. The direction can be specified
+     * as a session attribute or a media stream attribute. If the direction
+     * is specified as a session attribute, the direction is applicable to
+     * all media streams in the SDP.
+     */
+    if (media_attr == 0) {
+        /* no media level direction, get the direction from session */
+        direction = sdp_get_media_direction(sdp_p->dest_sdp,
+                                            SDP_SESSION_LEVEL, 0);
+    }
+
+    /*
+     * To support legacy way of signaling remote hold, we will interpret
+     * c=0.0.0.0 to be a=inactive
+     */
+    if (dest_addr->type == CPR_IP_ADDR_IPV4 &&
+        dest_addr->u.ip4 == 0) {
+
+        direction = SDP_DIRECTION_INACTIVE;
+    } else {
+
+        //todo IPv6: reject the request.
+    }
+    return direction;
+}
+
+/**
+ *
+ * The function overrides direction for some special feature
+ * processing.
+ *
+ * @param[in]dcb_p  - pointer to the fsmdef_dcb_t
+ * @param[in]media  - pointer to fsmdef_media_t for the media to
+ *                    override the direction.
+ *
+ * @return           None.
+ * @pre              (dcb_p not_eq NULL) and (media not_eq NULL)
+ */
+static void
+gsmsdp_feature_overide_direction (fsmdef_dcb_t *dcb_p, fsmdef_media_t *media)
+{
+    /*
+     * Disable video if this is a BARGE with video
+     */
+    if ( CC_IS_VIDEO(media->cap_index) &&
+                   dcb_p->join_call_id != CC_NO_CALL_ID ){
+        media->support_direction = SDP_DIRECTION_INACTIVE;
+    }
+
+    if (CC_IS_VIDEO(media->cap_index) && media->support_direction == SDP_DIRECTION_INACTIVE) {
+        DEF_DEBUG(GSM_F_PREFIX"video capability disabled to SDP_DIRECTION_INACTIVE \n", "gsmsdp_feature_overide_direction");
+    }
+}
+
+/*
+ * gsmsdp_negotiate_local_sdp_direction
+ *
+ * Description:
+ *
+ * Given an offer SDP, return the corresponding answer SDP direction.
+ *
+ * local hold   remote direction support direction new local direction
+ * enabled      inactive            any                 inactive
+ * enabled      sendrecv          sendonly              sendonly
+ * enabled      sendrecv          recvonly              inactive
+ * enabled      sendrecv          sendrecv              sendonly
+ * enabled      sendrecv          inactive              inactive
+ * enabled      sendonly            any                 inactive
+ * enabled      recvonly          sendrecv              sendonly
+ * enabled      recvonly          sendonly              sendonly
+ * enabled      recvonly          recvonly              inactive
+ * enabled      recvonly          inactive              inactive
+ * disabled     inactive            any                 inactive
+ * disabled     sendrecv          sendrecv              sendrecv
+ * disabled     sendrecv          sendonly              sendonly
+ * disabled     sendrecv          recvonly              recvonly
+ * disabled     sendrecv          inactive              inactive
+ * disabled     sendonly          sendrecv              recvonly
+ * disabled     sendonly          sendonly              inactive
+ * disabled     sendonly          recvonly              recvonly
+ * disabled     sendonly          inactive              inactive
+ * disabled     recvonly          sendrecv              sendonly
+ * disabled     recvonly          sendonly              sendonly
+ * disabled     recvonly          recvonly              inactive
+ * disabled     recvonly          inactive              inactive
+ *
+ * Parameters:
+ *
+ * dcb_p - pointer to the fsmdef_dcb_t.
+ * media - pointer to the fsmdef_media_t for the current media entry.
+ * local_hold - Boolean indicating if local hold feature is enabled
+ */
+static sdp_direction_e
+gsmsdp_negotiate_local_sdp_direction (fsmdef_dcb_t *dcb_p,
+                                      fsmdef_media_t *media,
+                                      boolean local_hold)
+{
+    sdp_direction_e direction = SDP_DIRECTION_SENDRECV;
+    sdp_direction_e remote_direction = gsmsdp_get_remote_sdp_direction(dcb_p,
+                                           media->level, &media->dest_addr);
+
+    if (remote_direction == SDP_DIRECTION_SENDRECV) {
+        if (local_hold) {
+            if ((media->support_direction == SDP_DIRECTION_SENDRECV) ||
+                (media->support_direction == SDP_DIRECTION_SENDONLY)) {
+                direction = SDP_DIRECTION_SENDONLY;
+            } else {
+                direction = SDP_DIRECTION_INACTIVE;
+            }
+        } else {
+            direction = media->support_direction;
+        }
+    } else if (remote_direction == SDP_DIRECTION_SENDONLY) {
+        if (local_hold) {
+            direction = SDP_DIRECTION_INACTIVE;
+        } else {
+            if ((media->support_direction == SDP_DIRECTION_SENDRECV) ||
+                (media->support_direction == SDP_DIRECTION_RECVONLY)) {
+                direction = SDP_DIRECTION_RECVONLY;
+            } else {
+                direction = SDP_DIRECTION_INACTIVE;
+            }
+        }
+    } else if (remote_direction == SDP_DIRECTION_INACTIVE) {
+        direction = SDP_DIRECTION_INACTIVE;
+    } else if (remote_direction == SDP_DIRECTION_RECVONLY) {
+        if ((media->support_direction == SDP_DIRECTION_SENDRECV) ||
+            (media->support_direction == SDP_DIRECTION_SENDONLY)) {
+            direction = SDP_DIRECTION_SENDONLY;
+        } else {
+            direction = SDP_DIRECTION_INACTIVE;
+        }
+    }
+
+    return direction;
+}
+
+/*
+ * gsmsdp_add_default_audio_formats_to_local_sdp
+ *
+ * Description:
+ *
+ * Add all supported media formats to the local SDP of the specified DCB
+ * at the specified media level. If the call is involved in a conference
+ * call, only add G.711 formats.
+ *
+ * Parameters
+ *
+ * dcb_p - The DCB whose local SDP is to be updated with the default media formats.
+ * sdp_p - Pointer to the local sdp structure. This is added so call to this
+ *         routine can be made irrespective of whether we have a dcb or not(To
+ *         handle out-of-call options request for example)
+ * media - Pointer to fsmdef_media_t for the media entry of the SDP.
+ *
+ */
+static void
+gsmsdp_add_default_audio_formats_to_local_sdp (fsmdef_dcb_t *dcb_p,
+                                               cc_sdp_t * sdp_p,
+                                               fsmdef_media_t *media)
+{
+    static const char fname[] = "gsmsdp_add_default_audio_formats_to_local_sdp";
+    int             local_media_types[CC_MAX_MEDIA_TYPES];
+    int16_t         local_avt_payload_type = RTP_NONE;
+    DtmfOutOfBandTransport_t transport = DTMF_OUTOFBAND_NONE;
+    int             type_cnt;
+    void           *local_sdp_p = NULL;
+    uint16_t        media_format_count;
+    uint16_t        level;
+    int i;
+
+    if (media) {
+        level = media->level;
+    } else {
+        level = 1;
+    }
+    local_sdp_p = (void *) sdp_p->src_sdp;
+
+    /*
+     * Create list of supported codecs. Get all the codecs that the phone
+     * supports.
+     */
+    media_format_count = sip_config_local_supported_codecs_get(
+                                (rtp_ptype *) local_media_types,
+                                CC_MAX_MEDIA_TYPES);
+    /*
+     * If there are no media payloads, it's because we are making an
+     * initial offer. We will be opening our receive port so we need to specify
+     * the media payload type to be used initially. We set the media payload
+     * type in the dcb to do this. Until we receive an answer from the far
+     * end, we will use our first choice payload type. i.e. the first payload
+     * type sent in our AUDIO media line.
+     */
+
+    if (dcb_p && media && media->num_payloads == 0) {
+
+        if (media->payloads &&
+            (media->num_payloads < media_format_count)) {
+            cpr_free(media->payloads);
+            media->payloads = NULL;
+        }
+
+        if (!media->payloads) {
+            media->payloads = cpr_calloc(media_format_count,
+                                         sizeof(vcm_payload_info_t));
+        }
+
+        media->num_payloads = 0;
+        for (i = 0; i < media_format_count; i++) {
+            if (local_media_types[i] > RTP_NONE) {
+                media->payloads[i].codec_type = local_media_types[i];
+                media->payloads[i].local_rtp_pt = local_media_types[i];
+                media->payloads[i].remote_rtp_pt = local_media_types[i];
+                media->num_payloads++;
+            }
+        }
+        gsmsdp_copy_payloads_to_previous_sdp(media);
+    }
+
+    /*
+     * Get configured OOB DTMF setting and avt payload type if applicable
+     */
+    config_get_value(CFGID_DTMF_OUTOFBAND, &transport, sizeof(transport));
+
+    if ((transport == DTMF_OUTOFBAND_AVT) ||
+        (transport == DTMF_OUTOFBAND_AVT_ALWAYS)) {
+        int temp_payload_type = RTP_NONE;
+
+        config_get_value(CFGID_DTMF_AVT_PAYLOAD,
+                         &(temp_payload_type),
+                         sizeof(temp_payload_type));
+        local_avt_payload_type = (uint16_t) temp_payload_type;
+    }
+
+    /*
+     * add all the audio media types
+     */
+    for (type_cnt = 0;
+         (type_cnt < media_format_count) &&
+         (local_media_types[type_cnt] > RTP_NONE);
+         type_cnt++) {
+
+        if (sdp_add_media_payload_type(local_sdp_p, level,
+                                       (uint16_t)local_media_types[type_cnt],
+                                       SDP_PAYLOAD_NUMERIC) != SDP_SUCCESS) {
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"Adding media payload type failed\n",
+                        DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+        }
+
+        if (media->support_direction != SDP_DIRECTION_INACTIVE) {
+            gsmsdp_set_media_attributes(local_media_types[type_cnt], local_sdp_p,
+                                    level, (uint16_t)local_media_types[type_cnt]);
+        }
+    }
+
+    /*
+     * add the avt media type
+     */
+    if (local_avt_payload_type > RTP_NONE) {
+        if (sdp_add_media_payload_type(local_sdp_p, level,
+                                       local_avt_payload_type,
+                                       SDP_PAYLOAD_NUMERIC) != SDP_SUCCESS) {
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"Adding AVT payload type failed\n",
+                        dcb_p->line, dcb_p->call_id, fname);
+        }
+
+        if (media->support_direction != SDP_DIRECTION_INACTIVE) {
+            gsmsdp_set_media_attributes(RTP_AVT, local_sdp_p, level,
+                                        local_avt_payload_type);
+            if (media) {
+                media->avt_payload_type = local_avt_payload_type;
+            }
+        }
+    }
+}
+
+/*
+ * gsmsdp_add_default_video_formats_to_local_sdp
+ *
+ * Description:
+ *
+ * Add all supported media formats to the local SDP of the specified DCB
+ * at the specified media level. If the call is involved in a conference
+ * call, only add G.711 formats.
+ *
+ * Parameters
+ *
+ * dcb_p - The DCB whose local SDP is to be updated with the default media formats.
+ * sdp_p - Pointer to the local sdp structure. This is added so call to this
+ *         routine can be made irrespective of whether we have a dcb or not(To
+ *         handle out-of-call options request for example)
+ * media - Pointer to fsmdef_media_t for the media entry of the SDP.
+ *
+ */
+static void
+gsmsdp_add_default_video_formats_to_local_sdp (fsmdef_dcb_t *dcb_p,
+                                               cc_sdp_t * sdp_p,
+                                               fsmdef_media_t *media)
+{
+    static const char fname[] = "gsmsdp_add_default_video_formats_to_local_sdp";
+    int             video_media_types[CC_MAX_MEDIA_TYPES];
+    int             type_cnt;
+    void           *local_sdp_p = NULL;
+    uint16_t        video_format_count;
+    uint16_t        level;
+    line_t          line = 0;
+    callid_t        call_id = 0;
+    int             i;
+
+    if (dcb_p && media) {
+        line = dcb_p->line;
+        call_id = dcb_p->call_id;
+    }
+    GSM_DEBUG(DEB_L_C_F_PREFIX"\n", DEB_L_C_F_PREFIX_ARGS(GSM, line, call_id, fname));
+
+    if (media) {
+        level = media->level;
+    } else {
+        level = 2;
+    }
+    local_sdp_p = (void *) sdp_p->src_sdp;
+
+    /*
+     * Create list of supported codecs. Get all the codecs that the phone supports.
+     */
+
+    video_format_count = sip_config_video_supported_codecs_get( (rtp_ptype *) video_media_types,
+                                                 CC_MAX_MEDIA_TYPES, TRUE /*offer*/);
+
+    GSM_DEBUG(DEB_L_C_F_PREFIX"video_count=%d\n", DEB_L_C_F_PREFIX_ARGS(GSM, line, call_id, fname), video_format_count);
+    /*
+     * If the there are no media payloads, its because we are making an
+     * initial offer. We will be opening our receive port so we need to specify
+     * the media payload type to be used initially. We set the media payload
+     * type in the dcb to do this. Until we receive an answer from the far
+     * end, we will use our first choice payload type. i.e. the first payload
+     * type sent in our video media line.
+     */
+    if (dcb_p && media && media->num_payloads == 0) {
+        if (media->payloads &&
+            (media->num_payloads < video_format_count)) {
+            cpr_free(media->payloads);
+            media->payloads = NULL;
+        }
+
+        if (!media->payloads) {
+            media->payloads = cpr_calloc(video_format_count,
+                                         sizeof(vcm_payload_info_t));
+        }
+
+        media->num_payloads = 0;
+        for (i = 0; i < video_format_count; i++) {
+            if (video_media_types[i] > RTP_NONE) {
+                media->payloads[i].codec_type = video_media_types[i];
+                media->payloads[i].local_rtp_pt = video_media_types[i];
+                media->payloads[i].remote_rtp_pt = video_media_types[i];
+                media->num_payloads++;
+            }
+        }
+        gsmsdp_copy_payloads_to_previous_sdp(media);
+    }
+
+
+    /*
+     * add all the video media types
+     */
+    for (type_cnt = 0;
+         (type_cnt < video_format_count) &&
+         (video_media_types[type_cnt] > RTP_NONE);
+         type_cnt++) {
+
+        if (sdp_add_media_payload_type(local_sdp_p, level,
+                                       (uint16_t)video_media_types[type_cnt],
+                                       SDP_PAYLOAD_NUMERIC) != SDP_SUCCESS) {
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"SDP ERROR(1)\n",
+                        line, call_id, fname);
+        }
+
+        if (media->support_direction != SDP_DIRECTION_INACTIVE) {
+            gsmsdp_set_video_media_attributes(video_media_types[type_cnt], sdp_p,
+                               level, (uint16_t)video_media_types[type_cnt]);
+        }
+    }
+}
+
+/**
+ * This function sets the mid attr at the media level
+ * and labels the relevant media streams when phone is
+ * operating in a dual stack mode
+ *
+ * @param[in]src_sdp_p  - Our source sdp.
+ * @param[in]level      - The level of the media that we are operating on.
+ *
+ * @return           - None
+ *
+ */
+static void gsmsdp_set_mid_attr (void *src_sdp_p, uint16_t level)
+{
+    uint16         inst_num;
+
+    if (platform_get_ip_address_mode() == CPR_IP_MODE_DUAL) {
+        /*
+         * add a=mid line
+         */
+        (void) sdp_add_new_attr(src_sdp_p, level, 0, SDP_ATTR_MID, &inst_num);
+
+        (void) sdp_attr_set_simple_u32(src_sdp_p, SDP_ATTR_MID, level, 0,
+                                       inst_num, level);
+    }
+}
+
+/**
+ * This function sets the anat attr to the session level
+ * and labels the relevant media streams
+ *
+ * @param[in]media   - The media line that we are operating on.
+ * @param[in]dcb_p   - Pointer to the DCB whose local SDP is to be updated.
+ *
+ * @return           - None
+ *
+ */
+static void gsmsdp_set_anat_attr (fsmdef_dcb_t *dcb_p, fsmdef_media_t *media)
+{
+    void           *src_sdp_p = (void *) dcb_p->sdp->src_sdp;
+    void           *dest_sdp_p = (void *) dcb_p->sdp->dest_sdp;
+    uint16         inst_num;
+    uint16_t       num_group_lines= 0;
+    uint16_t       num_anat_lines = 0;
+    u32            group_id_1, group_id_2;
+    uint16_t       i;
+    fsmdef_media_t *group_media;
+
+
+    if (dest_sdp_p == NULL) {
+        /* If this is our initial offer */
+        if (media->addr_type == SDP_AT_IP4) {
+            group_media = gsmsdp_find_anat_pair(dcb_p, media);
+            if (group_media != NULL) {
+                /*
+                 * add a=group line
+                 */
+                 (void) sdp_add_new_attr(src_sdp_p, SDP_SESSION_LEVEL, 0, SDP_ATTR_GROUP, &inst_num);
+
+                 (void) sdp_set_group_attr(src_sdp_p, SDP_SESSION_LEVEL, 0, inst_num, SDP_GROUP_ATTR_ANAT);
+
+                 (void) sdp_set_group_num_id(src_sdp_p, SDP_SESSION_LEVEL, 0, inst_num, 2);
+                 (void) sdp_set_group_id(src_sdp_p, SDP_SESSION_LEVEL, 0, inst_num, group_media->level);
+                 (void) sdp_set_group_id(src_sdp_p, SDP_SESSION_LEVEL, 0, inst_num, media->level);
+            }
+        }
+    } else {
+        /* This is an answer, check if the offer rcvd had anat grouping */
+        (void) sdp_attr_num_instances(dest_sdp_p, SDP_SESSION_LEVEL, 0, SDP_ATTR_GROUP,
+                                  &num_group_lines);
+
+        for (i = 1; i <= num_group_lines; i++) {
+             if (sdp_get_group_attr(dest_sdp_p, SDP_SESSION_LEVEL, 0, i) == SDP_GROUP_ATTR_ANAT) {
+                 num_anat_lines++;
+             }
+        }
+
+        for (i = 1; i <= num_anat_lines; i++) {
+             group_id_1 = sdp_get_group_id(dest_sdp_p, SDP_SESSION_LEVEL, 0, i, 1);
+             group_id_2 = sdp_get_group_id(dest_sdp_p, SDP_SESSION_LEVEL, 0, i, 2);
+
+             if ((media->level == group_id_1)  || (media->level == group_id_2)) {
+
+                 group_media = gsmsdp_find_anat_pair(dcb_p, media);
+                 if (group_media != NULL) {
+                     if (sdp_get_group_attr(src_sdp_p, SDP_SESSION_LEVEL, 0, i) != SDP_GROUP_ATTR_ANAT) {
+                         /*
+                          * add a=group line
+                          */
+                         (void) sdp_add_new_attr(src_sdp_p, SDP_SESSION_LEVEL, 0, SDP_ATTR_GROUP, &inst_num);
+                         (void) sdp_set_group_attr(src_sdp_p, SDP_SESSION_LEVEL, 0, inst_num, SDP_GROUP_ATTR_ANAT);
+
+                     }
+                     (void) sdp_set_group_num_id(src_sdp_p, SDP_SESSION_LEVEL, 0, i, 2);
+                     (void) sdp_set_group_id(src_sdp_p, SDP_SESSION_LEVEL, 0, i, group_media->level);
+                     (void) sdp_set_group_id(src_sdp_p, SDP_SESSION_LEVEL, 0, i, media->level);
+
+                 } else {
+                     /*
+                      * add a=group line
+                      */
+                     (void) sdp_add_new_attr(src_sdp_p, SDP_SESSION_LEVEL, 0, SDP_ATTR_GROUP, &inst_num);
+                     (void) sdp_set_group_attr(src_sdp_p, SDP_SESSION_LEVEL, 0, inst_num, SDP_GROUP_ATTR_ANAT);
+
+                     (void) sdp_set_group_num_id(src_sdp_p, SDP_SESSION_LEVEL, 0, inst_num, 1);
+                     (void) sdp_set_group_id(src_sdp_p, SDP_SESSION_LEVEL, 0, inst_num, media->level);
+                 }
+
+             }
+        }
+    }
+    gsmsdp_set_mid_attr (src_sdp_p, media->level);
+}
+
+/*
+ * gsmsdp_update_local_sdp_media
+ *
+ * Description:
+ *
+ * Adds an AUDIO media line to the local SDP of the specified DCB. If all_formats
+ * is TRUE, sets all media formats supported by the phone into the local SDP, else
+ * only add the single negotiated media format. If an AUDIO media line already
+ * exists in the local SDP, remove it as this function completely rebuilds the
+ * AUDIO media line and will do so at the same media level as the pre-existing
+ * AUDIO media line.
+ *
+ * Parameters:
+ *
+ * dcb_p - Pointer to the DCB whose local SDP is to be updated.
+ * cc_sdp_p - Pointer to the SDP being updated.
+ * all_formats - If true, all supported media formats will be added to the
+ *               AUDIO media line of the SDP. Otherwise, only the single
+ *               negotiated media format is added.
+ * media     - Pointer to fsmdef_media_t for the media entry of the SDP.
+ * transport - transport type to for this media line.
+ *
+ */
+static void
+gsmsdp_update_local_sdp_media (fsmdef_dcb_t *dcb_p, cc_sdp_t *cc_sdp_p,
+                              boolean all_formats, fsmdef_media_t *media,
+                              sdp_transport_e transport)
+{
+    static const char fname[] = "gsmsdp_update_local_sdp_media";
+    uint16_t        port;
+    sdp_result_e    result;
+    uint16_t        level;
+    void           *sdp_p;
+    int             sdpmode = 0;
+    int             i = 0;
+
+    if (!dcb_p || !media)  {
+        GSM_ERR_MSG(get_debug_string(FSMDEF_DBG_INVALID_DCB), fname);
+        return;
+    }
+    level = media->level;
+    port  = media->src_port;
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    sdp_p = cc_sdp_p ? (void *) cc_sdp_p->src_sdp : NULL;
+
+    if (sdp_p == NULL) {
+
+        gsmsdp_init_local_sdp(dcb_p->peerconnection, &(dcb_p->sdp));
+
+        cc_sdp_p = dcb_p->sdp;
+        if ((cc_sdp_p == NULL) || (cc_sdp_p->src_sdp == NULL)) {
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"sdp is NULL and init failed \n",
+                    dcb_p->line, dcb_p->call_id, fname);
+            return;
+        }
+        sdp_p = (void *) cc_sdp_p->src_sdp;
+    } else {
+
+    /*
+     * Remove the audio stream. Reset direction_set flag since
+     * all media attributes have just been removed.
+     */
+    sdp_delete_media_line(sdp_p, level);
+    media->direction_set = FALSE;
+    }
+
+    result = sdp_insert_media_line(sdp_p, level);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"Inserting media line to Sdp failed\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+        return;
+    }
+
+    if (media->support_direction != SDP_DIRECTION_INACTIVE) {
+        gsmsdp_set_connection_address(sdp_p, media->level, dcb_p->ice_default_candidate_addr);
+    }
+
+    (void) sdp_set_media_type(sdp_p, level, media->type);
+
+
+    (void) sdp_set_media_portnum(sdp_p, level, port, media->sctp_port);
+
+    /* Set media transport and crypto attributes if it is for SRTP */
+    gsmsdp_update_local_sdp_media_transport(dcb_p, sdp_p, media, transport,
+                                            all_formats);
+
+    if (all_formats) {
+        /*
+         * Add all supported media formats to the local sdp.
+         */
+        switch (media->type) {
+        case SDP_MEDIA_AUDIO:
+            gsmsdp_add_default_audio_formats_to_local_sdp(dcb_p, cc_sdp_p,
+                                                          media);
+            break;
+        case SDP_MEDIA_VIDEO:
+            gsmsdp_add_default_video_formats_to_local_sdp(dcb_p, cc_sdp_p,
+                                                          media);
+            break;
+        case SDP_MEDIA_APPLICATION:
+            gsmsdp_set_sctp_attributes (sdp_p, level, media);
+            break;
+        default:
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"SDP ERROR media %d for level %d is not"
+                        " supported\n",
+                        dcb_p->line, dcb_p->call_id, fname, media->level);
+            break;
+        }
+    } else {
+        /*
+         * Add negotiated codec list to the sdp
+         */
+        for(i = 0; i < media->num_payloads; i++) {
+          result =
+            sdp_add_media_payload_type(sdp_p, level,
+                (uint16_t)(media->payloads[i].local_rtp_pt),
+                SDP_PAYLOAD_NUMERIC);
+
+          if (result != SDP_SUCCESS) {
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"Adding dynamic payload type failed\n",
+                        dcb_p->line, dcb_p->call_id, fname);
+          }
+
+          switch (media->type) {
+            case SDP_MEDIA_AUDIO:
+              gsmsdp_set_media_attributes(media->payloads[i].codec_type,
+                  sdp_p, level,
+                  (uint16_t)(media->payloads[i].local_rtp_pt));
+              break;
+            case SDP_MEDIA_VIDEO:
+              gsmsdp_set_video_media_attributes(media->payloads[i].codec_type,
+                  cc_sdp_p, level,
+                  (uint16_t)(media->payloads[i].local_rtp_pt));
+              break;
+            case SDP_MEDIA_APPLICATION:
+              gsmsdp_set_sctp_attributes (sdp_p, level, media);
+              break;
+            default:
+              GSM_ERR_MSG(GSM_L_C_F_PREFIX"SDP ERROR media %d for level %d is"
+                        " not supported\n",
+                        dcb_p->line, dcb_p->call_id, fname, media->level);
+              break;
+          }
+
+        }//end for
+
+        /*
+         * add the avt media type
+         */
+        if (media->avt_payload_type > RTP_NONE) {
+            result = sdp_add_media_payload_type(sdp_p, level,
+                         (uint16_t)media->avt_payload_type,
+                         SDP_PAYLOAD_NUMERIC);
+            if (result != SDP_SUCCESS) {
+                GSM_ERR_MSG(GSM_L_C_F_PREFIX"Adding AVT payload type failed\n",
+                            dcb_p->line, dcb_p->call_id, fname);
+            }
+            gsmsdp_set_media_attributes(RTP_AVT, sdp_p, level,
+                (uint16_t) media->avt_payload_type);
+        }
+    }
+
+    if (!sdpmode)
+        gsmsdp_set_anat_attr(dcb_p, media);
+}
+
+/*
+ * gsmsdp_update_local_sdp
+ *
+ * Description:
+ *
+ * Updates the local SDP of the DCB based on the remote SDP.
+ *
+ * Parameters:
+ *
+ * dcb_p - Pointer to the DCB whose local SDP is to be updated.
+ * offer - Indicates whether the remote SDP was received in an offer
+ *               or an answer.
+ * initial_offer - this media line is initial offer.
+ * media - Pointer to fsmdef_media_t for the media entry of the SDP.
+ *
+ * Return:
+ *    TRUE  - update the local SDP was successfull.
+ *    FALSE - update the local SDP failed.
+ */
+static boolean
+gsmsdp_update_local_sdp (fsmdef_dcb_t *dcb_p, boolean offer,
+                         boolean initial_offer,
+                         fsmdef_media_t *media)
+{
+    static const char fname[] = "gsmsdp_update_local_sdp";
+    cc_action_data_t data;
+    sdp_direction_e direction;
+    boolean         local_hold = (boolean)FSM_CHK_FLAGS(media->hold, FSM_HOLD_LCL);
+
+    if (media->src_port == 0) {
+        GSM_DEBUG(DEB_L_C_F_PREFIX"allocate receive port for media line\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+        /*
+         * Source port has not been allocated, this could mean we
+         * processing an initial offer SDP, SDP that requets to insert
+         * a media line or re-insert a media line.
+         */
+        data.open_rcv.is_multicast = FALSE;
+        data.open_rcv.listen_ip = ip_addr_invalid;
+        data.open_rcv.port = 0;
+        data.open_rcv.keep = FALSE;
+        /*
+         * Indicate type of media (audio/video etc) becase some for supporting
+         * video over vieo, the port is obtained from other entity.
+         */
+        data.open_rcv.media_type = media->type;
+        data.open_rcv.media_refid = media->refid;
+        if (cc_call_action(dcb_p->call_id, dcb_p->line, CC_ACTION_OPEN_RCV,
+                           &data) == CC_RC_SUCCESS) {
+            /* allocate port successful, save the port */
+            media->src_port = data.open_rcv.port;
+            media->rcv_chan = FALSE;  /* mark no RX chan yet */
+        } else {
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"allocate rx port failed\n",
+                        dcb_p->line, dcb_p->call_id, fname);
+            return (FALSE);
+        }
+    }
+
+    /*
+     * Negotiate direction based on remote SDP.
+     */
+    direction = gsmsdp_negotiate_local_sdp_direction(dcb_p, media, local_hold);
+
+    /*
+     * Update Transmit SRTP transmit key if this SRTP session.
+     */
+    if (media->transport == SDP_TRANSPORT_RTPSAVP) {
+        gsmsdp_update_crypto_transmit_key(dcb_p, media, offer,
+                                       initial_offer, direction);
+    }
+
+    if (offer == TRUE) {
+        gsmsdp_update_local_sdp_media(dcb_p, dcb_p->sdp, FALSE, media,
+                                      media->transport);
+    }
+
+    /*
+     * Set local sdp direction.
+     */
+    if (media->direction_set) {
+        if (media->direction != direction) {
+            gsmsdp_set_local_sdp_direction(dcb_p, media, direction);
+        }
+    } else {
+        gsmsdp_set_local_sdp_direction(dcb_p, media, direction);
+    }
+    return (TRUE);
+}
+
+/*
+ * gsmsdp_update_local_sdp_for_multicast
+ *
+ * Description:
+ *
+ * Updates the local SDP of the DCB based on the remote SDP for
+ * multicast. Populates the local sdp with the same addr:port as
+ * in the offer and the same direction as in the offer (as per
+ * rfc3264).
+ *
+ * Parameters:
+ *
+ * dcb_p         - Pointer to the DCB whose local SDP is to be updated.
+ * portnum       - Remote port.
+ * media         - Pointer to fsmdef_media_t for the media entry of the SDP.
+ * offer         - boolean indicating an offer SDP if true.
+ * initial_offer - boolean indicating an initial offer SDP if true.
+ *
+ */
+static boolean
+gsmsdp_update_local_sdp_for_multicast (fsmdef_dcb_t *dcb_p,
+                                      uint16_t portnum,
+                                      fsmdef_media_t *media,
+                                      boolean offer,
+                                      boolean initial_offer)
+{
+   static const char fname[] = "gsmsdp_update_local_sdp_for_multicast";
+    sdp_direction_e direction;
+    char            addr_str[MAX_IPADDR_STR_LEN];
+    uint16_t        level;
+    char            *p_addr_str;
+    char            *strtok_state;
+
+    level = media->level;
+
+    GSM_DEBUG(DEB_L_C_F_PREFIX"%d %d %d\n",
+                         DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname),
+                         portnum, level, initial_offer);
+
+    direction = gsmsdp_get_remote_sdp_direction(dcb_p, media->level,
+                                                &media->dest_addr);
+    GSM_DEBUG(DEB_L_C_F_PREFIX"sdp direction: %d\n",
+              DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), direction);
+    /*
+     * Update Transmit SRTP transmit key any way to clean up the
+     * tx condition that we may have offered prior.
+     */
+    gsmsdp_update_crypto_transmit_key(dcb_p, media, offer, initial_offer,
+                                      direction);
+
+    gsmsdp_update_local_sdp_media(dcb_p, dcb_p->sdp, FALSE,
+                                  media, media->transport);
+
+    /*
+     * Set local sdp direction same as on remote SDP for multicast
+     */
+    if ((direction == SDP_DIRECTION_RECVONLY) || (direction == SDP_DIRECTION_INACTIVE)) {
+        if ((media->support_direction == SDP_DIRECTION_SENDRECV) ||
+            (media->support_direction == SDP_DIRECTION_RECVONLY)) {
+            /*
+             * Echo same direction back in our local SDP but set the direction
+             * in DCB to recvonly so that LSM operations on rcv port work
+             * without modification.
+             */
+        } else {
+            direction = SDP_DIRECTION_INACTIVE;
+            GSM_DEBUG(DEB_L_C_F_PREFIX"media line"
+                      " does not support receive stream\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+        }
+        gsmsdp_set_local_sdp_direction(dcb_p, media, direction);
+        media->direction_set = TRUE;
+    } else {
+        /*
+         * return FALSE indicating error
+         */
+        return (FALSE);
+    }
+
+    /*
+     * Set the ip addr to the multicast ip addr.
+     */
+    ipaddr2dotted(addr_str, &media->dest_addr);
+    p_addr_str = PL_strtok_r(addr_str, "[ ]", &strtok_state);
+
+    /*
+     * Set the local SDP port number to match far ends port number.
+     */
+    (void) sdp_set_media_portnum(dcb_p->sdp->src_sdp, level, portnum, 0);
+
+    /*
+     * c= line <network type><address type><connection address>
+     */
+    (void) sdp_set_conn_nettype(dcb_p->sdp->src_sdp, level, SDP_NT_INTERNET);
+    (void) sdp_set_conn_addrtype(dcb_p->sdp->src_sdp, level, media->addr_type);
+    (void) sdp_set_conn_address(dcb_p->sdp->src_sdp, level, p_addr_str);
+
+    return (TRUE);
+}
+
+/*
+ * gsmsdp_get_remote_avt_payload_type
+ *
+ * Description:
+ *
+ * Returns the AVT payload type of the given audio line in the specified SDP.
+ *
+ * Parameters:
+ *
+ * level - The media level of the SDP where the media attribute is to be found.
+ * sdp_p - Pointer to the SDP whose AVT payload type is being searched.
+ *
+ */
+static int
+gsmsdp_get_remote_avt_payload_type (uint16_t level, void *sdp_p)
+{
+    uint16_t        i;
+    uint16_t        ptype;
+    int             remote_avt_payload_type = RTP_NONE;
+    uint16_t        num_a_lines = 0;
+    const char     *encname = NULL;
+
+    /*
+     * Get number of RTPMAP attributes for the media line
+     */
+    (void) sdp_attr_num_instances(sdp_p, level, 0, SDP_ATTR_RTPMAP,
+                                  &num_a_lines);
+
+    /*
+     * Loop through AUDIO media line RTPMAP attributes. The last
+     * NET dynamic payload type will be returned.
+     */
+    for (i = 0; i < num_a_lines; i++) {
+        ptype = sdp_attr_get_rtpmap_payload_type(sdp_p, level, 0,
+                                                 (uint16_t) (i + 1));
+        if (sdp_media_dynamic_payload_valid(sdp_p, ptype, level)) {
+            encname = sdp_attr_get_rtpmap_encname(sdp_p, level, 0,
+                                                  (uint16_t) (i + 1));
+            if (encname) {
+                if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_TEL_EVENT) == 0) {
+                    remote_avt_payload_type = ptype;
+                }
+            }
+        }
+    }
+    return (remote_avt_payload_type);
+}
+
+
+#define MIX_NEAREND_STRING  "X-mix-nearend"
+
+/*
+ *  gsmsdp_negotiate_codec
+ *
+ *  Description:
+ *
+ *  Negotiates an acceptable codec from the local and remote SDPs
+ *
+ *  Parameters:
+ *
+ *  dcb_p - Pointer to DCB whose codec is being negotiated
+ *  sdp_p - Pointer to local and remote SDP
+ *  media - Pointer to the fsmdef_media_t for a given media entry whose
+ *          codecs are being negotiated.
+ *  offer - Boolean indicating if the remote SDP came in an OFFER.
+ *
+ *  Returns:
+ *
+ *  codec  >  0: most preferred negotiated codec
+ *         <= 0: negotiation failed
+ */
+static int
+gsmsdp_negotiate_codec (fsmdef_dcb_t *dcb_p, cc_sdp_t *sdp_p,
+                        fsmdef_media_t *media, boolean offer,
+                        boolean initial_offer, uint16 media_level)
+{
+    static const char fname[] = "gsmsdp_negotiate_codec";
+    rtp_ptype       pref_codec = RTP_NONE;
+    uint16_t        i;
+    uint16_t        j;
+    int            *master_list_p = NULL;
+    int            *slave_list_p = NULL;
+    DtmfOutOfBandTransport_t transport = DTMF_OUTOFBAND_NONE;
+    int             avt_payload_type;
+    uint16_t        num_remote_types;
+    uint16_t        num_local_types;
+    uint16_t        num_master_types;
+    uint16_t        num_slave_types;
+    int             remote_codecs[CC_MAX_MEDIA_TYPES];
+    int             remote_payload_types[CC_MAX_MEDIA_TYPES];
+    int             local_codecs[CC_MAX_MEDIA_TYPES];
+    sdp_payload_ind_e pt_indicator;
+    uint32          ptime = 0;
+    uint32          maxptime = 0;
+    const char*     attr_label;
+    uint16_t        level;
+    boolean         explicit_reject = FALSE;
+    boolean         found_codec = FALSE;
+    int32_t         num_match_payloads = 0;
+    int             codec = RTP_NONE;
+    int             remote_pt = RTP_NONE;
+    int32_t         payload_types_count = 0; /* count for allocating right amout
+                                                of memory for media->payloads */
+    int             temp;
+    u16             a_inst;
+    vcm_payload_info_t *payload_info = NULL;
+    vcm_payload_info_t *previous_payload_info;
+
+    if (!dcb_p || !sdp_p || !media) {
+        return (RTP_NONE);
+    }
+
+    level = media_level;
+    attr_label = sdp_attr_get_simple_string(sdp_p->dest_sdp,
+                                            SDP_ATTR_LABEL, level, 0, 1);
+
+    if (attr_label != NULL) {
+        if (strcmp(attr_label, MIX_NEAREND_STRING) == 0) {
+            dcb_p->session = WHISPER_COACHING;
+        }
+    }
+    /*
+     * Obtain list of payload types from the remote SDP
+     */
+    num_remote_types = sdp_get_media_num_payload_types(sdp_p->dest_sdp, level);
+
+    if (num_remote_types > CC_MAX_MEDIA_TYPES) {
+        num_remote_types = CC_MAX_MEDIA_TYPES;
+    }
+
+    for (i = 0; i < num_remote_types; i++) {
+        temp = sdp_get_media_payload_type(sdp_p->dest_sdp, level,
+                                          (uint16_t) (i + 1), &pt_indicator);
+        remote_codecs[i] = GET_CODEC_TYPE(temp);
+        remote_payload_types[i] = GET_DYN_PAYLOAD_TYPE_VALUE(temp);
+    }
+
+    /*
+     * Get all the codecs that the phone supports.
+     */
+    if (media->type == SDP_MEDIA_AUDIO) {
+        num_local_types = sip_config_local_supported_codecs_get(
+                                    (rtp_ptype *)local_codecs,
+                                    CC_MAX_MEDIA_TYPES);
+    } else if (media->type == SDP_MEDIA_VIDEO) {
+        num_local_types = sip_config_video_supported_codecs_get(
+            (rtp_ptype *)local_codecs, CC_MAX_MEDIA_TYPES, offer);
+    } else {
+        GSM_DEBUG(DEB_L_C_F_PREFIX"unsupported media type %d\n",
+            DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname),
+            media->type);
+        return (RTP_NONE);
+    }
+
+    /*
+     * Set the AVT payload type to whatever value the farend wants to use,
+     * but only if we have AVT turned on and the farend wants it
+     * or if we are configured to always send it
+     */
+    config_get_value(CFGID_DTMF_OUTOFBAND, &transport, sizeof(transport));
+
+    /*
+     * Save AVT payload type for use by gsmsdp_compare_to_previous_sdp
+     */
+    media->previous_sdp.avt_payload_type = media->avt_payload_type;
+
+    switch (transport) {
+        case DTMF_OUTOFBAND_AVT:
+            avt_payload_type = gsmsdp_get_remote_avt_payload_type(
+                                   media->level, sdp_p->dest_sdp);
+            if (avt_payload_type > RTP_NONE) {
+                media->avt_payload_type = avt_payload_type;
+            } else {
+                media->avt_payload_type = RTP_NONE;
+            }
+            break;
+
+        case DTMF_OUTOFBAND_AVT_ALWAYS:
+            avt_payload_type = gsmsdp_get_remote_avt_payload_type(
+                                   media->level, sdp_p->dest_sdp);
+            if (avt_payload_type > RTP_NONE) {
+                media->avt_payload_type = avt_payload_type;
+            } else {
+                /*
+                 * If we are AVT_ALWAYS and the remote end is not using AVT,
+                 * then send DTMF as out-of-band and use our configured
+                 * payload type.
+                 */
+                config_get_value(CFGID_DTMF_AVT_PAYLOAD,
+                                 &media->avt_payload_type,
+                                 sizeof(media->avt_payload_type));
+
+                GSM_DEBUG(DEB_L_C_F_PREFIX"AVT_ALWAYS forcing out-of-band DTMF,"
+                          " payload_type = %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line,
+                                                dcb_p->call_id, fname),
+                          media->avt_payload_type);
+            }
+            break;
+
+        case DTMF_OUTOFBAND_NONE:
+        default:
+            media->avt_payload_type = RTP_NONE;
+            break;
+    }
+
+    /*
+     * Find a matching codec in our local list with the remote list.
+     * The local list was created with our preferred codec first in the list,
+     * so this will ensure that we will match the preferred codec with the
+     * remote list first, before matching other codecs.
+     */
+    pref_codec = sip_config_preferred_codec();
+    if (pref_codec != RTP_NONE) {
+        /*
+         * If a preferred codec was configured and the platform
+         * currently can do this codec, then it would be the
+         * first element of the local_codecs because of the
+         * logic in sip_config_local_supported_codec_get().
+         */
+        if (local_codecs[0] != pref_codec) {
+            /*
+             * preferred codec is configured but it is not avaible
+             * currently, treat it as there is no codec available.
+             */
+            pref_codec = RTP_NONE;
+        }
+    }
+
+    if (pref_codec == RTP_NONE) {
+        master_list_p = remote_codecs;
+        slave_list_p = local_codecs;
+        num_master_types = num_remote_types;
+        num_slave_types = num_local_types;
+        GSM_DEBUG(DEB_L_C_F_PREFIX"Remote Codec list is Master\n",
+            DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+    } else {
+        master_list_p = local_codecs;
+        slave_list_p = remote_codecs;
+        num_master_types = num_local_types;
+        num_slave_types = num_remote_types;
+        GSM_DEBUG(DEB_L_C_F_PREFIX"Local Codec list is Master\n",
+           DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+    }
+
+    /*
+     * Save payload information for use by gsmspd_compare_to_previous_sdp
+     */
+    gsmsdp_copy_payloads_to_previous_sdp(media);
+
+    /*
+     * Setup payload info structure list to store matched payload details
+     * in the SDP.
+     */
+    media->num_payloads = 0;
+    if(num_master_types <= num_slave_types ) {
+      payload_types_count = num_master_types;
+    } else {
+      payload_types_count = num_slave_types;
+    }
+
+    /* Remove any previously allocated lists */
+    if (media->payloads) {
+        cpr_free(media->payloads);
+    }
+
+    /* Allocate memory for PT value, local PT and remote PT. */
+    media->payloads = cpr_calloc(payload_types_count,
+                                 sizeof(vcm_payload_info_t));
+
+    /* Store the ptime and maxptime parameter for this m= section */
+    if (media->type == SDP_MEDIA_AUDIO) {
+        ptime = sdp_attr_get_simple_u32(sdp_p->dest_sdp,
+                                    SDP_ATTR_PTIME, level, 0, 1);
+        if (ptime != 0) {
+            media->packetization_period = (uint16_t) ptime;
+        }
+        maxptime = sdp_attr_get_simple_u32(sdp_p->dest_sdp,
+                      SDP_ATTR_MAXPTIME, level, 0, 1);
+        if (maxptime != 0) {
+            media->max_packetization_period = (uint16_t) maxptime;
+        }
+    }
+
+    for (i = 0; i < num_master_types; i++) {
+        for (j = 0; j < num_slave_types; j++) {
+            if (master_list_p[i] == slave_list_p[j]) {
+
+                /* We've found a codec in common. Configure the coresponding
+                   payload information structure */
+                codec = slave_list_p[j];
+                payload_info = &(media->payloads[media->num_payloads]);
+
+                if (master_list_p == remote_payload_types) {
+                    remote_pt = remote_payload_types[i];
+                } else {
+                    remote_pt = remote_payload_types[j];
+                }
+
+                payload_info->codec_type = codec;
+                payload_info->local_rtp_pt = remote_pt;
+                payload_info->remote_rtp_pt = remote_pt;
+
+                /* If the negotiated payload type in an answer is different from
+                   what we sent in our offer, we set the local payload type to
+                   our default (since that's what we put in our offer) */
+                if (!offer) {
+                    previous_payload_info =
+                        gsmsdp_find_info_for_codec(codec,
+                           media->previous_sdp.payloads,
+                           media->previous_sdp.num_payloads, 0);
+                    if ((previous_payload_info == NULL) ||
+                        (previous_payload_info->local_rtp_pt
+                            != payload_info->local_rtp_pt)) {
+                        payload_info->local_rtp_pt = codec;
+                    }
+                }
+
+                if (media->type == SDP_MEDIA_AUDIO) {
+
+                    if (sdp_attr_rtpmap_payload_valid(sdp_p->dest_sdp, level, 0,
+                        &a_inst, remote_pt) ) {
+                        /* Set the number of channels -- if omitted,
+                           default is 1 */
+                        payload_info->audio.channels =
+                            sdp_attr_get_rtpmap_num_chan(sdp_p->dest_sdp,
+                                                         level, 0, a_inst);
+                        if (payload_info->audio.channels == 0) {
+                            payload_info->audio.channels = 1;
+                        }
+
+                        /* Set frequency = clock rate. This is generally
+                           correct.  Codecs can override this assumption on a
+                           case-by-case basis in the switch construct below. */
+                        payload_info->audio.frequency =
+                            sdp_attr_get_rtpmap_clockrate(sdp_p->dest_sdp,
+                                      level, 0, a_inst);
+                    } else {
+                        GSM_DEBUG(DEB_L_C_F_PREFIX"Could not find rtpmap "
+                            "entry for payload %d -- setting defaults\n",
+                            DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line,
+                            dcb_p->call_id, fname), codec);
+                        payload_info->audio.channels = 1;
+                        /* See http://www.iana.org/assignments/rtp-parameters/
+                           rtp-parameters.xml */
+                        switch (codec) {
+                            case STATIC_RTP_AVP_DVI4_16000_1:
+                                codec = RTP_DVI4;
+                                payload_info->audio.frequency = 16000;
+                                break;
+                            case STATIC_RTP_AVP_L16_44100_2:
+                                codec = RTP_L16;
+                                payload_info->audio.frequency = 44100;
+                                payload_info->audio.channels = 2;
+                                break;
+                            case STATIC_RTP_AVP_L16_44100_1:
+                                codec = RTP_L16;
+                                payload_info->audio.frequency = 44100;
+                                break;
+                            case STATIC_RTP_AVP_DVI4_11025_1:
+                                codec = RTP_DVI4;
+                                payload_info->audio.frequency = 11025;
+                                break;
+                            case STATIC_RTP_AVP_DVI4_22050_1:
+                                codec = RTP_DVI4;
+                                payload_info->audio.frequency = 22050;
+                                break;
+                            default:
+                                payload_info->audio.frequency = 8000;
+                        }
+                    }
+
+
+                    switch (codec) {
+                        case RTP_PCMA:
+                        case RTP_PCMU:
+                            /* 20 ms = 1/50th of a second */
+                            payload_info->audio.packet_size =
+                                payload_info->audio.frequency / 50;
+
+                            payload_info->audio.bitrate = 8 *
+                                payload_info->audio.frequency *
+                                payload_info->audio.channels;
+                            break;
+
+
+                        case RTP_OPUS:
+                            if (!sdp_attr_rtpmap_payload_valid(sdp_p->dest_sdp,
+                                  level, 0, &a_inst, remote_pt) ||
+                                (payload_info->audio.frequency
+                                  != RTPMAP_OPUS_CLOCKRATE) ||
+                                (payload_info->audio.channels != 2)) {
+
+                                /* Be conservative in what we accept: any
+                                   implementation that does not use a 48 kHz
+                                   clockrate or 2 channels is broken. */
+                                explicit_reject = TRUE;
+                                continue; // keep looking
+                            }
+
+                            /* ********************************************* */
+                            /* TODO !! FIXME !! XXX
+                             * We can't support two-channel Opus until we merge
+                             * in webrtc.org upstream, rev 3050 or later. See
+                         http://code.google.com/p/webrtc/issues/detail?id=1013
+                             * for details. We also need to have proper
+                             * handling of the sprop-stereo and stereo SDP
+                             * values before we use stereo encoding/decoding.
+                             * Details in Mozilla Bug 818618.
+                             */
+                            payload_info->audio.channels = 1;
+                            /* ********************************************* */
+
+                            /* Store fmtp options */
+                            sdp_attr_get_fmtp_max_average_bitrate (
+                                sdp_p->dest_sdp, level, 0, 1,
+                                &payload_info->opus.max_average_bitrate);
+
+                            payload_info->opus.maxcodedaudiobandwidth =
+                                sdp_attr_get_fmtp_maxcodedaudiobandwidth(
+                                    sdp_p->dest_sdp, level, 0, 1);
+
+                            sdp_attr_get_fmtp_usedtx (sdp_p->dest_sdp, level, 0,
+                                1, &payload_info->opus.usedtx);
+
+                            sdp_attr_get_fmtp_stereo (sdp_p->dest_sdp, level, 0,
+                                1, &payload_info->opus.stereo);
+
+                            sdp_attr_get_fmtp_useinbandfec (sdp_p->dest_sdp,
+                                level, 0, 1, &payload_info->opus.useinbandfec);
+
+                            sdp_attr_get_fmtp_cbr (sdp_p->dest_sdp, level, 0, 1,
+                                &payload_info->opus.cbr);
+
+                            /* Copied from media/webrtc/trunk/src/modules/
+                               audio_coding/main/source/acm_codec_database.cc */
+                            payload_info->audio.frequency = 32000;
+                            payload_info->audio.packet_size = 960;
+                            payload_info->audio.bitrate = 32000;
+                            break;
+
+                        case RTP_ISAC:
+                            /* TODO: Update these from proper SDP constructs */
+                            payload_info->audio.frequency = 16000;
+                            payload_info->audio.packet_size = 480;
+                            payload_info->audio.bitrate = 32000;
+                            break;
+
+                        case RTP_ILBC:
+                            payload_info->ilbc.mode =
+                              (uint16_t)sdp_attr_get_fmtp_mode_for_payload_type(
+                                  sdp_p->dest_sdp, level, 0, remote_pt);
+
+                            /* TODO -- These should be updated to reflect the
+                               actual frequency */
+                            if (payload_info->ilbc.mode == SIPSDP_ILBC_MODE20)
+                            {
+                                payload_info->audio.packet_size = 160;
+                                payload_info->audio.bitrate = 15200;
+                            }
+                            else /* mode = 30 */
+                            {
+                                payload_info->audio.packet_size = 240;
+                                payload_info->audio.bitrate = 13300;
+                            }
+                            break;
+
+                          default:
+                              GSM_DEBUG(DEB_L_C_F_PREFIX"codec=%d not setting "
+                                  "codec parameters (not implemented)\n",
+                                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line,
+                                  dcb_p->call_id, fname), codec);
+                            payload_info->audio.packet_size = -1;
+                            payload_info->audio.bitrate = -1;
+                        } /* end switch */
+
+
+                } else if (media->type == SDP_MEDIA_VIDEO) {
+                    if ( media-> video != NULL ) {
+                       vcmFreeMediaPtr(media->video);
+                       media->video = NULL;
+                    }
+
+                    if (!vcmCheckAttribs(codec, sdp_p, level,
+                                         &media->video)) {
+                          GSM_DEBUG(DEB_L_C_F_PREFIX"codec= %d ignored - "
+                               "attribs not accepted\n",
+                               DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line,
+                               dcb_p->call_id, fname), codec);
+                          explicit_reject = TRUE;
+                          continue; /* keep looking */
+                    }
+
+                    /* cache the negotiated profile_level and bandwidth */
+                    media->previous_sdp.tias_bw = media->tias_bw;
+                    media->tias_bw =  ccsdpGetBandwidthValue(sdp_p,level, 1);
+                    if ( (attr_label =
+                        ccsdpAttrGetFmtpProfileLevelId(sdp_p,level,0,1))
+                            != NULL ) {
+                        media->previous_sdp.profile_level =
+                            media->profile_level;
+                        sscanf(attr_label,"%x", &media->profile_level);
+                    }
+
+                    /* This should ultimately use RFC 6236 a=imageattr
+                       if present */
+                    switch (codec) {
+                        case RTP_VP8:
+                            payload_info->video.width = 640;
+                            payload_info->video.height = 480;
+                        break;
+                        case RTP_I420:
+                            payload_info->video.width = 176;
+                            payload_info->video.height = 144;
+                        break;
+                        default:
+                            GSM_DEBUG(DEB_L_C_F_PREFIX"codec=%d not setting "
+                                "codec parameters (not implemented)\n",
+                                DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line,
+                                dcb_p->call_id, fname), codec);
+                            payload_info->video.width = -1;
+                            payload_info->video.height = -1;
+                    }
+                } /* end video */
+
+                GSM_DEBUG(DEB_L_C_F_PREFIX"codec= %d\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line,
+                                            dcb_p->call_id, fname), codec);
+
+
+                found_codec = TRUE;
+                if(media->num_payloads >= payload_types_count) {
+                    /* We maxed our allocated memory -- processing is done. */
+                    return codec;
+                }
+
+                /* Incrementing this number serves as a "commit" for the
+                   payload_info. If we bail out of the loop before this
+                   happens, then the collected information is abandoned. */
+                media->num_payloads++;
+
+                if(offer) {
+                    /* If we are creating an answer, return after the first match.
+                       TODO -- Eventually, we'll (probably) want to answer with
+                       all the codecs we can receive. See bug 814227. */
+                    return codec;
+                }
+            }
+        }
+    }
+
+    /* Return the most preferred codec */
+    if(found_codec) {
+        return (media->payloads[0].codec_type);
+    }
+
+    /*
+     * CSCsv84705 - we could not negotiate a common codec because
+     * the local list is empty. This condition could happen when
+     * using g729 in locally mixed conference in which another call
+     * to vcm_get_codec_list() would return 0 or no codec.  So if
+     * this is a not an init offer, we should just go ahead and use
+     * the last negotiated codec if the remote list matches with
+     * currently used.
+     */
+    if (!initial_offer && !explicit_reject) {
+        for (i = 0; i < num_remote_types; i++) {
+            if (media->num_payloads != 0 && media->payloads[0].codec_type ==
+                remote_payload_types[i]) {
+                GSM_DEBUG(DEB_L_C_F_PREFIX"local codec list was empty codec= %d"
+                          " local=%d remote =%d\n", DEB_L_C_F_PREFIX_ARGS(GSM,
+                          dcb_p->line, dcb_p->call_id, fname),
+                          media->payloads[0].codec_type,
+                          media->payloads[0].local_rtp_pt,
+                          media->payloads[0].remote_rtp_pt);
+                return (media->payloads[0].codec_type);
+            }
+        }
+    }
+
+    return (RTP_NONE);
+}
+
+static void
+gsmsdp_negotiate_datachannel_attribs(fsmdef_dcb_t* dcb_p, cc_sdp_t* sdp_p, uint16_t level, fsmdef_media_t* media)
+{
+    uint32          num_streams;
+    char           *protocol;
+
+    sdp_attr_get_fmtp_streams (sdp_p->dest_sdp, level, 0, 1, &num_streams);
+
+    media->streams = num_streams;
+
+    if(media->protocol == NULL) {
+        media->protocol = cpr_malloc(SDP_MAX_STRING_LEN+1);
+        if (media->protocol == NULL)
+               return;
+    }
+    sdp_attr_get_fmtp_data_channel_protocol(sdp_p->dest_sdp, level, 0, 1, media->protocol);
+
+    media->sctp_port = sdp_attr_get_fmtp_payload_type (sdp_p->dest_sdp, level, 0, 1);
+
+    /* Increment port for answer SDP */
+    media->sctp_port++;
+}
+
+/*
+ * gsmsdp_add_unsupported_stream_to_local_sdp
+ *
+ * Description:
+ *
+ * Adds a rejected media line to the local SDP. If there is already a media line at
+ * the specified level, check to see if it matches the corresponding media line in the
+ * remote SDP. If it does not, remove the media line from the local SDP so that
+ * the corresponding remote SDP media line can be added. Note that port will be set
+ * to zero indicating the media line is rejected.
+ *
+ * Parameters:
+ *
+ * scp_p - Pointer to the local and remote SDP.
+ * level - The media line level being rejected.
+ */
+static void
+gsmsdp_add_unsupported_stream_to_local_sdp (cc_sdp_t *sdp_p,
+                                            uint16_t level)
+{
+    static const char fname[] = "gsmsdp_add_unsupported_stream_to_local_sdp";
+    uint32_t          remote_pt;
+    sdp_payload_ind_e remote_pt_indicator;
+    cpr_ip_addr_t     addr;
+
+    if (sdp_p == NULL) {
+        GSM_ERR_MSG(GSM_F_PREFIX"sdp is null.\n", fname);
+        return;
+    }
+
+    if (sdp_get_media_type(sdp_p->src_sdp, level) != SDP_MEDIA_INVALID) {
+        sdp_delete_media_line(sdp_p->src_sdp, level);
+    }
+
+    if (sdp_p->dest_sdp == NULL) {
+        GSM_ERR_MSG(GSM_F_PREFIX"no remote SDP available\n", fname);
+        return;
+    }
+
+    /*
+     * Insert media line at the specified level.
+     */
+    if (sdp_insert_media_line(sdp_p->src_sdp, level) != SDP_SUCCESS) {
+        GSM_ERR_MSG(GSM_F_PREFIX"failed to insert a media line\n", fname);
+        return;
+    }
+
+    /*
+     * Set the attributes of the media line. Specify port = 0 to
+     * indicate media line is rejected.
+     */
+    (void) sdp_set_media_type(sdp_p->src_sdp, level,
+                              sdp_get_media_type(sdp_p->dest_sdp, level));
+    (void) sdp_set_media_portnum(sdp_p->src_sdp, level, 0, 0);
+    (void) sdp_set_media_transport(sdp_p->src_sdp, level,
+                    sdp_get_media_transport(sdp_p->dest_sdp, level));
+
+    remote_pt = sdp_get_media_payload_type(sdp_p->dest_sdp, level, 1,
+                                           &remote_pt_indicator);
+    /*
+     * Don't like having to cast the payload type but sdp_get_media_payload_type
+     * returns a uint32_t but sdp_add_media_payload_type takes a uint16_t payload type.
+     * This needs to be fixed in Rootbeer.
+     */
+    (void) sdp_add_media_payload_type(sdp_p->src_sdp, level,
+                                      (uint16_t) remote_pt,
+                                      remote_pt_indicator);
+    /*
+     * The rejected media line needs to have "c=" line since
+     * we currently do not include the "c=" at the session level.
+     * The sdp parser in other end point such as the SDP parser
+     * in the CUCM and in the phone ensures that there
+     * is at least one "c=" line that can be used with each media
+     * line. Such the parser will flag unsupported media line without
+     * "c=" in that media line and at the session as error.
+     *
+     * The solution to have "c=" at the session level and
+     * omitting "c=" at the media level all together can also
+     * resolve this problem. Since the phone is also supporting
+     * ANAT group for IPV4/IPV6 offering therefore selecting
+     * session level and determining not to include "c=" line
+     * at the media level can become complex. For this reason, the
+     * unsupported media line will have "c=" with 0.0.0.0 address instead.
+     */
+    gsmsdp_set_connection_address(sdp_p->src_sdp, level, "0.0.0.0");
+}
+
+/*
+ * gsmsdp_get_remote_media_address
+ *
+ * Description:
+ *
+ * Extract the remote address from the given sdp.
+ *
+ * Parameters:
+ *
+ * fcb_p - Pointer to the FCB containing thhe DCB whose media lines are
+ *         being negotiated
+ * sdp_p - Pointer to the the remote SDP
+ * level - media line level.
+ * dest_addr - pointer to the cpr_ip_addr_t structure to return
+ *             remote address.
+ *
+ *  Returns:
+ *  FALSE - fails.
+ *  TRUE  - success.
+ *
+ */
+static boolean
+gsmsdp_get_remote_media_address (fsmdef_dcb_t *dcb_p,
+                                 cc_sdp_t * sdp_p, uint16_t level,
+                                 cpr_ip_addr_t *dest_addr)
+{
+   const char fname[] = "gsmsdp_get_remote_media_address";
+    const char     *addr_str = NULL;
+    int             dns_err_code;
+    boolean         stat;
+
+    *dest_addr = ip_addr_invalid;
+
+    stat = sdp_connection_valid(sdp_p->dest_sdp, level);
+    if (stat) {
+        addr_str = sdp_get_conn_address(sdp_p->dest_sdp, level);
+    } else {
+        /* Address not at the media level. Try the session level. */
+        stat = sdp_connection_valid(sdp_p->dest_sdp, SDP_SESSION_LEVEL);
+        if (stat) {
+            addr_str = sdp_get_conn_address(sdp_p->dest_sdp, SDP_SESSION_LEVEL);
+        }
+    }
+
+    if (stat && addr_str) {
+        /* Assume that this is dotted address */
+        if (str2ip(addr_str, dest_addr) != 0) {
+            /* It could be Fully Qualify DN, need to add DNS look up here */
+            dns_err_code = dnsGetHostByName(addr_str, dest_addr, 100, 1);
+            if (dns_err_code) {
+                *dest_addr = ip_addr_invalid;
+                stat = FALSE;
+                GSM_ERR_MSG(GSM_L_C_F_PREFIX"DNS remote address error %d"
+                            " with media at %d\n", dcb_p->line, dcb_p->call_id,
+                            fname, dns_err_code, level);
+            }
+        }
+    } else {
+        /*
+         * No address the media level or the session level.
+         */
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"No remote address from SDP with at %d\n",
+                    dcb_p->line, dcb_p->call_id, fname, level);
+    }
+    /*
+     * Convert the remote address to host address to be used. It was
+     * found out that without doing so, the softphone can crash when
+     * in attempt to setup remote address to transmit API of DSP
+     * implementation on win32.
+     */
+    util_ntohl(dest_addr, dest_addr);
+    return (stat);
+}
+
+/* Function     gsmsdp_is_multicast_address
+ *
+ * Inputs:      - IP Address
+ *
+ * Returns:     YES if is multicast address, no if otherwise
+ *
+ * Purpose:     This is a utility function that tests to see if the passed
+ *      address is a multicast address.  It does so by verifying that the
+ *      address is between 225.0.0.0 and 239.255.255.255.
+ *
+ * Note:        Addresses passed are not in network byte order.
+ *
+ * Note2:       Addresses between 224.0.0.0 and 224.255.255.225 are also multicast
+ *      addresses, but 224.0.0.0 to 224.0.0.255 are reserved and it is recommended
+ *      to start at 225.0.0.0.  We need to research to see if this is a reasonable
+ *      restriction.
+ *
+ */
+int
+gsmsdp_is_multicast_address (cpr_ip_addr_t theIpAddress)
+{
+    if  (theIpAddress.type == CPR_IP_ADDR_IPV4) {
+    /*
+     * Address already in host format
+     */
+        if ((theIpAddress.u.ip4 >= MULTICAST_START_ADDRESS) &&
+            (theIpAddress.u.ip4 <= MULTICAST_END_ADDRESS)) {
+        return (TRUE);
+    }
+    } else {
+        //todo IPv6: Check IPv6 multicast address here.
+
+    }
+    return (FALSE);
+}
+
+/**
+ *
+ * The function assigns or associate the new media line in the
+ * offered SDP to an entry in the media capability table.
+ *
+ * @param[in]dcb_p       - pointer to the fsmdef_dcb_t
+ * @param[in]sdp_p       - pointer to cc_sdp_t that contains the retmote SDP.
+ * @param[in]level       - uint16_t for media line level.
+ *
+ * @return           Pointer to the fsmdef_media_t if successfully
+ *                   found the anat pair media line otherwise return NULL.
+ *
+ * @pre              (dcb not_eq NULL)
+ * @pre              (sdp_p not_eq NULL)
+ * @pre              (media not_eq NULL)
+ */
+static fsmdef_media_t*
+gsmsdp_find_anat_media_line (fsmdef_dcb_t *dcb_p, cc_sdp_t *sdp_p, uint16_t level)
+{
+    fsmdef_media_t *anat_media = NULL;
+    u32            group_id_1, group_id_2;
+    u32            dst_mid, group_mid;
+    uint16_t       num_group_lines= 0;
+    uint16_t       num_anat_lines = 0;
+    uint16_t       i;
+
+    /*
+     * Get number of ANAT groupings at the session level for the media line
+     */
+    (void) sdp_attr_num_instances(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, SDP_ATTR_GROUP,
+                                  &num_group_lines);
+
+    for (i = 1; i <= num_group_lines; i++) {
+         if (sdp_get_group_attr(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, i) == SDP_GROUP_ATTR_ANAT) {
+             num_anat_lines++;
+         }
+    }
+
+    for (i = 1; i <= num_anat_lines; i++) {
+
+        dst_mid = sdp_attr_get_simple_u32(sdp_p->dest_sdp, SDP_ATTR_MID, level, 0, 1);
+        group_id_1 = sdp_get_group_id(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, i, 1);
+        group_id_2 = sdp_get_group_id(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, i, 2);
+
+        if (dst_mid == group_id_1) {
+            GSMSDP_FOR_ALL_MEDIA(anat_media, dcb_p) {
+                group_mid = sdp_attr_get_simple_u32(sdp_p->src_sdp,
+                                                    SDP_ATTR_MID, (uint16_t) group_id_2, 0, 1);
+                if (group_mid == group_id_2) {
+                    /* found a match */
+                    return (anat_media);
+                }
+            }
+        } else if (dst_mid == group_id_2) {
+            GSMSDP_FOR_ALL_MEDIA(anat_media, dcb_p) {
+                group_mid = sdp_attr_get_simple_u32(sdp_p->src_sdp,
+                                                    SDP_ATTR_MID, (uint16_t) group_id_1, 0, 1);
+                if (group_mid == group_id_1) {
+                    /* found a match */
+                    return (anat_media);
+                }
+            }
+        }
+    }
+    return (anat_media);
+}
+
+/**
+ *
+ * The function validates if all the anat groupings
+ * have the right number of ids and their media type
+ * is not the same
+ *
+ * @param[in]sdp_p      - pointer to the cc_sdp_t.
+ *
+ * @return           TRUE - anat validation passes
+ *                   FALSE - anat validation fails
+ *
+ * @pre              (dcb not_eq NULL)
+ * @pre              (sdp_p not_eq NULL)
+ */
+static boolean
+gsmsdp_validate_anat (cc_sdp_t *sdp_p)
+{
+    u16          i, num_group_id;
+    u32          group_id_1, group_id_2;
+    sdp_media_e  media_type_gid1, media_type_gid2;
+    uint16_t     num_group_lines= 0;
+    uint16_t     num_anat_lines = 0;
+
+    /*
+     * Get number of ANAT groupings at the session level for the media line
+     */
+    (void) sdp_attr_num_instances(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, SDP_ATTR_GROUP,
+                                  &num_group_lines);
+
+    for (i = 1; i <= num_group_lines; i++) {
+         if (sdp_get_group_attr(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, i) == SDP_GROUP_ATTR_ANAT) {
+             num_anat_lines++;
+         }
+    }
+
+    for (i = 1; i <= num_anat_lines; i++) {
+         num_group_id = sdp_get_group_num_id (sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, i);
+         if ((num_group_id <=0) || (num_group_id > 2)) {
+             /* This anat line has zero or more than two grouping, this is invalid */
+             return (FALSE);
+         } else if (num_group_id == 2) {
+            /* Make sure that these anat groupings are not of same type */
+            group_id_1 = sdp_get_group_id(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, i, 1);
+            group_id_2 = sdp_get_group_id(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, i, 2);
+            media_type_gid1 = sdp_get_media_type(sdp_p->dest_sdp, (u16) group_id_1);
+            media_type_gid2 = sdp_get_media_type(sdp_p->dest_sdp, (u16) group_id_2);
+            if (media_type_gid1 != media_type_gid2) {
+                /* Group id types do not match */
+                return (FALSE);
+            }
+            if (group_id_1 != sdp_attr_get_simple_u32(sdp_p->dest_sdp, SDP_ATTR_MID, (u16) group_id_1, 0, 1)) {
+                /* Group id does not match the mid at the corresponding line */
+                return (FALSE);
+            }
+            if (group_id_2 != sdp_attr_get_simple_u32(sdp_p->dest_sdp, SDP_ATTR_MID, (u16) group_id_2, 0, 1)) {
+                return (FALSE);
+            }
+         }
+    }
+
+    return (TRUE);
+}
+
+/**
+ *
+ * The function validates if all the destination
+ * Sdp m lines have mid values and if those mid values match
+ * the source Sdp mid values
+ *
+ * @param[in]sdp_p      - pointer to the cc_sdp_t
+ * @param[in]level      - uint16_t for media line level.
+ *
+ * @return           TRUE - mid validation passes
+ *                   FALSE - mid validation fails
+ *
+ * @pre              (dcb not_eq NULL)
+ * @pre              (sdp_p not_eq NULL)
+ */
+static boolean
+gsmsdp_validate_mid (cc_sdp_t *sdp_p, uint16_t level)
+{
+    int32     src_mid, dst_mid;
+    u16       i;
+    uint16_t  num_group_lines= 0;
+    uint16_t  num_anat_lines = 0;
+
+    /*
+     * Get number of ANAT groupings at the session level for the media line
+     */
+    (void) sdp_attr_num_instances(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, SDP_ATTR_GROUP,
+                                  &num_group_lines);
+
+    for (i = 1; i <= num_group_lines; i++) {
+         if (sdp_get_group_attr(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0, i) == SDP_GROUP_ATTR_ANAT) {
+             num_anat_lines++;
+         }
+    }
+
+
+    if (num_anat_lines > 0) {
+        dst_mid = sdp_attr_get_simple_u32(sdp_p->dest_sdp, SDP_ATTR_MID, level, 0, 1);
+        if (dst_mid == 0) {
+            return (FALSE);
+        }
+        if (sdp_get_group_attr(sdp_p->src_sdp, SDP_SESSION_LEVEL, 0, 1) == SDP_GROUP_ATTR_ANAT) {
+            src_mid = sdp_attr_get_simple_u32(sdp_p->src_sdp, SDP_ATTR_MID, level, 0, 1);
+            if (dst_mid != src_mid) {
+                return (FALSE);
+             }
+        }
+
+    }
+    return (TRUE);
+}
+
+/**
+ *
+ * The function negotiates the type of the media lines
+ * based on anat attributes and ipv4/ipv6 settinsg.
+ *
+ * @param[in]dcb_p      - pointer to the fsmdef_dcb_t
+ * @param[in]media      - pointer to the fsmdef_media_t
+ *
+ * @return           TRUE - this media line can be kept
+ *                   FALSE - this media line can not be kept
+ *
+ * @pre              (dcb not_eq NULL)
+ * @pre              (sdp_p not_eq NULL)
+ */
+static boolean
+gsmsdp_negotiate_addr_type (fsmdef_dcb_t *dcb_p, fsmdef_media_t *media)
+{
+    static const char fname[] = "gsmsdp_negotiate_addr_type";
+    cpr_ip_type     media_addr_type;
+    cpr_ip_mode_e   ip_mode;
+    fsmdef_media_t  *group_media;
+
+    media_addr_type = media->dest_addr.type;
+    if ((media_addr_type != CPR_IP_ADDR_IPV4) &&
+        (media_addr_type != CPR_IP_ADDR_IPV6)) {
+        /* Unknown/unsupported address type */
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"address type is not IPv4 or IPv6\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+        return (FALSE);
+    }
+    ip_mode = platform_get_ip_address_mode();
+    /*
+     * find out whether this media line is part of an ANAT group or not.
+     */
+    group_media = gsmsdp_find_anat_pair(dcb_p, media);
+
+    /*
+     * It is possible that we have a media sink/source device that
+     * attached to the phone, then we only accept IPV4 for these device.
+     *
+     * The code below is using FSM_MEDIA_F_SUPPORT_SECURITY as indication
+     * whether this media line is mapped to the off board device or not.
+     * When we get a better API to find out then use the better API than
+     * checking the FSM_MEDIA_F_SUPPORT_SECURITY.
+     */
+    if (!FSM_CHK_FLAGS(media->flags, FSM_MEDIA_F_SUPPORT_SECURITY)) {
+        if (media_addr_type != CPR_IP_ADDR_IPV4) {
+            /* off board device we do not allow other address type but IPV4 */
+            GSM_DEBUG(DEB_L_C_F_PREFIX"offboard device does not support IPV6\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+            return (FALSE);
+        }
+
+        /*
+         * P2:
+         * Need an API to get address from the off board device. For now,
+         * use our local address.
+         */
+        if ((ip_mode == CPR_IP_MODE_DUAL) || (ip_mode == CPR_IP_MODE_IPV4)) {
+            if (group_media != NULL) {
+                /*
+                 * this media line is part of ANAT group, keep the previous
+                 * one negotiated media line.
+                 */
+                return (FALSE);
+            }
+            gsmsdp_get_local_source_v4_address(media);
+            return (TRUE);
+        }
+        /* phone is IPV6 only mode */
+        return (FALSE);
+    }
+
+    if (ip_mode == CPR_IP_MODE_DUAL) {
+         /*
+          * In dual mode, IPV6 is preferred address type. If there is an
+          * ANAT then select the media line that has IPV6 address.
+          */
+         if (group_media == NULL) {
+             /*
+              * no pair media line found, this can be the first media
+              * line negotiate. Keep this media line for now.
+              */
+             if (media_addr_type == CPR_IP_ADDR_IPV4) {
+                 gsmsdp_get_local_source_v4_address(media);
+             } else {
+                 gsmsdp_get_local_source_v6_address(media);
+             }
+             return (TRUE);
+         }
+
+         /*
+          * Found a ANAT pair media structure that this media line
+          * is part of.
+          */
+         if (media_addr_type == CPR_IP_ADDR_IPV4) {
+             /*
+              * This media line is IPV4, keep the other line that have
+              * been accepted before i.e. it shows up first therefore
+              * the other one has preference.
+              */
+             return (FALSE);
+         }
+
+         /* This media line is IPV6 */
+         if (group_media->src_addr.type == CPR_IP_ADDR_IPV4) {
+             /*
+              * The previous media line part of ANAT group is IPV4. The
+              * phone policy is to select IPV6 for media stream. Remove
+              * the previous media line and keep this media line (IPV6).
+              */
+              gsmsdp_add_unsupported_stream_to_local_sdp(dcb_p->sdp,
+                                                         group_media->level);
+              gsmsdp_remove_media(dcb_p, group_media);
+              /* set this media line source address to IPV6 */
+              gsmsdp_get_local_source_v6_address(media);
+              return (TRUE);
+         }
+         /*
+          * keep the previous one is also IPV6, remove this one i.e.
+          * the one found has higher preferecne although this is not
+          * a valid ANAT grouping.
+          */
+         return (FALSE);
+    }
+
+    /*
+     * The phone is not in dual mode, the address type must be from the media
+     * line must match the address type that the phone is supporting.
+     */
+    if ((ip_mode == CPR_IP_MODE_IPV6) &&
+        (media_addr_type == CPR_IP_ADDR_IPV4)) {
+        /* incompatible address type */
+        return (FALSE);
+    }
+    if ((ip_mode == CPR_IP_MODE_IPV4) &&
+        (media_addr_type == CPR_IP_ADDR_IPV6)) {
+        /* incompatible address type */
+        return (FALSE);
+    }
+
+    if (group_media != NULL) {
+        /*
+         * This meida line is part of an ANAT group, keep the previous
+         *  media line and throw away this line.
+         */
+        return (FALSE);
+    }
+
+    /*
+     * We have a compatible address type, set the source address based on
+     * the address type from the remote media line.
+     */
+    if (media_addr_type == CPR_IP_ADDR_IPV4) {
+        gsmsdp_get_local_source_v4_address(media);
+    } else {
+        gsmsdp_get_local_source_v6_address(media);
+    }
+    /* keep this media line */
+    return (TRUE);
+}
+
+/**
+ *
+ * The function finds the best media capability that matches the offer
+ * media line according to the media table specified.
+ *
+ * @param[in]dcb_p       - pointer to the fsmdef_dcb_t
+ * @param[in]sdp_p       - pointer to cc_sdp_t that contains the retmote SDP.
+ * @param[in]media       - pointer to the fsmdef_media_t.
+ * @param[in]media_table - media table to use (global or session)
+ *
+ * @return     cap_index - the best match for the offer
+ *
+ * @pre              (dcb_p not_eq NULL)
+ * @pre              (sdp_p not_eq NULL)
+ * @pre              (media not_eq NULL)
+ */
+static uint8_t
+gsmdsp_find_best_match_media_cap_index (fsmdef_dcb_t    *dcb_p,
+                                        cc_sdp_t        *sdp_p,
+                                        fsmdef_media_t  *media,
+                                        media_table_e   media_table)
+{
+    const cc_media_cap_t *media_cap;
+    uint8_t              cap_index, candidate_cap_index;
+    boolean              srtp_fallback;
+    sdp_direction_e      remote_direction, support_direction;
+    sdp_transport_e      remote_transport;
+    sdp_media_e          media_type;
+
+    remote_transport = sdp_get_media_transport(sdp_p->dest_sdp, media->level);
+    remote_direction = gsmsdp_get_remote_sdp_direction(dcb_p, media->level,
+                                                       &media->dest_addr);
+    srtp_fallback    = sip_regmgr_srtp_fallback_enabled(dcb_p->line);
+    media_type       = media->type;
+
+
+    /*
+     * Select the best suitable media capability entry that
+     * match this media line.
+     *
+     * The following rules are used:
+     *
+     * 1) rule out entry that is invalid or not enabled or with
+     *    different media type.
+     * 2) rule out entry that has been used by other existing
+     *    media line.
+     *
+     * After the above rules applies look for the better match for
+     * direction support and security support. The platform should
+     * arrange the capability table in preference order with
+     * higher prefered entry placed at the lower index in the table.
+     */
+    candidate_cap_index = CC_MAX_MEDIA_CAP;
+    for (cap_index = 0; cap_index < CC_MAX_MEDIA_CAP; cap_index++) {
+        /* Find the cap entry that has the same media type and enabled */
+        if (media_table == MEDIA_TABLE_GLOBAL) {
+            media_cap = &g_media_table.cap[cap_index];
+        } else {
+            media_cap = gsmsdp_get_media_cap_entry_by_index(cap_index,dcb_p);
+        }
+        if ((media_cap == NULL) || !media_cap->enabled ||
+            (media_cap->type != media_type)) {
+            /* does not exist, not enabled or not the same type */
+            continue;
+        }
+
+        /* Check for already in used */
+        if (gsmsdp_find_media_by_cap_index(dcb_p, cap_index) != NULL) {
+            /* this capability entry has been used */
+            continue;
+        }
+
+        /*
+         * Check for security support. The rules below attempts to
+         * use entry that support security unless there is no entry
+         * and the SRTP fallback is enabled. If the remote offer is not
+         * SRTP just ignore the supported security and proceed on i.e.
+         * any entry is ok.
+         */
+        if (remote_transport == SDP_TRANSPORT_RTPSAVP) {
+            if (!media_cap->support_security && !srtp_fallback) {
+                /*
+                 * this entry does not support security and SRTP fallback
+                 * is not enabled.
+                 */
+                continue;
+            }
+            if (!media_cap->support_security) {
+                /*
+                 * this entry is not support security but srtp fallback
+                 * is enabled, it potentially can be used
+                 */
+                candidate_cap_index = cap_index;
+            }
+        }
+
+        /*
+         * Check for suitable direction support. The rules for matching
+         * directions are not exact rules. Try to match the closely
+         * offer as much as possible. This is the best we know. We can
+         * not guess what the real capability of the offer may have or will
+         * change in the future (re-invite).
+         */
+        support_direction = media_cap->support_direction;
+        if (remote_direction == SDP_DIRECTION_INACTIVE) {
+            if (support_direction != SDP_DIRECTION_SENDRECV) {
+                /* prefer send and receive for inactive */
+                candidate_cap_index = cap_index;
+            }
+        } else if (remote_direction == SDP_DIRECTION_RECVONLY) {
+            if ((support_direction != SDP_DIRECTION_SENDRECV) &&
+                (support_direction != SDP_DIRECTION_SENDONLY)) {
+                /* incompatible direction */
+                continue;
+            } else if (support_direction != SDP_DIRECTION_SENDONLY) {
+                candidate_cap_index = cap_index;
+            }
+        } else if (remote_direction == SDP_DIRECTION_SENDONLY) {
+            if ((support_direction != SDP_DIRECTION_SENDRECV) &&
+                (support_direction != SDP_DIRECTION_RECVONLY)) {
+                /* incompatible direction */
+                continue;
+            } else if (support_direction != SDP_DIRECTION_RECVONLY) {
+                candidate_cap_index = cap_index;
+            }
+        } else if (remote_direction == SDP_DIRECTION_SENDRECV) {
+            if (support_direction != SDP_DIRECTION_SENDRECV) {
+                candidate_cap_index = cap_index;
+            }
+        }
+
+        if (candidate_cap_index == cap_index) {
+            /* this entry is not exactly best match, try other ones */
+            continue;
+        }
+        /* this is the first best match found, use it */
+        break;
+    }
+
+    if (cap_index == CC_MAX_MEDIA_CAP) {
+        if (candidate_cap_index != CC_MAX_MEDIA_CAP) {
+            /* We have a candidate entry to use */
+            cap_index = candidate_cap_index;
+        }
+    }
+
+    return cap_index;
+}
+
+/**
+ *
+ * The function finds the best media capability that matches the offer
+ * media line.
+ *
+ * @param[in]dcb_p       - pointer to the fsmdef_dcb_t
+ * @param[in]sdp_p       - pointer to cc_sdp_t that contains the retmote SDP.
+ * @param[in]media       - pointer to the fsmdef_media_t.
+ *
+ * @return           TRUE - successful assigning a capability entry
+ *                          to the media line.
+ *                   FALSE - failed to assign a capability entry to the
+ *                           media line.
+ *
+ * @pre              (dcb_p not_eq NULL)
+ * @pre              (sdp_p not_eq NULL)
+ * @pre              (media not_eq NULL)
+ */
+static boolean
+gsmsdp_assign_cap_entry_to_incoming_media (fsmdef_dcb_t    *dcb_p,
+                                           cc_sdp_t        *sdp_p,
+                                           fsmdef_media_t  *media)
+{
+    static const char fname[] = "gsmsdp_assign_cap_entry_to_incoming_media";
+    const cc_media_cap_t *media_cap;
+    uint8_t              cap_index;
+    fsmdef_media_t       *anat_media;
+
+    /*
+     * Find an existing media line that this media line belongs to the
+     * same media group. If found, the same cap_index will be used.
+     */
+    anat_media = gsmsdp_find_anat_media_line(dcb_p, sdp_p, media->level);
+    if (anat_media != NULL) {
+        media_cap = gsmsdp_get_media_cap_entry_by_index(anat_media->cap_index, dcb_p);
+        if (media_cap == NULL) {
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"no media capability\n",
+                        dcb_p->line, dcb_p->call_id, fname);
+            return (FALSE);
+        }
+        gsmsdp_set_media_capability(media, media_cap);
+        /* found the existing media line in the same ANAT group */
+        media->cap_index = anat_media->cap_index;
+        return (TRUE);
+    }
+
+
+    cap_index  = gsmdsp_find_best_match_media_cap_index(dcb_p,
+                                                        sdp_p,
+                                                        media,
+                                                        MEDIA_TABLE_SESSION);
+
+    if (cap_index == CC_MAX_MEDIA_CAP) {
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"reached max streams supported or"
+                      " no suitable media capability\n",
+                      dcb_p->line, dcb_p->call_id, fname);
+            return (FALSE);
+        }
+
+    /* set the capabilities to the media and associate with it */
+    media_cap = gsmsdp_get_media_cap_entry_by_index(cap_index,dcb_p);
+    if (media_cap == NULL) {
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"no media cap\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+        return (FALSE);
+    }
+    gsmsdp_set_media_capability(media, media_cap);
+
+    /* override the direction for special feature */
+    gsmsdp_feature_overide_direction(dcb_p, media);
+    if (media->support_direction == SDP_DIRECTION_INACTIVE) {
+        GSM_DEBUG(DEB_L_C_F_PREFIX"feature overrides direction to inactive,"
+                  " no capability assigned\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+        return (FALSE);
+    }
+
+    media->cap_index = cap_index;
+    GSM_DEBUG(DEB_L_C_F_PREFIX"assign media cap index %d\n",
+              DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), cap_index);
+    return (TRUE);
+}
+
+/**
+ *
+ * The function handles negotiate adding of a media line.
+ *
+ * @param[in]dcb_p       - pointer to the fsmdef_dcb_t
+ * @param[in]media_type  - media type.
+ * @param[in]level       - media line.
+ * @param[in]remote_port - remote port
+ * @param[in]offer       - boolean indicates offer or answer.
+ *
+ * @return           pointer to fsmdef_media_t if media is successfully
+ *                   added or return NULL.
+ *
+ * @pre              (dcb_p not_eq NULL)
+ * @pre              (sdp_p not_eq NULL)
+ * @pre              (remote_addr not_eq NULL)
+ */
+static fsmdef_media_t *
+gsmsdp_negotiate_add_media_line (fsmdef_dcb_t  *dcb_p,
+                                 sdp_media_e   media_type,
+                                 uint16_t      level,
+                                 uint16_t      remote_port,
+                                 boolean       offer)
+{
+    static const char fname[] = "gsmsdp_negotiate_add_media_line";
+    fsmdef_media_t       *media;
+
+    if (remote_port == 0) {
+        /*
+         * This media line is new but marked as disbaled.
+         */
+        return (NULL);
+    }
+
+    if (!offer) {
+        /*
+         * This is not an offer, the remote end wants to add
+         * a new media line in the answer.
+         */
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"remote trying add media in answer SDP\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+        return (NULL);
+    }
+
+    /*
+     * Allocate a new raw media structure but not filling completely yet.
+     */
+    media = gsmsdp_get_new_media(dcb_p, media_type, level);
+    if (media == NULL) {
+        /* unable to add another media */
+        return (NULL);
+    }
+
+    /*
+     * If this call is locally held, mark the media with local hold so
+     * that the negotiate direction will have the correct direction.
+     */
+    if ((dcb_p->fcb->state == FSMDEF_S_HOLDING) ||
+        (dcb_p->fcb->state == FSMDEF_S_HOLD_PENDING)) {
+        /* the call is locally held, set the local held status */
+        FSM_SET_FLAGS(media->hold, FSM_HOLD_LCL);
+    }
+    return (media);
+}
+
+/**
+ *
+ * The function handles negotiate remove of a media line. Note the
+ * removal of a media line does not actaully removed from the offer/answer
+ * SDP.
+ *
+ * @param[in]dcb_p    - pointer to the fsmdef_dcb_t
+ * @param[in]media    - pointer to the fsmdef_media_t for the media entry
+ *                      to deactivate.
+ * @param[in]remote_port - remote port from the remote's SDP.
+ * @param[in]offer    - boolean indicates offer or answer.
+ *
+ * @return           TRUE  - when line is inactive.
+ *                   FALSE - when line remains to be further processed.
+ *
+ * @pre              (dcb not_eq NULL) and (media not_eq NULL)
+ */
+static boolean
+gsmsdp_negotiate_remove_media_line (fsmdef_dcb_t *dcb_p,
+                                    fsmdef_media_t *media,
+                                    uint16_t remote_port,
+                                    boolean offer)
+{
+    static const char fname[] = "gsmsdp_negotiate_remove_media_line";
+
+    if (offer) {
+        /* This is an offer SDP from the remote */
+        if (remote_port != 0) {
+            /* the remote quests media is not for removal */
+            return (FALSE);
+        }
+        /*
+         * Remote wants to remove the media line or to keep the media line
+         * disabled. Fall through.
+         */
+    } else {
+        /* This is an answer SDP from the remote */
+        if ((media->src_port != 0) && (remote_port != 0)) {
+            /* the media line is not for removal */
+            return (FALSE);
+        }
+        /*
+         * There are 3 possible causes:
+         * 1) our offered port is 0 and remote's port is 0
+         * 2) our offered port is 0 and remote's port is not 0.
+         * 3) our offered port is not 0 and remote's port is 0.
+         *
+         * In any of these cases, the media line will not be used.
+         */
+        if ((media->src_port == 0) && (remote_port != 0)) {
+            /* we offer media line removal but the remote does not comply */
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"remote insists on keeping media line\n",
+                        dcb_p->line, dcb_p->call_id, fname);
+        }
+    }
+
+    /*
+     * This media line is to be removed.
+     */
+    return (TRUE);
+}
+
+/*
+ * Find a media line based on the media type.
+ */
+fsmdef_media_t* gsmsdp_find_media_by_media_type(fsmdef_dcb_t *dcb_p, sdp_media_e media_type) {
+
+    fsmdef_media_t *media = NULL;
+
+    /*
+     * search the all entries that has a valid media and matches the media type
+     */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+        if (media->type == media_type) {
+            /* found a match */
+            return (media);
+        }
+    }
+    return (NULL);
+}
+
+/*
+ * gsmsdp_negotiate_media_lines
+ *
+ * Description:
+ *
+ * Walk down the media lines provided in the remote sdp. Compare each
+ * media line to the corresponding media line in the local sdp. If
+ * the media line does not exist in the local sdp, add it. If the media
+ * line exists in the local sdp but is different from the remote sdp,
+ * change the local sdp to match the remote sdp. If the media line
+ * is an AUDIO format, negotiate the codec and update the local sdp
+ * as needed.
+ *
+ * Parameters:
+ *
+ * fcb_p - Pointer to the FCB containing thhe DCB whose media lines are being negotiated
+ * sdp_p - Pointer to the local and remote SDP
+ * initial_offer - Boolean indicating if the remote SDP came in the first OFFER of this session
+ * offer - Boolean indicating if the remote SDP came in an OFFER.
+ * notify_stream_added - Boolean indicating the UI should be notified of streams added
+ *
+ */
+cc_causes_t
+gsmsdp_negotiate_media_lines (fsm_fcb_t *fcb_p, cc_sdp_t *sdp_p, boolean initial_offer,
+                              boolean offer, boolean notify_stream_added, boolean create_answer)
+{
+    static const char fname[] = "gsmsdp_negotiate_media_lines";
+    cc_causes_t     cause = CC_CAUSE_OK;
+    uint16_t        num_m_lines = 0;
+    uint16_t        num_local_m_lines = 0;
+    uint16_t        i = 0;
+    sdp_media_e     media_type;
+    fsmdef_dcb_t   *dcb_p = fcb_p->dcb;
+    uint16_t        port;
+    boolean         update_local_ret_value = TRUE;
+    sdp_transport_e transport;
+    uint16_t        crypto_inst;
+    boolean         media_found = FALSE;
+    cpr_ip_addr_t   remote_addr;
+    boolean         new_media;
+    sdp_direction_e video_avail = SDP_DIRECTION_INACTIVE;
+    boolean        unsupported_line;
+    fsmdef_media_t *media;
+    uint8_t         cap_index;
+    sdp_direction_e remote_direction;
+    boolean         result;
+    int             sdpmode = 0;
+    char           *session_pwd;
+    cc_action_data_t  data;
+    int             j=0;
+    int             rtcpmux = 0;
+    tinybool        rtcp_mux = FALSE;
+    sdp_result_e    sdp_res;
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    num_m_lines = sdp_get_num_media_lines(sdp_p->dest_sdp);
+    if (num_m_lines == 0) {
+        GSM_DEBUG(DEB_L_C_F_PREFIX"no media lines found.\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+        return CC_CAUSE_NO_MEDIA;
+    }
+
+    /*
+     * Validate the anat values
+     */
+    if (!gsmsdp_validate_anat(sdp_p)) {
+        /* Failed anat validation */
+        GSM_DEBUG(DEB_L_C_F_PREFIX"failed anat validation\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+        return (CC_CAUSE_NO_MEDIA);
+    }
+
+    /*
+     * Process each media line in the remote SDP
+     */
+    for (i = 1; i <= num_m_lines; i++) {
+        unsupported_line = FALSE; /* assume line will be supported */
+        new_media        = FALSE;
+        media            = NULL;
+        media_type = sdp_get_media_type(sdp_p->dest_sdp, i);
+
+        /*
+         * Only perform these checks when called from createanswer
+         * because at this point we are concerned as to which m= lines
+         * have been created in the answer.
+         */
+        if (create_answer) {
+
+            /* Since the incoming SDP might not be in the same order as
+               our media, we find them by type rather than location
+               for this check. Note that we're not checking for the
+               value of any _particular_ m= section; we're just checking
+               whether (at least) one of the specified type exists.  */
+            media = gsmsdp_find_media_by_media_type(dcb_p, media_type);
+
+            if (media_type == SDP_MEDIA_AUDIO && !media) {
+                /* continue if answer will not add this m= line */
+                continue;
+            }
+
+            if (media_type == SDP_MEDIA_VIDEO && !media) {
+                continue;
+            }
+
+            if (media_type == SDP_MEDIA_APPLICATION && !media) {
+                continue;
+            }
+        }
+
+        port = (uint16_t) sdp_get_media_portnum(sdp_p->dest_sdp, i);
+        GSM_DEBUG(DEB_L_C_F_PREFIX"Port is %d at %d %d\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname),
+                  port, i, initial_offer);
+
+        switch (media_type) {
+        case SDP_MEDIA_AUDIO:
+        case SDP_MEDIA_VIDEO:
+        case SDP_MEDIA_APPLICATION:
+            /*
+             * Get remote address before other negotiations process in case
+             * the address 0.0.0.0 (old style hold) to be used
+             * for direction negotiation.
+             */
+            if (!gsmsdp_get_remote_media_address(dcb_p, sdp_p, i,
+                                             &remote_addr)) {
+                /* failed to get the remote address */
+                GSM_DEBUG(DEB_L_C_F_PREFIX"unable to get remote addr at %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), i);
+                unsupported_line = TRUE;
+                break;
+            }
+
+            /*
+             * Find the corresponding media entry in the dcb to see
+             * this has been negiotiated previously (from the
+             * last offer/answer session).
+             */
+            if(!create_answer)
+              media = gsmsdp_find_media_by_level(dcb_p, i);
+
+            if (media == NULL) {
+                /* No previous media, negotiate adding new media line. */
+                media = gsmsdp_negotiate_add_media_line(dcb_p, media_type, i,
+                                                        port, offer);
+                if (media == NULL) {
+                    /* new one can not be added */
+                    unsupported_line = TRUE;
+                    break;
+                }
+                /*
+                 * This media is a newly added, it is by itself an
+                 * initial offer of this line.
+                 */
+                new_media = TRUE;
+                GSM_DEBUG(DEB_L_C_F_PREFIX"new media entry at %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), i);
+            } else if (media->type == media_type) {
+                /*
+                 * Use the remote port to determine whether the
+                 * media line is to be removed from the SDP.
+                 */
+                if (gsmsdp_negotiate_remove_media_line(dcb_p, media, port,
+                                                       offer)) {
+                    /* the media line is to be removed from the SDP */
+                    unsupported_line = TRUE;
+                    GSM_DEBUG(DEB_L_C_F_PREFIX"media at %d is removed\n",
+                              DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), i);
+                   break;
+                }
+            } else {
+                /* The media at the same level but not the expected type */
+                GSM_ERR_MSG(GSM_L_C_F_PREFIX"mismatch media type at %d\n",
+                            dcb_p->line, dcb_p->call_id, fname, i);
+                unsupported_line = TRUE;
+                break;
+            }
+
+            /* Do not negotiate if media is set to inactive */
+            if (SDP_DIRECTION_INACTIVE == media->direction) {
+                break;
+            }
+
+            /* Reset multicast flag and port */
+            media->is_multicast = FALSE;
+            media->multicast_port = 0;
+
+            /* Update remote address */
+            media->previous_sdp.dest_addr = media->dest_addr;
+            media->dest_addr = remote_addr;
+
+            /*
+             * Associate the new media (for adding new media line) to
+             * the capability table.
+             */
+            if (media->cap_index == CC_MAX_MEDIA_CAP) {
+                if (!gsmsdp_assign_cap_entry_to_incoming_media(dcb_p, sdp_p,
+                                                               media)) {
+                    unsupported_line = TRUE;
+                    GSM_DEBUG(DEB_L_C_F_PREFIX"unable to assign capability entry at %d\n",
+                              DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), i);
+                    // Check if we need to update the UI that video has been offered
+                    if ( offer && media_type == SDP_MEDIA_VIDEO &&
+                          ( ( g_media_table.cap[CC_VIDEO_1].support_direction !=
+                                   SDP_DIRECTION_INACTIVE) )  ) {
+                        // passed basic checks, now on to more expensive checks...
+                        remote_direction = gsmsdp_get_remote_sdp_direction(dcb_p,
+                                                                           media->level,
+                                                                           &media->dest_addr);
+                        cap_index        = gsmdsp_find_best_match_media_cap_index(dcb_p,
+                                                                                  sdp_p,
+                                                                                  media,
+                                                                                  MEDIA_TABLE_GLOBAL);
+
+                        GSM_DEBUG(DEB_L_C_F_PREFIX"remote_direction: %d global match %sfound\n",
+                            DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname),
+                            remote_direction, (cap_index != CC_MAX_MEDIA_CAP) ? "" : "not ");
+                        if ( cap_index != CC_MAX_MEDIA_CAP &&
+                               remote_direction != SDP_DIRECTION_INACTIVE ) {
+                           // this is an offer and platform can support video
+                           GSM_DEBUG(DEB_L_C_F_PREFIX"\n\n\n\nUpdate video Offered Called %d\n",
+                                    DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), remote_direction);
+                           lsm_update_video_offered(dcb_p->line, dcb_p->call_id, remote_direction);
+                        }
+                    }
+                    break;
+                }
+            }
+
+            /*
+             * Negotiate address type and take only address type
+             * that can be accepted.
+             */
+            if (!gsmsdp_negotiate_addr_type(dcb_p, media)) {
+                unsupported_line = TRUE;
+                break;
+            }
+
+            /*
+             * Negotiate RTP/SRTP. The result is the media transport
+             * which could be RTP/SRTP or fail.
+             */
+            transport = gsmsdp_negotiate_media_transport(dcb_p, sdp_p,
+                                                         offer, media,
+                                                         &crypto_inst, i);
+            if (transport == SDP_TRANSPORT_INVALID) {
+                /* unable to negotiate transport */
+                unsupported_line = TRUE;
+                GSM_DEBUG(DEB_L_C_F_PREFIX"transport mismatch at %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), i);
+                break;
+            }
+
+            /* Don't need to negotiate a codec for an m= applicaton line */
+            if (SDP_MEDIA_APPLICATION != media_type) {
+
+                /*
+                 * Negotiate to a single codec
+                 */
+                if (gsmsdp_negotiate_codec(dcb_p, sdp_p, media, offer, initial_offer, i) ==
+                    RTP_NONE) {
+                    /* unable to negotiate codec */
+                    unsupported_line = TRUE;
+                    /* Failed codec negotiation */
+                    cause = CC_CAUSE_PAYLOAD_MISMATCH;
+                    GSM_DEBUG(DEB_L_C_F_PREFIX"codec mismatch at %d\n",
+                              DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), i);
+                    break;
+                }
+            } else {
+                gsmsdp_negotiate_datachannel_attribs(dcb_p, sdp_p, i, media);
+            }
+
+            /*
+             * Both media transport (RTP/SRTP) and codec are
+             * now negotiated to common ones, update transport
+             * parameters to be used for SRTP, if there is any.
+             */
+            gsmsdp_update_negotiated_transport(dcb_p, sdp_p, media,
+                                               crypto_inst, transport, i);
+            GSM_DEBUG(DEB_F_PREFIX"local transport after updating negotiated: %d\n",DEB_F_PREFIX_ARGS(GSM, fname), sdp_get_media_transport(dcb_p->sdp->src_sdp, 1));
+            /*
+             * Add to or update media line to the local SDP as needed.
+             */
+            if (gsmsdp_is_multicast_address(media->dest_addr)) {
+                /*
+                 * Multicast, if the address is multicast
+                 * then change the local sdp and do the necessary
+                 * call to set up reception of multicast packets
+                 */
+                GSM_DEBUG(DEB_L_C_F_PREFIX"Got multicast offer\n",
+                         DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+                media->is_multicast = TRUE;
+                media->multicast_port = port;
+                update_local_ret_value =
+                     gsmsdp_update_local_sdp_for_multicast(dcb_p, port,
+                                                           media, offer,
+                                                           new_media);
+            } else {
+                update_local_ret_value = gsmsdp_update_local_sdp(dcb_p,
+                                                                 offer,
+                                                                 new_media,
+                                                                 media);
+            }
+            GSM_DEBUG(DEB_F_PREFIX"local transport after updateing local SDP: %d\n",DEB_F_PREFIX_ARGS(GSM, fname), sdp_get_media_transport(dcb_p->sdp->src_sdp, 1));
+
+            /*
+             * Successful codec negotiated cache direction for  ui video update
+             */
+            if (media_type == SDP_MEDIA_VIDEO ) {
+                video_avail = media->direction;
+            }
+
+            if (update_local_ret_value == TRUE) {
+                media->previous_sdp.dest_port = media->dest_port;
+                media->dest_port = port;
+                if (media_type == SDP_MEDIA_AUDIO || sdpmode) {
+                    /* at least found one workable audio media line */
+                    media_found = TRUE;
+                }
+            } else {
+                /*
+                 * Rejecting multicast because direction is not RECVONLY
+                 */
+                unsupported_line = TRUE;
+                update_local_ret_value = TRUE;
+            }
+
+            /*
+             * Negotiate rtcp-mux
+             */
+
+            sdp_res = sdp_attr_get_rtcp_mux_attribute (sdp_p->dest_sdp, i,
+                                              0, SDP_ATTR_RTCP_MUX, 1, &rtcp_mux);
+
+            if (SDP_SUCCESS == sdp_res) {
+               media->rtcp_mux = TRUE;
+            }
+
+            if (!unsupported_line) {
+
+              if (sdpmode) {
+                  int j;
+
+                  /* Set ICE */
+                  for (j=0; j<media->candidate_ct; j++) {
+                    gsmsdp_set_ice_attribute (SDP_ATTR_ICE_CANDIDATE, media->level,
+                                              sdp_p->src_sdp, media->candidatesp[j]);
+                  }
+
+                  config_get_value(CFGID_RTCPMUX, &rtcpmux, sizeof(rtcpmux));
+                  if (rtcpmux) {
+                    gsmsdp_set_rtcp_mux_attribute (SDP_ATTR_RTCP_MUX, media->level, sdp_p->src_sdp, TRUE);
+                  }
+
+                  if (notify_stream_added) {
+                    /*
+                     * Add track to remote streams in dcb
+                     */
+                     int pc_stream_id = 0;
+
+                     if (SDP_MEDIA_APPLICATION != media_type) {
+                         lsm_add_remote_stream (dcb_p->line, dcb_p->call_id, media, &pc_stream_id);
+                         gsmsdp_add_remote_stream(i-1, pc_stream_id, dcb_p, media);
+                     } else {
+                         /*
+                          * Inform VCM that a Data Channel has been negotiated
+                          */
+                         lsm_data_channel_negotiated(dcb_p->line, dcb_p->call_id, media, &pc_stream_id);
+                     }
+                  }
+              }
+            }
+
+            break;
+
+        default:
+            /* Not a support media type stream */
+            unsupported_line = TRUE;
+            break;
+        }
+
+        if (unsupported_line) {
+            /* add this line to unsupported line */
+            gsmsdp_add_unsupported_stream_to_local_sdp(sdp_p, i);
+            gsmsdp_set_mid_attr(sdp_p->src_sdp, i);
+            /* Remove the media if one to be removed */
+            if (media != NULL) {
+                /* remove this media off the list */
+                gsmsdp_remove_media(dcb_p, media);
+            }
+        }
+        if (!gsmsdp_validate_mid(sdp_p, i)) {
+             /* Failed mid validation */
+            cause = CC_CAUSE_NO_MEDIA;
+            GSM_DEBUG(DEB_L_C_F_PREFIX"failed mid validation at %d\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), i);
+        }
+    }
+
+    /*
+     * Must have at least one media found and at least one audio
+     * line.
+     */
+    if (!media_found) {
+        if (cause != CC_CAUSE_PAYLOAD_MISMATCH) {
+            cause = CC_CAUSE_NO_MEDIA;
+        }
+    } else {
+        if (cause == CC_CAUSE_PAYLOAD_MISMATCH) {
+            /*
+             * some media lines have codec mismatch but there are some
+             * that works, do not return error.
+             */
+            cause = CC_CAUSE_OK;
+        }
+
+        /*
+         * If we are processing an offer sdp, need to set the
+         * start time and stop time based on the remote SDP
+         */
+        gsmsdp_update_local_time_stamp(dcb_p, offer, initial_offer);
+
+        /*
+         * workable media line was found. Need to make sure we don't
+         * advertise more than workable media lines. Loop through
+         * remaining media lines in local SDP and set port to zero.
+         */
+        num_local_m_lines = sdp_get_num_media_lines(sdp_p->src_sdp);
+        if (num_local_m_lines > num_m_lines) {
+            for (i = num_m_lines + 1; i <= num_local_m_lines; i++) {
+                (void) sdp_set_media_portnum(sdp_p->src_sdp, i, 0, 0);
+            }
+        }
+
+        /*
+         * Update UI for Remote Stream Added
+         */
+        if (sdpmode) {
+
+            /* Fail negotiation if DTLS is not in SDP */
+            cause = gsmsdp_configure_dtls_data_attributes(fcb_p);
+            if (cause != CC_CAUSE_OK) {
+                GSM_DEBUG("gsmsdp_negotiate_media_lines- DTLS negotiation failed\n");
+                return cause;
+            }
+
+            /* ToDO(emannion)
+             * Fail negotiation if ICE is not negotiated.
+             */
+
+            /*
+             * Bubble the stream added event up to the PC UI
+             */
+            if (notify_stream_added) {
+                for (j=0; j < CC_MAX_STREAMS; j++ ) {
+                    /* If this stream has been created it should have > 0 tracks. */
+                    if (dcb_p->remote_media_stream_tbl->streams[j].num_tracks) {
+                        ui_on_remote_stream_added(evOnRemoteStreamAdd, dcb_p->line, dcb_p->call_id,
+                           dcb_p->caller_id.call_instance_id, dcb_p->remote_media_stream_tbl->streams[j]);
+
+                        /* Setting num_tracks == 0 indicates stream not set */
+                        dcb_p->remote_media_stream_tbl->streams[j].num_tracks = 0;
+                    }
+                }
+            }
+        }
+    }
+    /*
+     * We have negotiated the line, clear flag that we have set
+     * that we are waiting for an answer SDP in ack.
+     */
+    dcb_p->remote_sdp_in_ack = FALSE;
+
+    /*
+     * check to see if UI needs to be updated for video
+     */
+    GSM_DEBUG(DEB_L_C_F_PREFIX"Update video Avail Called %d\n",
+               DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname),video_avail);
+
+    // update direction but preserve the cast attrib
+    dcb_p->cur_video_avail &= CC_ATTRIB_CAST;
+    dcb_p->cur_video_avail |= (uint8_t)video_avail;
+
+    lsm_update_video_avail(dcb_p->line, dcb_p->call_id, dcb_p->cur_video_avail);
+
+    return cause;
+}
+
+/*
+ * This function returns boolean parameters indicating what media types
+ * exist in the offered SDP.
+ */
+cc_causes_t
+gsmsdp_get_offered_media_types (fsm_fcb_t *fcb_p, cc_sdp_t *sdp_p, boolean *has_audio,
+                                boolean *has_video, boolean *has_data)
+{
+    cc_causes_t     cause = CC_CAUSE_OK;
+    uint16_t        num_m_lines = 0;
+    uint16_t        i = 0;
+    sdp_media_e     media_type;
+    fsmdef_dcb_t   *dcb_p = fcb_p->dcb;
+    boolean         result;
+
+    num_m_lines = sdp_get_num_media_lines(sdp_p->dest_sdp);
+    if (num_m_lines == 0) {
+        GSM_DEBUG(DEB_L_C_F_PREFIX"no media lines found.\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, __FUNCTION__));
+        return CC_CAUSE_NO_MEDIA;
+    }
+
+    *has_audio = FALSE;
+    *has_video = FALSE;
+    *has_data = FALSE;
+
+    /*
+     * Process each media line in the remote SDP
+     */
+    for (i = 1; i <= num_m_lines; i++) {
+        media_type = sdp_get_media_type(sdp_p->dest_sdp, i);
+
+        if(SDP_MEDIA_AUDIO == media_type)
+            *has_audio = TRUE;
+        else if(SDP_MEDIA_VIDEO == media_type)
+            *has_video = TRUE;
+        else if(SDP_MEDIA_APPLICATION == media_type)
+            *has_data = TRUE;
+    }
+
+    return cause;
+}
+
+/*
+ * gsmsdp_init_local_sdp
+ *
+ * Description:
+ *
+ * This function initializes the local sdp for generation of an offer sdp or
+ * an answer sdp. The following sdp values are initialized.
+ *
+ * v= line
+ * o= line <username><session id><version><network type><address type><address>
+ * s= line
+ * t= line <start time><stop time>
+ *
+ * Parameters:
+ *
+ * peerconnection - handle to peerconnection object
+ * sdp_pp     - Pointer to the local sdp
+ *
+ * returns    cc_causes_t
+ *            CC_CAUSE_OK - indicates success
+ *            CC_CAUSE_ERROR - indicates failure
+ */
+static cc_causes_t
+gsmsdp_init_local_sdp (const char *peerconnection, cc_sdp_t **sdp_pp)
+{
+    char            addr_str[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t   ipaddr;
+    unsigned long   session_id = 0;
+    char            session_version_str[GSMSDP_VERSION_STR_LEN];
+    void           *local_sdp_p = NULL;
+    cc_sdp_t       *sdp_p = NULL;
+    int             nat_enable = 0;
+    char           *p_addr_str;
+    cpr_ip_mode_e   ip_mode;
+    char           *strtok_state;
+
+    if (!peerconnection || !sdp_pp) {
+        return CC_CAUSE_ERROR;
+    }
+
+    ip_mode = platform_get_ip_address_mode();
+    /*
+     * Get device address. We will need this later.
+     */
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        if ((ip_mode == CPR_IP_MODE_DUAL) || (ip_mode == CPR_IP_MODE_IPV6)) {
+            sip_config_get_net_ipv6_device_ipaddr(&ipaddr);
+        } else if (ip_mode == CPR_IP_MODE_IPV4) {
+            sip_config_get_net_device_ipaddr(&ipaddr);
+        }
+    } else {
+        sip_config_get_nat_ipaddr(&ipaddr);
+    }
+
+
+       ipaddr2dotted(addr_str, &ipaddr);
+
+    p_addr_str = PL_strtok_r(addr_str, "[ ]", &strtok_state);
+
+    /*
+     * Create the local sdp struct
+     */
+    if (*sdp_pp == NULL) {
+        sipsdp_src_dest_create(peerconnection,
+            CCSIP_SRC_SDP_BIT, sdp_pp);
+    } else {
+        sdp_p = *sdp_pp;
+        if (sdp_p->src_sdp != NULL) {
+            sipsdp_src_dest_free(CCSIP_SRC_SDP_BIT, sdp_pp);
+        }
+        sipsdp_src_dest_create(peerconnection,
+            CCSIP_SRC_SDP_BIT, sdp_pp);
+    }
+    sdp_p = *sdp_pp;
+
+    if ( sdp_p == NULL )
+       return CC_CAUSE_ERROR;
+
+    local_sdp_p = sdp_p->src_sdp;
+
+    /*
+     * v= line
+     */
+    (void) sdp_set_version(local_sdp_p, SIPSDP_VERSION);
+
+    /*
+     * o= line <username><session id><version><network type>
+     * <address type><address>
+     */
+    (void) sdp_set_owner_username(local_sdp_p, SIPSDP_ORIGIN_USERNAME);
+
+    session_id = abs(cpr_rand() % 28457);
+    snprintf(session_version_str, sizeof(session_version_str), "%d",
+             (int) session_id);
+    (void) sdp_set_owner_sessionid(local_sdp_p, session_version_str);
+
+    snprintf(session_version_str, sizeof(session_version_str), "%d", 0);
+    (void) sdp_set_owner_version(local_sdp_p, session_version_str);
+
+    (void) sdp_set_owner_network_type(local_sdp_p, SDP_NT_INTERNET);
+
+    if ((ip_mode == CPR_IP_MODE_DUAL) || (ip_mode == CPR_IP_MODE_IPV6)) {
+        (void) sdp_set_owner_address_type(local_sdp_p, SDP_AT_IP6);
+    } else if (ip_mode == CPR_IP_MODE_IPV4) {
+       (void) sdp_set_owner_address_type(local_sdp_p, SDP_AT_IP4);
+    }
+    (void) sdp_set_owner_address(local_sdp_p, p_addr_str);
+
+    /*
+     * s= line
+     */
+    (void) sdp_set_session_name(local_sdp_p, SIPSDP_SESSION_NAME);
+
+    /*
+     * t= line <start time><stop time>
+     * We init these to zero. If we are building an answer sdp, these will
+     * be reset from the offer sdp.
+     */
+    (void) sdp_set_time_start(local_sdp_p, "0");
+    (void) sdp_set_time_stop(local_sdp_p, "0");
+
+    return CC_CAUSE_OK;
+}
+
+/**
+ * The function sets the capabilities from media capability to the
+ * media structure.
+ *
+ * @param[in]media     - pointer to the fsmdef_media_t to be set with
+ *                       capabilites from the media_cap.
+ * @param[in]media_cap - media capability to be used with this new media
+ *                       line.
+ *
+ * @return           None.
+ *
+ * @pre              (media not_eq NULL)
+ * @pre              (media_cap not_eq NULL)
+ */
+static void
+gsmsdp_set_media_capability (fsmdef_media_t *media,
+                             const cc_media_cap_t *media_cap)
+{
+    /* set default direction */
+    media->direction = media_cap->support_direction;
+    media->support_direction = media_cap->support_direction;
+    if (media_cap->support_security) {
+        /* support security */
+        FSM_SET_FLAGS(media->flags, FSM_MEDIA_F_SUPPORT_SECURITY);
+    }
+}
+
+/**
+ * The function adds a media line into the local SDP.
+ *
+ * @param[in]dcb_p     - pointer to the fsmdef_dcb_t
+ * @param[in]media_cap - media capability to be used with this new media
+ *                       line.
+ * @param[in]cap_index - media capability entry index to associate with
+ *                       the media line.
+ * @param[in]level     - media line order in the SDP so called level.
+ * @param[in]addr_type - cpr_ip_type for address of the media line to add.
+ *
+ * @return           Pointer to the fsmdef_media_t if successfully
+ *                   add a new line otherwise return NULL.
+ *
+ * @pre              (dcb_p not_eq NULL)
+ * @pre              (media_cap not_eq NULL)
+ */
+static fsmdef_media_t *
+gsmsdp_add_media_line (fsmdef_dcb_t *dcb_p, const cc_media_cap_t *media_cap,
+                       uint8_t cap_index, uint16_t level,
+                       cpr_ip_type addr_type, boolean offer)
+{
+    static const char fname[] = "gsmsdp_add_media_line";
+    cc_action_data_t  data;
+    fsmdef_media_t   *media = NULL;
+    int               i=0;
+    int               rtcpmux = 0;
+    int               sctp_port = 0;
+
+    switch (media_cap->type) {
+    case SDP_MEDIA_AUDIO:
+    case SDP_MEDIA_VIDEO:
+    case SDP_MEDIA_APPLICATION:
+        media = gsmsdp_get_new_media(dcb_p, media_cap->type, level);
+        if (media == NULL) {
+            /* should not happen */
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"no media entry available\n",
+                        dcb_p->line, dcb_p->call_id, fname);
+            return (NULL);
+        }
+
+        /* set capabilities */
+        gsmsdp_set_media_capability(media, media_cap);
+
+        /* associate this media line to the capability entry */
+        media->cap_index = cap_index; /* keep the media cap entry index */
+
+        /* override the direction for special feature */
+        gsmsdp_feature_overide_direction(dcb_p, media);
+        if (media->support_direction == SDP_DIRECTION_INACTIVE) {
+            GSM_DEBUG(DEB_L_C_F_PREFIX"feature overrides direction to inactive"
+                      " no media added\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+
+            /*
+             * For an answer, SDP_DIRECTION_INACTIVE will now add an m=
+             * line but it is disabled.
+             */
+            if (!offer) {
+                media->src_port = 0;
+            } else {
+              gsmsdp_remove_media(dcb_p, media);
+              return (NULL);
+            }
+        }
+
+        if (media->support_direction != SDP_DIRECTION_INACTIVE) {
+          /*
+           * Get the local RTP port. The src port will be set in the dcb
+           * within the call to cc_call_action(CC_ACTION_OPEN_RCV)
+           */
+          data.open_rcv.is_multicast = FALSE;
+          data.open_rcv.listen_ip = ip_addr_invalid;
+          data.open_rcv.port = 0;
+          data.open_rcv.keep = FALSE;
+          /*
+           * Indicate type of media (audio/video etc) becase some for
+           * supporting video over vieo, the port is obtained from other
+           * entity.
+           */
+          data.open_rcv.media_type = media->type;
+          data.open_rcv.media_refid = media->refid;
+          if (cc_call_action(dcb_p->call_id, dcb_p->line,
+                             CC_ACTION_OPEN_RCV,
+                             &data) != CC_RC_SUCCESS) {
+              GSM_ERR_MSG(GSM_L_C_F_PREFIX"allocate rx port failed\n",
+                          dcb_p->line, dcb_p->call_id, fname);
+              gsmsdp_remove_media(dcb_p, media);
+              return (NULL);
+          }
+
+          /* allocate port successful, save the port */
+
+          media->src_port = data.open_rcv.port;
+
+          if(media_cap->type == SDP_MEDIA_APPLICATION) {
+            config_get_value(CFGID_SCTP_PORT, &sctp_port, sizeof(sctp_port));
+            media->sctp_port = sctp_port;
+          }
+
+          /*
+           * Setup the local soruce address.
+           */
+          if (addr_type == CPR_IP_ADDR_IPV6) {
+              gsmsdp_get_local_source_v6_address(media);
+          } else if (addr_type == CPR_IP_ADDR_IPV4) {
+              gsmsdp_get_local_source_v4_address(media);
+          } else {
+              GSM_ERR_MSG(GSM_L_C_F_PREFIX"invalid IP address mode\n",
+                          dcb_p->line, dcb_p->call_id, fname);
+              gsmsdp_remove_media(dcb_p, media);
+              return (NULL);
+          }
+
+        }
+
+        /*
+         * Initialize the media transport for RTP or SRTP (or do not thing
+         * and leave to the gsmsdp_update_local_sdp_media to set default)
+         */
+        gsmsdp_init_sdp_media_transport(dcb_p, dcb_p->sdp->src_sdp, media);
+
+
+        gsmsdp_update_local_sdp_media(dcb_p, dcb_p->sdp, TRUE, media,
+                                          media->transport);
+
+        if (media->support_direction != SDP_DIRECTION_INACTIVE) {
+
+          gsmsdp_set_local_sdp_direction(dcb_p, media, media->direction);
+
+          /*
+           * wait until here to set ICE candidates as SDP is now initialized
+           */
+          for (i=0; i<media->candidate_ct; i++) {
+            gsmsdp_set_ice_attribute (SDP_ATTR_ICE_CANDIDATE, level, dcb_p->sdp->src_sdp, media->candidatesp[i]);
+          }
+
+          config_get_value(CFGID_RTCPMUX, &rtcpmux, sizeof(rtcpmux));
+          if (rtcpmux) {
+            gsmsdp_set_rtcp_mux_attribute (SDP_ATTR_RTCP_MUX, level, dcb_p->sdp->src_sdp, TRUE);
+          }
+
+
+          /*
+           * Since we are initiating an initial offer and opening a
+           * receive port, store initial media settings.
+           */
+          media->previous_sdp.avt_payload_type = media->avt_payload_type;
+          media->previous_sdp.direction = media->direction;
+          media->previous_sdp.packetization_period = media->packetization_period;
+          gsmsdp_copy_payloads_to_previous_sdp(media);
+          break;
+        }
+
+    default:
+        /* Unsupported media type, not added */
+        GSM_DEBUG(DEB_L_C_F_PREFIX"media type %d is not supported\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), media_cap->type);
+        break;
+    }
+    return (media);
+}
+
+/*
+ * gsmsdp_create_local_sdp
+ *
+ * Description:
+ *
+ * Parameters:
+ *
+ * dcb_p - Pointer to the DCB whose local SDP is to be updated.
+ * force_streams_enabled - temporarily generate SDP even when no
+ *                         streams are added
+ *
+ * returns    cc_causes_t
+ *            CC_CAUSE_OK - indicates success
+ *            CC_CAUSE_ERROR - indicates failure
+ */
+cc_causes_t
+gsmsdp_create_local_sdp (fsmdef_dcb_t *dcb_p, boolean force_streams_enabled,
+                         boolean audio, boolean video, boolean data, boolean offer)
+{
+    static const char fname[] = "gsmsdp_create_local_sdp";
+    uint16_t        level;
+    const cc_media_cap_table_t *media_cap_tbl;
+    const cc_media_cap_t       *media_cap;
+    cpr_ip_mode_e   ip_mode;
+    uint8_t         cap_index;
+    fsmdef_media_t  *media;
+    boolean         has_audio;
+    int             sdpmode = 0;
+    boolean         media_enabled;
+
+    if ( CC_CAUSE_OK != gsmsdp_init_local_sdp(dcb_p->peerconnection,
+        &(dcb_p->sdp)) )
+      return CC_CAUSE_ERROR;
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    dcb_p->src_sdp_version = 0;
+
+    media_cap_tbl = dcb_p->media_cap_tbl;
+
+    if (media_cap_tbl == NULL) {
+        /* should not happen */
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"no media capbility available\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+        return (CC_CAUSE_ERROR);
+    }
+
+    media_cap = &media_cap_tbl->cap[0];
+    level = 0;
+    for (cap_index = 0; cap_index < CC_MAX_MEDIA_CAP-1; cap_index++) {
+
+        /* Build local m lines based on m lines that were in the offered SDP */
+        media_enabled = TRUE;
+        if (FALSE == audio && SDP_MEDIA_AUDIO == media_cap->type) {
+            media_enabled = FALSE;
+        } else if (FALSE == video && SDP_MEDIA_VIDEO == media_cap->type) {
+            media_enabled = FALSE;
+        } else if (FALSE == data && SDP_MEDIA_APPLICATION == media_cap->type) {
+            media_enabled = FALSE;
+        }
+
+        /*
+         * Add each enabled media line to the SDP
+         */
+        if (media_enabled && ( media_cap->enabled || force_streams_enabled)) {
+            level = level + 1;  /* next level */
+            ip_mode = platform_get_ip_address_mode();
+            if (ip_mode >= CPR_IP_MODE_IPV6) {
+                if (gsmsdp_add_media_line(dcb_p, media_cap, cap_index,
+                                          level, CPR_IP_ADDR_IPV6, offer)
+                    == NULL) {
+                    /* fail to add a media line, go back one level */
+                    level = level - 1;
+                }
+
+                if (ip_mode == CPR_IP_MODE_DUAL) {
+                    level = level + 1;  /* next level */
+                    if (gsmsdp_add_media_line(dcb_p, media_cap, cap_index,
+                                              level, CPR_IP_ADDR_IPV4, offer) ==
+                        NULL) {
+                        /* fail to add a media line, go back one level */
+                        level = level - 1;
+                    }
+                }
+            } else {
+                if (gsmsdp_add_media_line(dcb_p, media_cap, cap_index, level,
+                                          CPR_IP_ADDR_IPV4, offer) == NULL) {
+                    /* fail to add a media line, go back one level */
+                    level = level - 1;
+                }
+            }
+        }
+        /* next capability */
+        media_cap++;
+    }
+
+    if (level == 0) {
+        /*
+         * Did not find media line for the SDP and we do not
+         * support SDP without any media line.
+         */
+        GSM_ERR_MSG(GSM_L_C_F_PREFIX"no media line for SDP\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+        return (CC_CAUSE_ERROR);
+    }
+
+    /*
+     *
+     * This is a suitable place to add ice ufrag and pwd to the SDP
+     */
+
+    if (dcb_p->ice_ufrag)
+        gsmsdp_set_ice_attribute (SDP_ATTR_ICE_UFRAG, SDP_SESSION_LEVEL, dcb_p->sdp->src_sdp, dcb_p->ice_ufrag);
+    if (dcb_p->ice_pwd)
+        gsmsdp_set_ice_attribute (SDP_ATTR_ICE_PWD, SDP_SESSION_LEVEL, dcb_p->sdp->src_sdp, dcb_p->ice_pwd);
+
+    if(strlen(dcb_p->digest_alg)  > 0)
+        gsmsdp_set_dtls_fingerprint_attribute (SDP_ATTR_DTLS_FINGERPRINT, SDP_SESSION_LEVEL,
+            dcb_p->sdp->src_sdp, dcb_p->digest_alg, dcb_p->digest);
+
+    if (!sdpmode) {
+
+       /*
+        * Ensure that there is at least one audio line.
+        */
+        has_audio = FALSE;
+        GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+            if (media->type == SDP_MEDIA_AUDIO) {
+                has_audio = TRUE; /* found one audio line, done */
+                break;
+            }
+        }
+        if (!has_audio) {
+            /* No audio, do not allow */
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"no audio media line for SDP\n",
+                    dcb_p->line, dcb_p->call_id, fname);
+            return (CC_CAUSE_ERROR);
+        }
+    }
+
+    return CC_CAUSE_OK;
+}
+
+/**
+ * The function creates a SDP that contains phone's current
+ * capability for an option.
+ *
+ * @param[in/out]sdp_pp     - pointer to a pointer to cc_sdp_t to return
+ *                            the created SDP.
+ * @return                  none.
+ * @pre              (sdp_pp not_eq NULL)
+ */
+void
+gsmsdp_create_options_sdp (cc_sdp_t ** sdp_pp)
+{
+    cc_sdp_t *sdp_p;
+
+    /* This empty string represents to associated peerconnection object */
+    if (gsmsdp_init_local_sdp("", sdp_pp) == CC_CAUSE_ERROR) {
+        return;
+    }
+
+    sdp_p = *sdp_pp;
+
+    /*
+     * Insert media line at level 1.
+     */
+    if (sdp_insert_media_line(sdp_p->src_sdp, 1) != SDP_SUCCESS) {
+        // Error
+        return;
+    }
+
+    (void) sdp_set_media_type(sdp_p->src_sdp, 1, SDP_MEDIA_AUDIO);
+    (void) sdp_set_media_portnum(sdp_p->src_sdp, 1, 0, 0);
+    gsmsdp_set_media_transport_for_option(sdp_p->src_sdp, 1);
+
+    /*
+     * Add all supported media formats to the local sdp.
+     */
+    gsmsdp_add_default_audio_formats_to_local_sdp(NULL, sdp_p, NULL);
+
+    /* Add Video m line if video caps are enabled */
+    if ( g_media_table.cap[CC_VIDEO_1].enabled == TRUE ) {
+        if (sdp_insert_media_line(sdp_p->src_sdp, 2) != SDP_SUCCESS) {
+            // Error
+            return;
+        }
+
+        (void) sdp_set_media_type(sdp_p->src_sdp, 2, SDP_MEDIA_VIDEO);
+        (void) sdp_set_media_portnum(sdp_p->src_sdp, 2, 0, 0);
+        gsmsdp_set_media_transport_for_option(sdp_p->src_sdp, 2);
+
+        gsmsdp_add_default_video_formats_to_local_sdp(NULL, sdp_p, NULL);
+    }
+}
+
+/**
+ * The function checks and removes media capability for the media
+ * lines that is to be removed.
+ *
+ * @param[in]dcb_p   - Pointer to DCB
+ *
+ * @return           TRUE  - if there is a media line removed.
+ *                   FALSE - if there is no media line to remove.
+ *
+ * @pre              (dcb_p not_eq NULL)
+ */
+static boolean
+gsmsdp_check_remove_local_sdp_media (fsmdef_dcb_t *dcb_p)
+{
+    static const char fname[] = "gsmsdp_check_remove_local_sdp_media";
+    fsmdef_media_t             *media, *media_to_remove;
+    const cc_media_cap_t       *media_cap;
+    boolean                    removed = FALSE;
+
+    media = GSMSDP_FIRST_MEDIA_ENTRY(dcb_p);
+    while (media) {
+        media_cap = gsmsdp_get_media_cap_entry_by_index(media->cap_index,dcb_p);
+        if (media_cap != NULL) {
+            /* found the corresponding capability of the media line */
+            if (!media_cap->enabled) {
+                GSM_DEBUG(DEB_L_C_F_PREFIX"remove media at level %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), media->level);
+                /* set the media line to unused */
+                gsmsdp_add_unsupported_stream_to_local_sdp(dcb_p->sdp,
+                                                           media->level);
+                /*
+                 * remember the media to remove and get the next media to
+                 * work on before removing this media off the linked list.
+                 */
+                media_to_remove = media;
+                media = GSMSDP_NEXT_MEDIA_ENTRY(media);
+
+                /* remove the media from the list */
+                gsmsdp_remove_media(dcb_p, media_to_remove);
+                removed = TRUE;
+                continue;
+            }
+        }
+        media = GSMSDP_NEXT_MEDIA_ENTRY(media);
+    }
+    return (removed);
+}
+
+/**
+ * The function checks and adds media capability for the media
+ * lines that is to be added.
+ *
+ * @param[in]dcb_p   - Pointer to DCB
+ * @param[in]hold    - TRUE indicates the newly media line
+ *                     should have direction that indicates hold.
+ *
+ * @return           TRUE  - if there is a media line added.
+ *                   FALSE - if there is no media line to added.
+ *
+ * @pre              (dcb_p not_eq NULL)
+ */
+static boolean
+gsmsdp_check_add_local_sdp_media (fsmdef_dcb_t *dcb_p, boolean hold)
+{
+    static const char fname[] = "gsmsdp_check_add_local_sdp_media";
+    fsmdef_media_t             *media;
+    const cc_media_cap_t       *media_cap;
+    uint8_t                    cap_index;
+    uint16_t                   num_m_lines, level_to_use;
+    void                       *src_sdp;
+    boolean                    need_mix = FALSE;
+    boolean                    added = FALSE;
+    cpr_ip_mode_e              ip_mode;
+    cpr_ip_type                ip_addr_type[2]; /* for 2 IP address types */
+    uint16_t                   i, num_ip_addrs;
+
+    if (fsmcnf_get_ccb_by_call_id(dcb_p->call_id) != NULL) {
+        /*
+         * This call is part of a local conference. The mixing
+         * support will be needed for additional media line.
+         * If platform does not have capability to support mixing
+         * of a particular media type for the local conference, either
+         * leg in the conference will not see addition media line
+         * added.
+         */
+        need_mix = TRUE;
+    }
+
+    /*
+     * Find new media entries to be added.
+     */
+    src_sdp = dcb_p->sdp ? dcb_p->sdp->src_sdp : NULL;
+    for (cap_index = 0; cap_index < CC_MAX_MEDIA_CAP; cap_index++) {
+        media_cap = gsmsdp_get_media_cap_entry_by_index(cap_index, dcb_p);
+        if (media_cap == NULL) {
+            GSM_ERR_MSG(GSM_L_C_F_PREFIX"no media capbility available\n",
+                        dcb_p->line, dcb_p->call_id, fname);
+            continue;
+        }
+        if (!media_cap->enabled) {
+            /* this entry is disabled, skip it */
+            continue;
+        }
+        media = gsmsdp_find_media_by_cap_index(dcb_p, cap_index);
+        if (media != NULL) {
+            /* this media entry exists, skip it */
+            continue;
+        }
+
+        /*
+         * This is a new entry the capability table to be added.
+         */
+        if (CC_IS_AUDIO(cap_index) && need_mix) {
+            if (!gsmsdp_platform_addition_mix(dcb_p, media_cap->type)) {
+                /* platform can not support additional mixing of this type */
+                GSM_DEBUG(DEB_L_C_F_PREFIX"no support addition mixing for %d "
+                          "media type\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname),
+                                                 media_cap->type);
+                continue;
+            }
+        }
+
+        /*
+         * It depends on current address mode, if the mode is dual mode then
+         * we need to add 2 media lines.
+         */
+        ip_mode = platform_get_ip_address_mode();
+        switch (ip_mode) {
+        case CPR_IP_MODE_DUAL:
+            /* add both addresses, IPV6 line is first as it is prefered */
+            num_ip_addrs = 2;
+            ip_addr_type[0] = CPR_IP_ADDR_IPV6;
+            ip_addr_type[1] = CPR_IP_ADDR_IPV4;
+            break;
+        case CPR_IP_MODE_IPV6:
+            /* add IPV6 address only */
+            num_ip_addrs = 1;
+            ip_addr_type[0] = CPR_IP_ADDR_IPV6;
+            break;
+        default:
+            /* add IPV4 address only */
+            num_ip_addrs = 1;
+            ip_addr_type[0] = CPR_IP_ADDR_IPV4;
+            break;
+        }
+        /* add media line or lines */
+        for (i = 0; i < num_ip_addrs; i++) {
+            /*
+             * This is a new stream to add, find an unused media line
+             * in the SDP. Find the unused media line that has the same
+             * media type as the one to be added. The RFC-3264 allows reuse
+             * any unused slot in the SDP body but it was recomended to use
+             * the same type to increase the chance of interoperability by
+             * using the unuse slot that has the same media type.
+             */
+            level_to_use = gsmsdp_find_unused_media_line_with_type(src_sdp,
+                               media_cap->type);
+            if (level_to_use == 0) {
+                /* no empty slot is found, add a new line to the SDP */
+                num_m_lines  = sdp_get_num_media_lines(src_sdp);
+                level_to_use = num_m_lines + 1;
+            }
+            GSM_DEBUG(DEB_L_C_F_PREFIX"add media at level %d\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), level_to_use);
+
+            /* add a new media */
+            media = gsmsdp_add_media_line(dcb_p, media_cap, cap_index,
+                                          level_to_use, ip_addr_type[i], FALSE);
+            if (media != NULL) {
+                /* successfully add a new media line */
+                if (hold) {
+                    /* this new media needs to be sent out with hold */
+                    gsmsdp_set_local_hold_sdp(dcb_p, media);
+                }
+                added = TRUE;
+            } else {
+                GSM_ERR_MSG(GSM_L_C_F_PREFIX"Unable to add a new media\n",
+                            dcb_p->line, dcb_p->call_id, fname);
+            }
+        }
+    }
+    return (added);
+}
+
+/**
+ * The function checks support direction changes and updates the support
+ * direction of media lines.
+ *
+ * @param[in]dcb_p         - Pointer to DCB
+ * @param[in]no_sdp_update - TRUE indicates do not update SDP.
+ *
+ * @return           TRUE  - if there is a media line support direction
+ *                           changes.
+ *                   FALSE - if there is no media line that has
+ *                           support direction change.
+ *
+ * @pre              (dcb_p not_eq NULL)
+ */
+static boolean
+gsmsdp_check_direction_change_local_sdp_media (fsmdef_dcb_t *dcb_p,
+                                               boolean no_sdp_update)
+{
+    static const char fname[] = "gsmsdp_check_direction_change_local_sdp_media";
+    fsmdef_media_t             *media;
+    const cc_media_cap_t       *media_cap;
+    boolean                    direction_change = FALSE;
+    sdp_direction_e            save_supported_direction;
+
+    media = GSMSDP_FIRST_MEDIA_ENTRY(dcb_p);
+    while (media) {
+        media_cap = gsmsdp_get_media_cap_entry_by_index(media->cap_index, dcb_p);
+        if (media_cap != NULL) {
+            if (media->support_direction !=
+                media_cap->support_direction) {
+                /*
+                 * There is a possibility that supported direction has
+                 * been overrided due to some feature. Check to see
+                 * the supported direction remains the same after override
+                 * take place. If it is different then there is a direction
+                 * change.
+                 */
+                save_supported_direction = media->support_direction;
+                media->support_direction = media_cap->support_direction;
+                gsmsdp_feature_overide_direction(dcb_p, media);
+                if (media->support_direction == save_supported_direction) {
+                    /* nothing change after override */
+                } else {
+                    /* there is no override, this is a change */
+                    direction_change = TRUE;
+                }
+                if (direction_change) {
+                    /* Support direction changed */
+                    GSM_DEBUG(DEB_L_C_F_PREFIX"change support direction at level %d"
+                              " from %d to %d\n",
+                                                         DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname),
+                                                         media->level, media->support_direction,
+                              media_cap->support_direction);
+                    if (no_sdp_update) {
+                        /*
+                         * The caller does not want to update SDP.
+                         */
+                        media->direction = media_cap->support_direction;
+                    } else {
+                        /*
+                         * Need to update direction in the SDP.
+                         * The direction in the media structure will
+                         * be set by the gsmsdp_set_local_sdp_direction.
+                         */
+                        gsmsdp_set_local_sdp_direction(dcb_p, media,
+                                               media->support_direction);
+                    }
+                }
+            }
+        }
+        media = GSMSDP_NEXT_MEDIA_ENTRY(media);
+    }
+    return (direction_change);
+}
+
+/**
+ * The function resets media specified takes the hold state into account
+ *
+ * @param[in]dcb_p   - Pointer to DCB
+ * @param[in]media   - Media to be updated
+ * @param[in]hold    - Set media line will be set to hold.
+ */
+static void gsmsdp_reset_media(fsmdef_dcb_t *dcb_p, fsmdef_media_t *media, boolean hold){
+    gsmsdp_reset_local_sdp_media(dcb_p, media, hold);
+    if (hold) {
+        gsmsdp_set_local_hold_sdp(dcb_p, media);
+    } else {
+        gsmsdp_set_local_resume_sdp(dcb_p, media);
+    }
+}
+
+/**
+ *
+ * This functions checks if the media IP Address has changed
+ *
+ * Convert the configMedia IP String to Ip address format,
+ * check if it is valid and then compare to current media source
+ * address, if the address is valid and the media source and
+ * config media ip differ, then
+ *
+ *  1) Stop the media to close the socket
+ *  2) Set flag to true, to initiate re-invite
+ *  3) Update the media src addr
+ *  4) Update the c= line to reflect the new IP address source
+ *
+ * @param[in]dcb_p   - Pointer to DCB
+ * @return         TRUE  - if IP changed
+ *                 FALSE - if no no IP changed
+ *
+ * @pre              (dcb_p not_eq NULL)
+ */
+boolean
+gsmsdp_media_ip_changed (fsmdef_dcb_t *dcb_p)
+{
+    static const char     fname[] = "gsmsdp_media_ip_changed";
+    boolean               ip_changed = FALSE;
+    cpr_ip_addr_t         addr ;
+    char                  curr_media_ip[MAX_IPADDR_STR_LEN];
+    char                  addr_str[MAX_IPADDR_STR_LEN];
+    fsmdef_media_t        *media;
+
+    /*
+     * Check if media IP has changed
+     */
+    init_empty_str(curr_media_ip);
+    config_get_value(CFGID_MEDIA_IP_ADDR, curr_media_ip,
+                        MAX_IPADDR_STR_LEN);
+    if (!is_empty_str(curr_media_ip)) {
+        str2ip(curr_media_ip, &addr);
+        util_ntohl(&addr, &addr);
+        GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+            if ((util_check_if_ip_valid(&media->src_addr) == TRUE) &&
+                (util_check_if_ip_valid(&addr) == TRUE) &&
+                (util_compare_ip(&media->src_addr, &addr) == FALSE)) {
+                ipaddr2dotted(curr_media_ip, &media->src_addr); // for logging
+
+                (void)cc_call_action(dcb_p->call_id, dcb_p->line,
+                          CC_ACTION_STOP_MEDIA,
+                          NULL);
+                ip_changed = TRUE;
+                media->src_addr = addr;
+                if (dcb_p->sdp != NULL) {
+                    gsmsdp_set_connection_address(dcb_p->sdp->src_sdp,
+                            media->level,
+                            dcb_p->ice_default_candidate_addr);
+                }
+                ipaddr2dotted(addr_str, &media->src_addr);  // for logging
+                GSM_ERR_MSG("%s MEDIA IP_CHANGED: after Update IP %s"\
+                            " before %s" ,fname, addr_str, curr_media_ip );
+            }
+        }
+    }
+
+    return (ip_changed);
+}
+
+/**
+ * this function will check whether the media ip addres in local sdp is same as to
+ * media IP provided by application. If IP differs, then re-INVITE request is posted.
+ */
+boolean is_gsmsdp_media_ip_updated_to_latest( fsmdef_dcb_t * dcb ) {
+    cpr_ip_addr_t         media_ip_in_host_order ;
+    char                  curr_media_ip[MAX_IPADDR_STR_LEN];
+    fsmdef_media_t        *media;
+
+    init_empty_str(curr_media_ip);
+    config_get_value(CFGID_MEDIA_IP_ADDR, curr_media_ip, MAX_IPADDR_STR_LEN);
+    if (is_empty_str(curr_media_ip) == FALSE) {
+        str2ip(curr_media_ip, &media_ip_in_host_order);
+        util_ntohl(&media_ip_in_host_order, &media_ip_in_host_order);
+
+        GSMSDP_FOR_ALL_MEDIA(media, dcb) {
+            if (util_check_if_ip_valid(&media->src_addr) == TRUE) {
+                if (util_compare_ip(&media->src_addr, &media_ip_in_host_order) == FALSE) {
+                    return FALSE;
+                }
+            }
+        }
+    }
+    return TRUE;
+}
+
+
+/**
+ *
+ * The function checks for media capability changes and updates the
+ * local SDP for the changes. The function also provides a couple of options
+ * 1)  reset the unchange media lines to initialize the codec list,
+ * crypto etc. and 2) an option to update media directions for all
+ * media lines for hold. 3)
+ *
+ * @param[in]dcb_p   - Pointer to DCB
+ * @param[in]reset   - Reset the unchanged media lines to include all
+ *                     codecs etc. again.
+ * @param[in]hold    - Set media line will be set to hold.
+ *
+ * @return         TRUE  - if media changes occur.
+ *                 FALSE - if no media change occur.
+ *
+ * @pre              (dcb_p not_eq NULL)
+ */
+boolean
+gsmsdp_update_local_sdp_media_capability (fsmdef_dcb_t *dcb_p, boolean reset,
+                                          boolean hold)
+{
+    static const char     fname[] = "gsmsdp_update_local_sdp_media_capability";
+    fsmdef_media_t             *media;
+    boolean                    change_found = FALSE;
+    boolean                    check_for_change = FALSE;
+
+    change_found = gsmsdp_media_ip_changed(dcb_p);
+
+    /*
+     * check to see if media capability table has changed, by checking
+     * the ID.
+     */
+    if ((g_media_table.id != dcb_p->media_cap_tbl->id) || reset) {
+
+            /*
+             * capabilty table ID different or we are doing a reset for
+             * the full offer again, need to check for various changes.
+         * Update capabilities to match platform caps
+         */
+            check_for_change = TRUE;
+        }
+
+    /*
+     * Find any capability changes. The changes allowed are:
+     * 1) media entry is disabled (to be removed).
+     * 2) supported direction change.
+     * 3) new media entry is enabled (to be added) keep it the last one.
+     *
+     * Find a media to be removed first so that if there is another
+     * media line to add then there might be an unused a media line in the
+     * SDP to use.
+     */
+    if (check_for_change && gsmsdp_check_remove_local_sdp_media(dcb_p)) {
+        /* there were some media lines removed */
+        change_found = TRUE;
+    }
+
+    /*
+     * Find media lines that may have direction changes
+     */
+    if ( check_for_change &&
+         gsmsdp_check_direction_change_local_sdp_media(dcb_p, reset)) {
+        /* there were media lines that directions changes */
+        change_found = TRUE;
+    }
+
+    /*
+     * Reset all the existing media lines to have full codec etc.
+     * again if the caller requested.
+     */
+    if (reset) {
+        GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+            gsmsdp_reset_media(dcb_p, media, hold);
+        }
+    }
+
+    /*
+     * Find new media entries to be added.
+     */
+    if ( check_for_change && gsmsdp_check_add_local_sdp_media(dcb_p, hold)) {
+        change_found = TRUE;
+    }
+
+    if (change_found) {
+        GSM_DEBUG(DEB_L_C_F_PREFIX"media capability change found \n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+    }
+
+
+    /* report back any changes made */
+    return (change_found);
+}
+
+/*
+ * gsmsdp_reset_local_sdp_media
+ *
+ * Description:
+ *
+ * This function initializes the media portion of the local sdp. The following
+ * lines are initialized.
+ *
+ * m= line <media><port><transport><fmt list>
+ * a= session level line <media direction>
+ *
+ * Parameters:
+ *
+ * dcb_p - Pointer to DCB whose local SDP media is to be initialized
+ * media - Pointer to fsmdef_media_t for the media entry of the SDP.
+ *         If the value is NULL, it indicates all medie entries i the
+ *         dcb is reset.
+ * hold  - Boolean indicating if SDP is being initialized for sending hold
+ *
+ */
+void
+gsmsdp_reset_local_sdp_media (fsmdef_dcb_t *dcb_p, fsmdef_media_t *media,
+                              boolean hold)
+{
+    fsmdef_media_t *start_media, *end_media;
+
+    if (media == NULL) {
+        /* NULL value of the given media indicates for all media */
+        start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb_p);
+        end_media   = NULL; /* NULL means till the end of the list */
+    } else {
+        /* given media, uses the provided media */
+        start_media = media;
+        end_media   = media;
+    }
+
+    GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb_p) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            continue;
+        }
+
+        /*
+         * Reset media transport in preparation for hold or resume.
+         * It is possible that transport media may
+         * change from the current media transport (for SRTP re-offer
+         * to a different end point).
+         */
+        gsmsdp_reset_sdp_media_transport(dcb_p, dcb_p->sdp ? dcb_p->sdp->src_sdp : NULL,
+                                             media, hold);
+
+        gsmsdp_update_local_sdp_media(dcb_p, dcb_p->sdp, TRUE, media,
+                                          media->transport);
+
+
+        /*
+         * a= line
+         * If hold is being signaled, do not alter the media direction. This
+         * will be taken care of by the state machine handler function.
+         */
+        if (!hold) {
+            /*
+             * We are not locally held, set direction to the supported
+             * direction.
+             */
+            gsmsdp_set_local_sdp_direction(dcb_p, media,
+                                           media->support_direction);
+        }
+    }
+}
+
+/*
+ * gsmsdp_set_local_hold_sdp
+ *
+ * Description:
+ *
+ * Manipulates the local SDP of the specified DCB to indicate hold
+ * to the far end.
+ *
+ * Parameters:
+ *
+ * dcb_p - Pointer to the DCB whose SDP is to be manipulated.
+ * media - Pointer to the fsmdef_media_t for the current media entry.
+ *         If the value is NULL, it indicates all medie entries i the
+ *         dcb is reset.
+ */
+void
+gsmsdp_set_local_hold_sdp (fsmdef_dcb_t *dcb_p, fsmdef_media_t *media)
+{
+    int             old_style_hold = 0;
+    fsmdef_media_t *start_media, *end_media;
+
+    if (media == NULL) {
+        /* NULL value of the given media indicates for all media */
+        start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb_p);
+        end_media   = NULL; /* NULL means till the end of the list */
+    } else {
+        /* given media, uses the provided media */
+        start_media = media;
+        end_media   = media;
+    }
+
+    GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb_p) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            continue;
+        }
+        /*
+         * Check if configuration indicates that the old style hold should be
+         * signaled per RFC 2543. That is, c=0.0.0.0. Although
+         * 2543 does not speak to the SDP direction attribute, we set
+         * the direction to INACTIVE to be consistent with the connection
+         * address setting.
+         */
+        config_get_value(CFGID_2543_HOLD, &old_style_hold,
+                         sizeof(old_style_hold));
+        if (old_style_hold) {
+            gsmsdp_set_2543_hold_sdp(dcb_p, media->level);
+            gsmsdp_set_local_sdp_direction(dcb_p, media,
+                                           SDP_DIRECTION_INACTIVE);
+        } else {
+            /*
+             * RFC3264 states that hold is signaled by setting the media
+             * direction attribute to SENDONLY if in SENDRECV mode.
+             * INACTIVE if RECVONLY mode (mutual hold).
+             */
+            if (media->direction == SDP_DIRECTION_SENDRECV ||
+                media->direction == SDP_DIRECTION_SENDONLY) {
+                gsmsdp_set_local_sdp_direction(dcb_p, media,
+                                               SDP_DIRECTION_SENDONLY);
+            } else {
+                gsmsdp_set_local_sdp_direction(dcb_p, media,
+                                               SDP_DIRECTION_INACTIVE);
+            }
+        }
+    }
+}
+
+/*
+ * gsmsdp_set_local_resume_sdp
+ *
+ * Description:
+ *
+ * Manipulates the local SDP of the specified DCB to indicate hold
+ * to the far end.
+ *
+ * Parameters:
+ *
+ * dcb_p - Pointer to the DCB whose SDP is to be manipulated.
+ * media - Pointer to the fsmdef_media_t for the current media entry.
+ *
+ */
+void
+gsmsdp_set_local_resume_sdp (fsmdef_dcb_t *dcb_p, fsmdef_media_t *media)
+{
+    fsmdef_media_t *start_media, *end_media;
+
+    if (media == NULL) {
+        /* NULL value of the given media indicates for all media */
+        start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb_p);
+        end_media   = NULL; /* NULL means till the end of the list */
+    } else {
+        /* given media, uses the provided media */
+        start_media = media;
+        end_media   = media;
+    }
+
+    GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb_p) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            continue;
+        }
+        /*
+         * We are not locally held, set direction to the supported
+         * direction.
+         */
+        gsmsdp_set_local_sdp_direction(dcb_p, media, media->support_direction);
+    }
+}
+
+/*
+ * gsmsdp_encode_sdp
+ *
+ * Description:
+ * The function encodes SDP from the internal SDP representation
+ * to the SDP body to be sent out.
+ *
+ * Parameters:
+ * sdp_p    - pointer to the internal SDP info. block.
+ * msg_body - pointer to the msg body info. block.
+ *
+ * Returns:
+ * cc_causes_t to indicate failure or success.
+ */
+cc_causes_t
+gsmsdp_encode_sdp (cc_sdp_t *sdp_p, cc_msgbody_info_t *msg_body)
+{
+    char           *sdp_body;
+    cc_msgbody_t   *part;
+    uint32_t        body_length;
+
+    if (msg_body == NULL) {
+        return CC_CAUSE_ERROR;
+    }
+
+    /* Support single SDP encoding for now */
+    sdp_body = sipsdp_write_to_buf(sdp_p, &body_length);
+
+    if (sdp_body == NULL) {
+        return CC_CAUSE_ERROR;
+    } else if (body_length == 0) {
+        cpr_free(sdp_body);
+        return CC_CAUSE_ERROR;
+    }
+
+    /* Clear off the bodies info */
+    cc_initialize_msg_body_parts_info(msg_body);
+
+    /* Set up for one SDP entry */
+    msg_body->num_parts = 1;
+    msg_body->content_type = cc_content_type_SDP;
+    part = &msg_body->parts[0];
+    part->body = sdp_body;
+    part->body_length = body_length;
+    part->content_type = cc_content_type_SDP;
+    part->content_disposition.required_handling = FALSE;
+    part->content_disposition.disposition = cc_disposition_session;
+    part->content_id = NULL;
+    return CC_CAUSE_OK;
+}
+
+/*
+ * gsmsdp_encode_sdp_and_update_version
+ *
+ * Description:
+ * The function encodes SDP from the internal SDP representation
+ * to the SDP body to be sent out.  It also post-increments the owner
+ * version number to prepare for the next SDP to be sent out.
+ *
+ * Parameters:
+ * sdp_p    - pointer to DCB whose local SDP is to be encoded.
+ * msg_body - pointer to the msg body info. block.
+ *
+ * Returns:
+ * cc_causes_t to indicate failure or success.
+ */
+cc_causes_t
+gsmsdp_encode_sdp_and_update_version (fsmdef_dcb_t *dcb_p, cc_msgbody_info_t *msg_body)
+{
+    char version_str[GSMSDP_VERSION_STR_LEN];
+
+    snprintf(version_str, sizeof(version_str), "%d", dcb_p->src_sdp_version);
+
+    if ( dcb_p->sdp == NULL || dcb_p->sdp->src_sdp == NULL )
+    {
+       if ( CC_CAUSE_OK != gsmsdp_init_local_sdp(dcb_p->peerconnection,
+            &(dcb_p->sdp)) )
+       {
+               return CC_CAUSE_ERROR;
+       }
+    }
+    (void) sdp_set_owner_version(dcb_p->sdp->src_sdp, version_str);
+
+    if (gsmsdp_encode_sdp(dcb_p->sdp, msg_body) != CC_CAUSE_OK) {
+        return CC_CAUSE_ERROR;
+    }
+
+    dcb_p->src_sdp_version++;
+    return CC_CAUSE_OK;
+}
+
+/*
+ * gsmsdp_get_sdp
+ *
+ * Description:
+ * The function searches the SDP from the the all of msg. body parts.
+ * All body parts having the content type SDP will be stored at the
+ * given destination arrays. The number of SDP body are returned to
+ * indicate the number of SDP bodies found. The function searches
+ * the msg body in backward to order to form SDP in the destination array
+ * to be from the highest to the lowest preferences.
+ *
+ * Parameters:
+ * msg_body   - pointer to the incoming message body or cc_msgbody_info_t.
+ * part_array - pointer to pointer of cc_msgbody_t to store the
+ *              sorted order of the incoming SDP.
+ * max_part   - the maximum number of SDP can be written to the
+ *              part_array or the part_array size.
+ * Returns:
+ * The number of SDP parts found.
+ */
+static uint32_t
+gsmsdp_get_sdp_body (cc_msgbody_info_t *msg_body,
+                     cc_msgbody_t **part_array,
+                     uint32_t max_parts)
+{
+    uint32_t      i, count;
+    cc_msgbody_t *part;
+
+    if ((msg_body == NULL) || (msg_body->num_parts == 0)) {
+        /* No msg. body or no body parts in the msg. */
+        return (0);
+    }
+    /*
+     * Extract backward. The SDP are sent from the lowest
+     * preference to the highest preference. The highest
+     * preference will be extracted first into the given array
+     * so that when we negotiate, the SDP we will attempt to
+     * negotiate from the remote's highest to the lowest
+     * preferences.
+     */
+    count = 0;
+    part = &msg_body->parts[msg_body->num_parts - 1];
+    for (i = 0; (i < msg_body->num_parts) && (i < max_parts); i++) {
+        if (part->content_type == cc_content_type_SDP) {
+            /* Found an SDP, keep the pointer to the part */
+            *part_array = part; /* save pointer to SDP entry */
+            part_array++;       /* next entry                */
+            count++;
+        }
+        /* next one backward */
+        part--;
+    }
+    /* return the number of SDP bodies found */
+    return (count);
+}
+
+/*
+ * gsmsdp_realloc_dest_sdp
+ *
+ * Description:
+ * The function re-allocates the internal SDP info. block for the
+ * remote or destination SDP. If there the SDP info. or the
+ * SDP block does not exist, the new one is allocated. If SDP block
+ * exists, the current one is released and is replaced by a new one.
+ *
+ * Parameters:
+ * dcb_p - pointer to fsmdef_dcb_t.
+ *
+ * Returns:
+ * cc_causes_t to indicate failure or success.
+ */
+static cc_causes_t
+gsmsdp_realloc_dest_sdp (fsmdef_dcb_t *dcb_p)
+{
+    /* There are SDPs to process, prepare for parsing the SDP */
+    if (dcb_p->sdp == NULL) {
+        /* Create internal SDP information block with dest sdp block */
+        sipsdp_src_dest_create(dcb_p->peerconnection,
+            CCSIP_DEST_SDP_BIT, &dcb_p->sdp);
+    } else {
+        /*
+         * SDP info. block exists, remove the previously received
+         * remote or destination SDP and create a new one for
+         * the new SDP.
+         */
+        if (dcb_p->sdp->dest_sdp) {
+            sipsdp_src_dest_free(CCSIP_DEST_SDP_BIT, &dcb_p->sdp);
+        }
+        sipsdp_src_dest_create(dcb_p->peerconnection,
+            CCSIP_DEST_SDP_BIT, &dcb_p->sdp);
+    }
+
+    /* No SDP info block and parsed control block are available */
+    if ((dcb_p->sdp == NULL) || (dcb_p->sdp->dest_sdp == NULL)) {
+        /* Unable to create internal SDP structure to parse SDP. */
+        return CC_CAUSE_ERROR;
+    }
+    return CC_CAUSE_OK;
+}
+
+/*
+ * gsmsdp_negotiate_answer_sdp
+ *
+ * Description:
+ *
+ * Interface function used to negotiate an ANSWER SDP.
+ *
+ * Parameters:
+ *
+ * fcb_p - Pointer to the FCB containing the DCB whose local SDP is being negotiated.
+ * msg_body - Pointer to the cc_msgbody_info_t that contain the remote
+ *
+ */
+cc_causes_t
+gsmsdp_negotiate_answer_sdp (fsm_fcb_t *fcb_p, cc_msgbody_info_t *msg_body)
+{
+    static const char fname[] = "gsmsdp_negotiate_answer_sdp";
+    fsmdef_dcb_t *dcb_p = fcb_p->dcb;
+    cc_msgbody_t *sdp_bodies[CC_MAX_BODY_PARTS];
+    uint32_t      i, num_sdp_bodies;
+    cc_causes_t   status;
+    char         *sdp_body;
+
+    /* Get just the SDP bodies */
+    num_sdp_bodies = gsmsdp_get_sdp_body(msg_body, &sdp_bodies[0],
+                                         CC_MAX_BODY_PARTS);
+    GSM_DEBUG(DEB_F_PREFIX"\n",DEB_F_PREFIX_ARGS(GSM, fname));
+    if (num_sdp_bodies == 0) {
+        /*
+         * Clear the call - we don't have any remote SDP info!
+         */
+        return CC_CAUSE_ERROR;
+    }
+
+    /* There are SDPs to process, prepare for parsing the SDP */
+    if (gsmsdp_realloc_dest_sdp(dcb_p) != CC_CAUSE_OK) {
+        /* Unable to create internal SDP structure to parse SDP. */
+        return CC_CAUSE_ERROR;
+    }
+
+    /*
+     * Parse the SDP into internal structure,
+     * now just parse one
+     */
+    status = CC_CAUSE_ERROR;
+    for (i = 0; (i < num_sdp_bodies); i++) {
+        if ((sdp_bodies[i]->body != NULL) && (sdp_bodies[i]->body_length > 0)) {
+            /* Found a body */
+            sdp_body = sdp_bodies[i]->body;
+            if (sdp_parse(dcb_p->sdp->dest_sdp, &sdp_body,
+                          (uint16_t)sdp_bodies[i]->body_length)
+                    == SDP_SUCCESS) {
+                status = CC_CAUSE_OK;
+                break;
+            }
+        }
+    }
+    if (status != CC_CAUSE_OK) {
+        /* Error parsing SDP */
+        return status;
+    }
+
+    gsmsdp_set_remote_sdp(dcb_p, dcb_p->sdp);
+
+    status = gsmsdp_negotiate_media_lines(fcb_p, dcb_p->sdp, FALSE, FALSE, TRUE, TRUE);
+    GSM_DEBUG(DEB_F_PREFIX"returns with %d\n",DEB_F_PREFIX_ARGS(GSM, fname), status);
+    return (status);
+}
+
+
+
+
+/*
+ * gsmsdp_negotiate_offer_sdp
+ *
+ * Description:
+ *
+ * Interface function used to negotiate an OFFER SDP.
+ *
+ * Parameters:
+ *
+ * fcb_p - Pointer to the FCB containing the DCB whose local SDP is being negotiated.
+ * msg_body - Pointer to remote SDP body infostructure.
+ * init - Boolean indicating if the local SDP should be initialized as if this is the
+ *        first local SDP of this session.
+ *
+ */
+cc_causes_t
+gsmsdp_negotiate_offer_sdp (fsm_fcb_t *fcb_p,
+  cc_msgbody_info_t *msg_body, boolean init)
+{
+    cc_causes_t status;
+    fsmdef_dcb_t *dcb_p = fcb_p->dcb;
+
+    status = gsmsdp_process_offer_sdp(fcb_p, msg_body, init);
+    if (status != CC_CAUSE_OK)
+       return status;
+
+    /*
+     * If a new error code has been added to sdp processing please make sure
+     * the sip side is aware of it
+     */
+    status = gsmsdp_negotiate_media_lines(fcb_p, dcb_p->sdp, init, TRUE, FALSE, FALSE);
+    return (status);
+}
+
+
+/*
+ * gsmsdp_process_offer_sdp
+ *
+ * Description:
+ *
+ * Interface function used to process an OFFER SDP.
+ * Does not negotiate.
+ *
+ * Parameters:
+ *
+ * fcb_p - Pointer to the FCB containing the DCB whose local SDP is being negotiated.
+ * msg_body - Pointer to remote SDP body infostructure.
+ * init - Boolean indicating if the local SDP should be initialized as if this is the
+ *        first local SDP of this session.
+ *
+ */
+cc_causes_t
+gsmsdp_process_offer_sdp (fsm_fcb_t *fcb_p,
+  cc_msgbody_info_t *msg_body, boolean init)
+{
+    static const char fname[] = "gsmsdp_process_offer_sdp";
+    fsmdef_dcb_t *dcb_p = fcb_p->dcb;
+    cc_causes_t   status;
+    cc_msgbody_t *sdp_bodies[CC_MAX_BODY_PARTS];
+    uint32_t      i, num_sdp_bodies;
+    char         *sdp_body;
+
+    /* Get just the SDP bodies */
+    num_sdp_bodies = gsmsdp_get_sdp_body(msg_body, &sdp_bodies[0],
+                                         CC_MAX_BODY_PARTS);
+    GSM_DEBUG(DEB_L_C_F_PREFIX"Init is %d\n",
+        DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), init);
+    if (num_sdp_bodies == 0) {
+        /*
+         * No remote SDP. So we will offer in our response and receive far end
+         * answer in the ack. Only need to create local sdp if this is first offer
+         * of a session. Otherwise, we will send what we have.
+         */
+        if (init) {
+            if ( CC_CAUSE_OK != gsmsdp_create_local_sdp(dcb_p, FALSE, TRUE, TRUE, TRUE, TRUE)) {
+                return CC_CAUSE_ERROR;
+            }
+        } else {
+            /*
+             * Reset all media entries that we have to offer all capabilities
+             */
+           (void)gsmsdp_update_local_sdp_media_capability(dcb_p, TRUE, FALSE);
+        }
+        dcb_p->remote_sdp_in_ack = TRUE;
+        return CC_CAUSE_OK;
+    }
+
+    /* There are SDPs to process, prepare for parsing the SDP */
+    if (gsmsdp_realloc_dest_sdp(dcb_p) != CC_CAUSE_OK) {
+        /* Unable to create internal SDP structure to parse SDP. */
+        return CC_CAUSE_ERROR;
+    }
+
+    /*
+     * Parse the SDP into internal structure,
+     * now just parse one
+     */
+    status = CC_CAUSE_ERROR;
+    for (i = 0; (i < num_sdp_bodies); i++) {
+        if ((sdp_bodies[i]->body != NULL) && (sdp_bodies[i]->body_length > 0)) {
+            /* Found a body */
+            sdp_body = sdp_bodies[i]->body;
+            if (sdp_parse(dcb_p->sdp->dest_sdp, &sdp_body,
+                          (uint16_t)sdp_bodies[i]->body_length)
+                    == SDP_SUCCESS) {
+                status = CC_CAUSE_OK;
+                break;
+            }
+        }
+    }
+    if (status != CC_CAUSE_OK) {
+        /* Error parsing SDP */
+        return status;
+    }
+
+    if (init) {
+        (void)gsmsdp_init_local_sdp(dcb_p->peerconnection, &(dcb_p->sdp));
+        /* Note that there should not a previous version here as well */
+    }
+
+    gsmsdp_set_remote_sdp(dcb_p, dcb_p->sdp);
+
+    return (status);
+}
+
+/*
+ * gsmsdp_install_peer_ice_attributes
+ *
+ * Read ICE parameters from the SDP and set them into
+ * the ice engine. Check SESSION_LEVEL first then each media line.
+ *
+ * fcb_p - pointer to the fcb
+ *
+ */
+cc_causes_t
+gsmsdp_install_peer_ice_attributes(fsm_fcb_t *fcb_p)
+{
+    char            *ufrag;
+    char            *pwd;
+    char            **candidates;
+    int             candidate_ct;
+    sdp_result_e    sdp_res;
+    short           vcm_res;
+    fsmdef_dcb_t    *dcb_p = fcb_p->dcb;
+    cc_sdp_t        *sdp_p = dcb_p->sdp;
+    fsmdef_media_t  *media;
+    int             level;
+    short           result;
+
+    /* Tolerate missing ufrag/pwd here at the session level
+       because it might be at the media level */
+    sdp_res = sdp_attr_get_ice_attribute(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0,
+      SDP_ATTR_ICE_UFRAG, 1, &ufrag);
+    if (sdp_res != SDP_SUCCESS)
+      ufrag = NULL;
+
+    sdp_res = sdp_attr_get_ice_attribute(sdp_p->dest_sdp, SDP_SESSION_LEVEL, 0,
+      SDP_ATTR_ICE_PWD, 1, &pwd);
+    if (sdp_res != SDP_SUCCESS)
+      pwd = NULL;
+
+    if (ufrag && pwd) {
+        vcm_res = vcmSetIceSessionParams(dcb_p->peerconnection, ufrag, pwd);
+        if (vcm_res)
+            return (CC_CAUSE_ERROR);
+    }
+
+    /* Now process all the media lines */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+      if (!GSMSDP_MEDIA_ENABLED(media))
+        continue;
+
+      sdp_res = sdp_attr_get_ice_attribute(sdp_p->dest_sdp, media->level, 0,
+        SDP_ATTR_ICE_UFRAG, 1, &ufrag);
+      if (sdp_res != SDP_SUCCESS)
+        ufrag = NULL;
+
+      sdp_res = sdp_attr_get_ice_attribute(sdp_p->dest_sdp, media->level, 0,
+        SDP_ATTR_ICE_PWD, 1, &pwd);
+      if (sdp_res != SDP_SUCCESS)
+        pwd = NULL;
+
+      candidate_ct = 0;
+      candidates = NULL;
+      result = gsmsdp_get_ice_attributes (SDP_ATTR_ICE_CANDIDATE, media->level, sdp_p->dest_sdp,
+                                          &candidates, &candidate_ct);
+      if(!result)
+        return (CC_CAUSE_ERROR);
+
+      /* Set ICE parameters into ICE engine */
+
+      vcm_res = vcmSetIceMediaParams(dcb_p->peerconnection, media->level, ufrag, pwd,
+                                    candidates, candidate_ct);
+
+      /* Clean up */
+      if(candidates) {
+        int i;
+
+        for (i=0; i<candidate_ct; i++) {
+          if (candidates[i])
+            cpr_free(candidates[i]);
+        }
+        cpr_free(candidates);
+      }
+
+      if (vcm_res)
+        return (CC_CAUSE_ERROR);
+
+    }
+
+    return CC_CAUSE_OK;
+}
+
+/*
+ * gsmsdp_configure_dtls_data_attributes
+ *
+ * Read DTLS algorithm and digest from the SDP.
+ * If data is found at session level then that is used for each media stream
+ * else each media stream is set with its corresponding DTLS data from the remote SDP
+ *
+ * fcb_p - pointer to the fcb
+ *
+ */
+cc_causes_t
+gsmsdp_configure_dtls_data_attributes(fsm_fcb_t *fcb_p)
+{
+    char            *fingerprint = NULL;
+    char            *session_fingerprint = NULL;
+    sdp_result_e    sdp_res;
+    sdp_result_e    sdp_session_res;
+    short           vcm_res;
+    fsmdef_dcb_t    *dcb_p = fcb_p->dcb;
+    cc_sdp_t        *sdp_p = dcb_p->sdp;
+    fsmdef_media_t  *media;
+    int             level = SDP_SESSION_LEVEL;
+    short           result;
+    char           *token;
+    char            line_to_split[FSMDEF_MAX_DIGEST_ALG_LEN + FSMDEF_MAX_DIGEST_LEN + 2];
+    char           *delim = " ";
+    char            digest_alg[FSMDEF_MAX_DIGEST_ALG_LEN];
+    char            digest[FSMDEF_MAX_DIGEST_LEN];
+    char           *strtok_state;
+    cc_causes_t     cause = CC_CAUSE_OK;
+
+    /* First check for session level algorithm and key */
+    sdp_session_res = sdp_attr_get_dtls_fingerprint_attribute (sdp_p->dest_sdp, SDP_SESSION_LEVEL,
+                                      0, SDP_ATTR_DTLS_FINGERPRINT, 1, &session_fingerprint);
+
+    /* Now process all the media lines */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+        if (!GSMSDP_MEDIA_ENABLED(media))
+            continue;
+
+        /* check for media level algorithm and key */
+        sdp_res = sdp_attr_get_dtls_fingerprint_attribute (sdp_p->dest_sdp, media->level,
+                                    0, SDP_ATTR_DTLS_FINGERPRINT, 1, &fingerprint);
+
+        if (SDP_SUCCESS == sdp_res ) {
+            if (strlen(fingerprint) >= sizeof(line_to_split))
+                return CC_CAUSE_ERROR;
+            sstrncpy(line_to_split, fingerprint, sizeof(line_to_split));
+        } else if (SDP_SUCCESS == sdp_session_res) {
+            if (strlen(session_fingerprint) >= sizeof(line_to_split))
+                return CC_CAUSE_ERROR;
+            sstrncpy(line_to_split, session_fingerprint, sizeof(line_to_split));
+        } else {
+            cause = CC_CAUSE_ERROR;
+            continue;
+        }
+
+        if (SDP_SUCCESS == sdp_res || SDP_SUCCESS == sdp_session_res) {
+            if(!(token = PL_strtok_r(line_to_split, delim, &strtok_state)))
+                return CC_CAUSE_ERROR;
+
+            if (strlen(token) >= sizeof(digest_alg))
+                return CC_CAUSE_ERROR;
+
+            sstrncpy(digest_alg, token, sizeof(digest_alg));
+            if(!(token = PL_strtok_r(NULL, delim, &strtok_state)))
+                return CC_CAUSE_ERROR;
+
+            if (strlen(token) >= sizeof(digest))
+                return CC_CAUSE_ERROR;
+
+            sstrncpy(digest, token, sizeof(digest));
+
+            if (strlen(digest_alg) >= sizeof(media->negotiated_crypto.algorithm))
+                return CC_CAUSE_ERROR;
+
+            sstrncpy(media->negotiated_crypto.algorithm, digest_alg, sizeof(media->negotiated_crypto.algorithm));
+            if (strlen(media->negotiated_crypto.algorithm) == 0) {
+                return CC_CAUSE_ERROR;
+            }
+
+            if (strlen(digest) >= sizeof(media->negotiated_crypto.digest))
+                return CC_CAUSE_ERROR;
+
+            sstrncpy(media->negotiated_crypto.digest, digest, sizeof(media->negotiated_crypto.digest));
+            if (strlen(media->negotiated_crypto.digest) == 0) {
+                return CC_CAUSE_ERROR;
+            }
+
+            /* Here we have DTLS data */
+            cause = CC_CAUSE_OK;
+
+        } else {
+            GSM_DEBUG(DEB_F_PREFIX"DTLS attribute error\n",
+                                   DEB_F_PREFIX_ARGS(GSM, __FUNCTION__));
+            return CC_CAUSE_ERROR;
+        }
+    }
+
+    return cause;
+}
+
+/*
+ * gsmsdp_free
+ *
+ * Description:
+ * The function frees SDP resources that were allocated during the
+ * course of the call.
+ *
+ * Parameters:
+ * dcb_p    - pointer to fsmdef_dcb_t.
+ */
+void
+gsmsdp_free (fsmdef_dcb_t *dcb_p)
+{
+    if ((dcb_p != NULL) && (dcb_p->sdp != NULL)) {
+        sipsdp_free(&dcb_p->sdp);
+        dcb_p->sdp = NULL;
+    }
+}
+
+/*
+ * gsmsdp_sdp_differs_from_previous_sdp
+ *
+ * Description:
+ *
+ * Interface function used to compare newly received SDP to previously
+ * received SDP. Returns FALSE if attributes of interest are the same.
+ * Otherwise, returns TRUE.
+ *
+ * Parameters:
+ *
+ * rcv_only - If TRUE, check for receive port perspective.
+ * media    - Pointer to the fsmdef_media_t for the current media entry.
+ */
+boolean
+gsmsdp_sdp_differs_from_previous_sdp (boolean rcv_only, fsmdef_media_t *media)
+{
+    static const char fname[] = "gsmsdp_sdp_differs_from_previous_sdp";
+    char    prev_addr_str[MAX_IPADDR_STR_LEN];
+    char    dest_addr_str[MAX_IPADDR_STR_LEN];
+    int     i;
+
+    /* Consider attributes of interest for both directions */
+
+    if ((0 == media->num_payloads) || (0 == media->previous_sdp.num_payloads) ||
+        (media->num_payloads != media->previous_sdp.num_payloads)){
+        GSM_DEBUG(DEB_F_PREFIX"previous # payloads: %d new # payloads: %d\n",
+                  DEB_F_PREFIX_ARGS(GSM, fname),
+                  media->previous_sdp.num_payloads, media->num_payloads);
+    }
+
+    if (media->previous_sdp.avt_payload_type != media->avt_payload_type){
+        GSM_DEBUG(DEB_F_PREFIX"previous avt PT: %d new avt PT: %d\n",
+                  DEB_F_PREFIX_ARGS(GSM, fname),
+                  media->previous_sdp.avt_payload_type,
+                  media->avt_payload_type);
+        return TRUE;
+    }
+
+    for (i = 0; i < media->num_payloads; i++) {
+      if ((media->previous_sdp.payloads[i].remote_rtp_pt !=
+           media->payloads[i].remote_rtp_pt) ||
+          (media->previous_sdp.payloads[i].codec_type !=
+           media->payloads[i].codec_type)){
+          GSM_DEBUG(DEB_F_PREFIX"previous dynamic payload (PT) #%d: "
+                    "%d; new dynamic payload: %d\n",
+                    DEB_F_PREFIX_ARGS(GSM, fname), i,
+                    media->previous_sdp.payloads[i].remote_rtp_pt,
+                    media->payloads[i].remote_rtp_pt);
+          GSM_DEBUG(DEB_F_PREFIX"previous codec #%d: %d; new codec: %d\n",
+                    DEB_F_PREFIX_ARGS(GSM, fname), i,
+                    media->previous_sdp.payloads[i].codec_type,
+                    media->payloads[i].codec_type);
+          return TRUE;
+      }
+    }
+
+    /*
+     * Consider attributes of interest for transmit directions.
+     * If previous dest port is 0 then this is the first time
+     * we received sdp for comparison. We treat this situation
+     * as if addr and port did not change.
+     */
+    if ( (media->previous_sdp.dest_port != 0) && (rcv_only == FALSE)) {
+        if ((util_compare_ip(&(media->previous_sdp.dest_addr),
+                            &(media->dest_addr)) == FALSE) ||
+            (media->previous_sdp.dest_port != media->dest_port)) {
+        prev_addr_str[0] = '\0'; /* ensure valid string if convesion fails */
+        dest_addr_str[0] = '\0';
+        ipaddr2dotted(prev_addr_str, &media->previous_sdp.dest_addr);
+        ipaddr2dotted(dest_addr_str, &media->dest_addr);
+        GSM_DEBUG(DEB_F_PREFIX"previous address: %s new address: %s\n",
+                  DEB_F_PREFIX_ARGS(GSM, fname), prev_addr_str, dest_addr_str);
+        GSM_DEBUG(DEB_F_PREFIX"previous port: %d new port: %d\n",
+                  DEB_F_PREFIX_ARGS(GSM, fname), media->previous_sdp.dest_port, media->dest_port);
+            return TRUE;
+        } else if ( media->tias_bw != media->previous_sdp.tias_bw) {
+            GSM_DEBUG(DEB_F_PREFIX"previous bw: %d new bw: %d\n",
+                  DEB_F_PREFIX_ARGS(GSM, fname), media->previous_sdp.tias_bw, media->tias_bw);
+            return TRUE;
+        } else if ( media->profile_level != media->previous_sdp.profile_level) {
+            GSM_DEBUG(DEB_F_PREFIX"previous prof_level: %X new prof_level: %X\n",
+                  DEB_F_PREFIX_ARGS(GSM, fname), media->previous_sdp.profile_level, media->profile_level);
+            return TRUE;
+        }
+    }
+
+
+    /* Check crypto parameters if we are doing SRTP */
+    if (gsmsdp_crypto_params_change(rcv_only, media)) {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+
+/*
+ * gsmsdp_add_remote_stream
+ *
+ * Description:
+ *
+ * For each remote media stream add a track to the dcb for the
+ * current session.
+ *
+ * Parameters:
+ *
+ * idx   - Stream index
+ * pc_stream_id - stream id from vcm layer, will be set as stream id
+ *
+ * dcb_p - Pointer to the DCB whose SDP is to be manipulated.
+ * media - Pointer to the fsmdef_media_t for the current media entry.
+ */
+void gsmsdp_add_remote_stream(uint16_t idx, int pc_stream_id, fsmdef_dcb_t *dcb_p, fsmdef_media_t *media) {
+
+ /*
+  * This function is in its infancy, but when complete will create a list
+  * of streams, each with its list of tracks and associated data.
+  * Currently this just creates 1 track per 1 stream.
+  */
+
+  PR_ASSERT(idx < CC_MAX_STREAMS);
+
+  if (idx < CC_MAX_STREAMS) {
+    dcb_p->remote_media_stream_tbl->streams[idx].num_tracks = 1;
+    dcb_p->remote_media_stream_tbl->streams[idx].media_stream_id = pc_stream_id;
+    dcb_p->remote_media_stream_tbl->streams[idx].track[0].media_stream_track_id = idx+1;
+    dcb_p->remote_media_stream_tbl->streams[idx].track[0].video = (media->type == 0 ? FALSE : TRUE);
+  }
+}
+
+cc_causes_t
+gsmsdp_find_level_from_mid(fsmdef_dcb_t * dcb_p, const char * mid, uint16_t *level) {
+
+    fsmdef_media_t  *media;
+    u32              mid_id;
+    char             buf[5];
+
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+        if (!GSMSDP_MEDIA_ENABLED(media))
+            continue;
+
+        mid_id = sdp_attr_get_simple_u32(dcb_p->sdp->dest_sdp, SDP_ATTR_MID, media->level, 0, 1);
+        snprintf(buf, sizeof(buf), "%u", mid_id);
+        if (strcmp(mid, buf) == 0) {
+               *level = media->level;
+               return CC_CAUSE_OK;
+        }
+    }
+    return CC_CAUSE_VALUE_NOT_FOUND;
+}
diff --git a/libs/sipcc/core/gsm/gsm_sdp_crypto.c b/libs/sipcc/core/gsm/gsm_sdp_crypto.c
new file mode 100644 (file)
index 0000000..7ab7c79
--- /dev/null
@@ -0,0 +1,1914 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+#include <limits.h>
+
+#include "cpr_types.h"
+#include "cpr_rand.h"
+#include "sdp.h"
+#include "fsm.h"
+#include "gsm_sdp.h"
+#include "util_string.h"
+#include "lsm.h"
+#include "sip_interface_regmgr.h"
+#include "plat_api.h"
+
+static const char *gsmsdp_crypto_suite_name[SDP_SRTP_MAX_NUM_CRYPTO_SUITES] =
+{
+    "SDP_SRTP_UNKNOWN_CRYPTO_SUITE",
+    "SDP_SRTP_AES_CM_128_HMAC_SHA1_32",
+    "SDP_SRTP_AES_CM_128_HMAC_SHA1_80",
+    "SDP_SRTP_F8_128_HMAC_SHA1_80"
+};
+
+/*
+ * Cached random number pool. The size of the pool is for 10 key sets
+ * ahead. The number of cached keys are arbitrary picked to be large
+ * enough to sustain the number of basic calls.
+ */
+#define RAND_POOL_SIZE ((VCM_SRTP_MAX_KEY_SIZE+VCM_SRTP_MAX_SALT_SIZE)*10)
+#define RAND_REQ_LIMIT 256      /* limit random number request */
+static unsigned char rand_pool[RAND_POOL_SIZE]; /* random number pool */
+static int rand_pool_bytes = 0; /* current numbers in rand pool */
+
+/*
+ * Default key life time that phone supports,
+ * should be "2^48" but CCM does not support the value. Leave the
+ * life time to blank.
+ */
+#define GSMSDP_DEFALT_KEY_LIFETIME NULL
+
+/* Default algorithmID */
+#define GSMSDP_DEFAULT_ALGORITHM_ID VCM_AES_128_COUNTER
+
+/*
+ *  Function: gsmsdp_cache_crypto_keys
+ *
+ *  Parameters:
+ *      N/A
+ *
+ *  Description:
+ *      The function fills the crypto graphically random number pool
+ *  so that when SRTP key (and salt) is needed it is first obtained from
+ *  cached pool during the call for a better response to the real time
+ *  signalling event.
+ *
+ *  Returns:
+ *      N/A
+ */
+void
+gsmsdp_cache_crypto_keys (void)
+{
+    int number_to_fill;
+    int accumulate_bytes;
+    int bytes;
+
+    /*
+     * Only attempt to fill the cache when the pool needs to be
+     * filled and the phone is idle.
+     */
+    if ((rand_pool_bytes == RAND_POOL_SIZE) || !lsm_is_phone_idle()) {
+        return;
+    }
+
+    number_to_fill = RAND_POOL_SIZE - rand_pool_bytes;
+    accumulate_bytes = 0;
+
+    while (accumulate_bytes < number_to_fill) {
+        bytes = number_to_fill - accumulate_bytes;
+        /*
+         * Limit the request the number bytes for each request to
+         * crypto graphic random number generator to prevent
+         * error. There is a maximum limit to per request.
+         */
+        if (bytes > RAND_REQ_LIMIT) {
+            bytes = RAND_REQ_LIMIT;
+        }
+
+        if (platGenerateCryptoRand(&rand_pool[accumulate_bytes], &bytes)) {
+            accumulate_bytes += bytes;
+        } else {
+            /*
+             * Failed to get the crypto random number, uses the
+             * cpr_rand(), to keep the call going.
+             */
+            rand_pool[accumulate_bytes] = (uint8_t) (cpr_rand() & 0xff);
+            accumulate_bytes++;
+        }
+    }
+    rand_pool_bytes = RAND_POOL_SIZE;
+}
+
+/*
+ *  Function: gsmsdp_get_rand_from_cached_pool
+ *
+ *  Parameters:
+ *      dst_buf   - pointer to the unsigned char for the buffer to
+ *                  store random number obtained from the cached pool.
+ *      req_bytes - number of random number requested.
+ *
+ *  Description:
+ *      The function gets the random number from the cache pool.
+ *
+ *  Returns:
+ *      The number of random number obtained from the pool.
+ */
+static int
+gsmsdp_get_rand_from_cached_pool (unsigned char *dst_buf, int req_bytes)
+{
+    int bytes;
+
+    if (rand_pool_bytes == 0) {
+        /* the pool is empty */
+        return 0;
+    }
+
+    if (rand_pool_bytes >= req_bytes) {
+        /* There are enough bytes from the pool */
+        bytes = req_bytes;
+    } else {
+        /*
+         * pool does not have enough random bytes, give whatever
+         * remains in the pool
+         */
+        bytes = rand_pool_bytes;
+    }
+
+    memcpy(dst_buf, &rand_pool[RAND_POOL_SIZE - rand_pool_bytes], bytes);
+    rand_pool_bytes -= bytes;
+
+    /* Returns the actual number of bytes gotten from the pool */
+    return (bytes);
+}
+
+/*
+ *  Function: gsmsdp_generate_key
+ *
+ *  Description:
+ *      The function generates key and salt for SRTP.
+ *
+ *  Parameters:
+ *      algorithmID - algorithm ID
+ *      key         - pointer to vcm_crypto_key_t to store key output.
+ *
+ *  Returns:
+ *      None
+ */
+static void
+gsmsdp_generate_key (uint32_t algorithmID, vcm_crypto_key_t * key)
+{
+    int     accumulate_len, len, total_bytes, bytes_from_cache;
+    uint8_t random[sizeof(key->key) + sizeof(key->salt)];
+    uint8_t key_len, salt_len;
+
+    if (algorithmID == VCM_AES_128_COUNTER) {
+        key_len  = VCM_AES_128_COUNTER_KEY_SIZE;
+        salt_len = VCM_AES_128_COUNTER_SALT_SIZE;
+    } else {
+        /* Unsupported algorithm */
+        key_len  = VCM_SRTP_MAX_KEY_SIZE;
+        salt_len = VCM_SRTP_MAX_SALT_SIZE;
+    }
+
+    /*
+     * Request random bytes one time for both key and salt to avoid
+     * the overhead of platGenerateCryptoRand().
+     *
+     * The size of random byes should be based on the algorithmID
+     * used but since at this time only AES128 is used.
+     */
+    accumulate_len = 0;
+    total_bytes    = key_len + salt_len;
+
+    while (accumulate_len < total_bytes) {
+        len = total_bytes - accumulate_len;
+
+        /* Attempt to get random number from cached pool first */
+        bytes_from_cache =
+            gsmsdp_get_rand_from_cached_pool(&random[accumulate_len], len);
+        if (bytes_from_cache) {
+            /* There are some random number from the cache */
+            accumulate_len += bytes_from_cache;
+        } else {
+            /*
+             * The cached random number in the pool is empty.
+             *
+             * Attempt to get all of the random bytes in one request but
+             * if the number of random bytes is not enough then requesting
+             * until all bytes are accumulated.
+             */
+            if (len > RAND_REQ_LIMIT) {
+                len = RAND_REQ_LIMIT;
+            }
+            if (platGenerateCryptoRand(&random[accumulate_len], &len)) {
+                accumulate_len += len;
+            } else {
+                /*
+                 * Failed to get the crypto random number, uses the
+                 * cpr_rand(), to keep the call going.
+                 */
+                random[accumulate_len] = (uint8_t) (cpr_rand() & 0xff);
+                accumulate_len++;
+            }
+        }
+    }
+
+    /*
+     * Filled in key and salt from the random bytes generated.
+     */
+    key->key_len = key_len;
+    memcpy(&key->key[0], &random[0], key->key_len);
+
+    key->salt_len = salt_len;
+    memcpy(&key->salt[0], &random[key->key_len], key->salt_len);
+}
+
+/*
+ *  Function: gsmsdp_is_supported_crypto_suite
+ *
+ *  Description:
+ *      The function checks whether the given crypto suite from SDP
+ *      is supported or not.
+ *
+ *  Parameters:
+ *      crypto_suite - sdp_srtp_crypto_suite_t type to be checked
+ *
+ *  Returns:
+ *      TRUE  - the crypto suite is supported.
+ *      FALSE - the crypto suite is not supported.
+ */
+static boolean
+gsmsdp_is_supported_crypto_suite (sdp_srtp_crypto_suite_t crypto_suite)
+{
+    switch (crypto_suite) {
+    case SDP_SRTP_AES_CM_128_HMAC_SHA1_32:
+        /* These are the supported crypto suites */
+        return (TRUE);
+    default:
+        return (FALSE);
+    }
+}
+
+/*
+ *  Function: gsmsdp_is_valid_keysize
+ *
+ *  Description:
+ *      The function checks whether the given key size is valid.
+ *
+ *  Parameters:
+ *      crypto_suite - sdp_srtp_crypto_suite_t type
+ *      key_size     - key size to check
+ *
+ *  Returns:
+ *      TRUE  - the key size is valid
+ *      FALSE - the key size is not valid
+ */
+static boolean
+gsmsdp_is_valid_key_size (sdp_srtp_crypto_suite_t crypto_suite,
+                          unsigned char key_size)
+{
+    /*
+     * Make sure the size fits the maximum key size currently supported.
+     * This to make sure that when a longer key size is added but
+     * the maximum key size is not updated.
+     */
+    if (key_size > VCM_SRTP_MAX_KEY_SIZE) {
+        return (FALSE);
+    }
+
+    /* Check key size against crypto suite */
+    switch (crypto_suite) {
+    case SDP_SRTP_AES_CM_128_HMAC_SHA1_32:
+        if (key_size == VCM_AES_128_COUNTER_KEY_SIZE) {
+            return (TRUE);
+        }
+        break;
+
+    default:
+        break;
+    }
+    return (FALSE);
+}
+
+/*
+ *  Function: gsmsdp_is_valid_salt_size
+ *
+ *  Description:
+ *      The function checks whether the given salt size is valid.
+ *
+ *  Parameters:
+ *      crypto_suite - sdp_srtp_crypto_suite_t type
+ *      salt_size    - salt size to check
+ *
+ *  Returns:
+ *      TRUE  - the salt size is valid
+ *      FALSE - the salt size is not valid
+ */
+static boolean
+gsmsdp_is_valid_salt_size (sdp_srtp_crypto_suite_t crypto_suite,
+                           unsigned char salt_size)
+{
+    /*
+     * Make sure the size fits the maximum salt size currently supported.
+     * This to make sure that when a longer salt size is added but
+     * the maximum salt size is not updated.
+     */
+    if (salt_size > VCM_SRTP_MAX_SALT_SIZE) {
+        return (FALSE);
+    }
+
+    /* Check salt size against crypter suite */
+    switch (crypto_suite) {
+    case SDP_SRTP_AES_CM_128_HMAC_SHA1_32:
+        if (salt_size == VCM_AES_128_COUNTER_SALT_SIZE) {
+            return (TRUE);
+        }
+        break;
+
+    default:
+        break;
+    }
+    return (FALSE);
+}
+
+/*
+ *  Function: gsmdsp_cmp_key
+ *
+ *  Description:
+ *      The function compares 2 keys.
+ *
+ *  Parameters:
+ *      key1 - pointer to vcm_crypto_key_t
+ *      key2 - pointer to vcm_crypto_key_t
+ *
+ *  Returns:
+ *      TRUE  - the 2 keys are different
+ *      FALSE - the 2 keys are the same
+ */
+static boolean
+gsmdsp_cmp_key (vcm_crypto_key_t *key1, vcm_crypto_key_t *key2)
+{
+    if ((key1 == NULL) && (key2 != NULL)) {
+        /* No key 1 but has key 2 -> different */
+        return (TRUE);
+    }
+    if ((key1 != NULL) && (key2 == NULL)) {
+        /* Has key 1 but no key 2 -> different */
+        return (TRUE);
+    }
+    if ((key1 == NULL) && (key2 == NULL)) {
+        /* No key 1 and no key 2 -> same */
+        return (FALSE);
+    }
+    /*
+     * At this point pointer to key1 and key2 should not be NULL. Compare
+     * the key content
+     */
+    if ((key1->key_len != key2->key_len) || (key1->salt_len != key2->salt_len)) {
+        /* Key length or salt length are not the same */
+        return (TRUE);
+    }
+    if (key1->key_len != 0) {
+        if (memcmp(key1->key, key2->key, key1->key_len)) {
+            return (TRUE);
+        }
+    }
+    if (key1->salt_len != 0) {
+        if (memcmp(key1->salt, key2->salt, key1->salt_len)) {
+            return (TRUE);
+        }
+    }
+    /* Both keys are the same */
+    return (FALSE);
+}
+
+/*
+ *  Function: gsmsdp_is_supported_session_parm
+ *
+ *  Description:
+ *      The function checks to see whether the session parameters given
+ *      is supported or not.
+ *
+ *  Parameters:
+ *      session_parms - pointer to string of session parameters
+ *
+ *  Returns:
+ *      TRUE  - the session parameters are supported
+ *      FALSE - the session parameters are not supported
+ */
+static boolean
+gsmsdp_is_supported_session_parm (const char *session_parms)
+{
+    int         len, wsh;
+    const char *parm_ptr;
+    long strtol_result;
+    char *strtol_end;
+
+    if (session_parms == NULL) {
+        /* No session parameters, this is acceptable */
+        return (TRUE);
+    }
+    /*
+     * Only WSH is allowed even though the phone does not support it.
+     * The session param string can only have "WSH=nn" or (size of 6).
+     */
+    len = strlen(session_parms);
+    if (strcmp(session_parms, "WSH=") && (len == 6)) {
+        parm_ptr = &session_parms[sizeof("WSH=") - 1]; /* point the wsh value */
+
+        errno = 0;
+        strtol_result = strtol(parm_ptr, &strtol_end, 10);
+
+        /* minimum value of WSH is 64 */
+        if (errno || parm_ptr == strtol_end || strtol_result < 64 || strtol_result > INT_MAX) {
+            return FALSE;
+        }
+
+        return TRUE;
+    }
+    /* Other parameters are not supported */
+    return (FALSE);
+}
+
+/*
+ *  Function: gsmsdp_crypto_suite_to_algorithmID
+ *
+ *  Description:
+ *      The function converts the given crypto suite from SDP into
+ *      internal enumeration suitable for using with SRTP lib.
+ *
+ *  Parameters:
+ *      crypto_suite - sdp_srtp_crypto_suite_t type to converted into
+ *                     internal algorithm ID
+ *
+ *  Returns:
+ *      value VCM algorithmID
+ */
+static vcm_crypto_algorithmID
+gsmsdp_crypto_suite_to_algorithmID (sdp_srtp_crypto_suite_t crypto_suite)
+{
+    /* Convert the supported crypto suite into the algorithm ID */
+    switch (crypto_suite) {
+    case SDP_SRTP_AES_CM_128_HMAC_SHA1_32:
+        return (VCM_AES_128_COUNTER);
+    default:
+        return (VCM_INVLID_ALGORITM_ID);
+    }
+}
+
+/*
+ *  Function: gsmsdp_algorithmID_to_crypto_suite
+ *
+ *  Description:
+ *      The function converts the given crypto suite from SDP into
+ *      internal enumeration suitable for using with SRTP lib.
+ *
+ *  Parameters:
+ *      algorithmID - algorithm ID to be converted into crypto suite
+ *
+ *  Returns:
+ *      crypto suite that is corresponding to the internal algorithmID
+ */
+static sdp_srtp_crypto_suite_t
+gsmsdp_algorithmID_to_crypto_suite (vcm_crypto_algorithmID algorithmID)
+{
+    /* Convert the supported crypto suite into the crypto suite */
+    switch (algorithmID) {
+    case VCM_AES_128_COUNTER:
+        return (SDP_SRTP_AES_CM_128_HMAC_SHA1_32);
+    default:
+        return (SDP_SRTP_UNKNOWN_CRYPTO_SUITE);
+    }
+}
+
+
+/*
+ *  Function: gsmsdp_crypto_suite_string
+ *
+ *  Description:
+ *      The function converts crypto suite to string name.
+ *
+ *  Parameters:
+ *      crypto_suite - sdp_srtp_crypto_suite_t
+ *
+ *  Returns:
+ *      string constant of the corresponding crypto suite
+ */
+static const char *
+gsmsdp_crypto_suite_string (sdp_srtp_crypto_suite_t crypto_suite)
+{
+    if (crypto_suite >= SDP_SRTP_MAX_NUM_CRYPTO_SUITES) {
+        return (gsmsdp_crypto_suite_name[SDP_SRTP_UNKNOWN_CRYPTO_SUITE]);
+    }
+    return (gsmsdp_crypto_suite_name[crypto_suite]);
+}
+
+/*
+ *  Function: gsmsdp_get_key_from_sdp
+ *
+ *  Description:
+ *      The function checks the key from the SDP.
+ *
+ *  Parameters:
+ *      dcb_p    - pointer to the DCB.
+ *      sdp_p    - pointer to remote's SDP (void)
+ *      level    - the media level of the SDP of the media line.
+ *      inst_num - instance number of the crypto attribute.
+ *      key_st   - pointer to fsmdef_crypto_key_t for the key to be returned.
+ *                 If pointer is NULL, the function acts as key validation.
+ *
+ *  Returns:
+ *      TRUE  - The key is valid.
+ *      FALSE - The key is not valid.
+ */
+static boolean
+gsmsdp_get_key_from_sdp (fsmdef_dcb_t *dcb_p, void *sdp_p, uint16_t level,
+                         uint16_t inst_num, vcm_crypto_key_t *key_st)
+{
+    const char    *fname = "gsmsdp_get_key_from_sdp";
+    unsigned char  key_size;
+    unsigned char  salt_size;
+    const char    *key, *salt;
+    sdp_srtp_crypto_suite_t crypto_suite;
+
+    /* Get the crypto suite */
+    crypto_suite = sdp_attr_get_sdescriptions_crypto_suite(sdp_p,
+                                                           level, 0, inst_num);
+
+    /* Get key */
+    key_size = sdp_attr_get_sdescriptions_key_size(sdp_p, level, 0, inst_num);
+    if (!gsmsdp_is_valid_key_size(crypto_suite, key_size)) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "SDP has invalid key size %d at media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname, key_size, level);
+        return (FALSE);
+    }
+
+    key = sdp_attr_get_sdescriptions_key(sdp_p, level, 0, inst_num);
+    if (key == NULL) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "SDP has no key at media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname, level);
+        return (FALSE);
+    }
+
+    /* Get salt */
+    salt_size = sdp_attr_get_sdescriptions_salt_size(sdp_p, level, 0, inst_num);
+    if (!gsmsdp_is_valid_salt_size(crypto_suite, salt_size)) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "SDP has invalid salt size %d at media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname, salt_size, level);
+        return (FALSE);
+    }
+    salt = sdp_attr_get_sdescriptions_salt(sdp_p, level, 0, inst_num);
+    if (salt == NULL) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "SDP has no salt at media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname, level);
+        return (FALSE);
+    }
+
+    /*
+     * Key and salt have been sanity check including their sizes,
+     * copy key and salt if the caller requests for it.
+     */
+    if (key_st != NULL) {
+        /* The caller needs to copy of the key */
+        key_st->key_len = key_size;
+        memcpy(key_st->key, key, key_size);
+        key_st->salt_len = salt_size;
+        memcpy(key_st->salt, salt, salt_size);
+    }
+    return (TRUE);
+}
+
+/*
+ *  Function: gsmsdp_local_offer_srtp
+ *
+ *  Description:
+ *      The function checks whether the local (phone) SRTP has been offered.
+ *
+ *  Parameters:
+ *      media - pointer to the fsmdef_media_t for the media entry.
+ *
+ *  Returns:
+ *      TRUE  - local has offered SRTP
+ *      FALSE - local has not offered SRTP
+ */
+static boolean
+gsmsdp_local_offer_srtp (fsmdef_media_t *media)
+{
+    /*
+     * Use the local tag as an indication whether we have offered,
+     * SRTP or not.
+     */
+    if (media->local_crypto.tag == SDP_INVALID_VALUE) {
+        return (FALSE);
+    }
+    return (TRUE);
+}
+
+/*
+ *  Function: gsmsdp_clear_local_offer_srtp
+ *
+ *  Description:
+ *      The function clears the flag to mark that the local SRTP has
+ *  been offered.
+ *
+ *  Parameters:
+ *      media - pointer to the fsmdef_media_t for the media entry.
+ *
+ *  Returns:
+ *      none
+ */
+static void
+gsmsdp_clear_local_offer_srtp (fsmdef_media_t *media)
+{
+    /*
+     * Use the local tag as an indication whether we have offered,
+     * SRTP or not.
+     */
+    media->local_crypto.tag = SDP_INVALID_VALUE;
+}
+
+/*
+ *  Function: gsmsdp_check_common_crypto_param
+ *
+ *  Description:
+ *      The function checks common crypto parameters that can be shared
+ *  by selecting an offer and checking the answer SDP.
+ *
+ *  Parameters:
+ *      dcb_p     - pointer to the DCB whose local SDP is to be updated
+ *      cc_sdp_p  - pointer to cc_sdp_t structure
+ *      level     - the media level of the SDP of the media line
+ *      inst      - crypto attribute instance number of the
+ *                  answer crypto line
+ *      offer     - boolean indicates offer if it is set to TRUE
+ *
+ *  Returns:
+ *      TRUE  - when crypto parameters are valid and acceptable
+ *      FALSE - when crypto parameters are not valid or not acceptable
+ */
+static boolean
+gsmsdp_check_common_crypto_param (fsmdef_dcb_t *dcb_p, void *sdp_p,
+                                  uint16_t level, uint16_t inst, boolean offer)
+{
+    const char *fname = "gsmsdp_check_common_crypto_param";
+    const char *dir_str; /* direction string */
+    const char *session_parms;
+    const char *mki_value = NULL;
+    uint16_t    mki_length = 0;
+
+    if (offer) {
+        dir_str = "Offer";      /* the caller is working on an offer SDP */
+    } else {
+        dir_str = "Answer";     /* the caller is working on an answer SDP */
+    }
+
+    /* Validate the key */
+    if (!gsmsdp_get_key_from_sdp(dcb_p, sdp_p, level, inst, NULL)) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "%s SDP has invalid key at media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname, dir_str, level);
+        return (FALSE);
+    }
+
+    /* Check MKI, we do not support it */
+    if (sdp_attr_get_sdescriptions_mki(sdp_p, level, 0, inst,
+                                       &mki_value, &mki_length)
+        != SDP_SUCCESS) {
+        /* something is wrong with decoding MKI field, do not use it */
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "Fail to obtain MKI from %s SDP at media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname, dir_str, level);
+        return (FALSE);
+    }
+    if (mki_length) {
+        /* this crypto line has MKI specified, we do not support it */
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "%s SDP has MKI %d (not supported) at media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname, dir_str, mki_length,
+                  level);
+        return (FALSE);
+    }
+
+    /* Check session parameters */
+    session_parms = sdp_attr_get_sdescriptions_session_params(sdp_p,
+                                                              level, 0, inst);
+    if (!gsmsdp_is_supported_session_parm(session_parms)) {
+        /* some unsupported session parameters are found */
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "%s SDP has unsupported session param at media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname, dir_str, level);
+        return (FALSE);
+    }
+
+    /* This is good and acceptable one */
+    return (TRUE);
+}
+
+/*
+ *  Function: gsmsdp_select_offer_crypto
+ *
+ *  Description:
+ *      Select remote the crypto attributes from the remote offered SDP.
+ *
+ *  Parameters:
+ *      dcb_p       - pointer to the DCB
+ *      sdp_p       - pointer to remote's SDP (void)
+ *      level       - the media level of the SDP of the media line
+ *      crypto_inst - pointer to crypto attribute instance number of the
+ *                    selected crypto line
+ *
+ *  Returns:
+ *      TRUE  - when matching crypto parameters are found.
+ *      FALSE - when matching crypto parameters are not found.
+ */
+static boolean
+gsmsdp_select_offer_crypto (fsmdef_dcb_t *dcb_p, void *sdp_p, uint16_t level,
+                            uint16_t *crypto_inst)
+{
+    const char  *fname = "gsmsdp_select_offer_crypto";
+    uint16_t     num_attrs = 0; /* number of attributes */
+    uint16_t     attr;
+    int32_t      tag;
+    sdp_attr_e   attr_type;
+    sdp_result_e rc;
+    sdp_srtp_crypto_suite_t crypto_suite;
+
+    /* Find the number of attributes at this level of media line */
+    rc = sdp_get_total_attrs(sdp_p, level, 0, &num_attrs);
+    if (rc != SDP_SUCCESS) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "Failed finding attributes for media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname, level);
+        return (FALSE);
+    }
+
+    /*
+     * Search all crypto attributes to find a valid one that phone
+     * can support.
+     *
+     * The search starts from the first attributes to the higher. The
+     * attributes are listed from the most preferred attribute to the
+     * least in the SDP.
+     */
+    for (attr = 1; attr <= num_attrs; attr++) {
+        rc = sdp_get_attr_type(sdp_p, level, 0, attr, &attr_type, crypto_inst);
+        if ((rc != SDP_SUCCESS) || (attr_type != SDP_ATTR_SDESCRIPTIONS)) {
+            /* Can't not get the attribute or it is not sdescription */
+            continue;
+        }
+        /*
+         * Found a crypto attribute, try to match the supported crypto suite
+         */
+        crypto_suite = sdp_attr_get_sdescriptions_crypto_suite(sdp_p,
+                                                               level, 0,
+                                                               *crypto_inst);
+        if (!gsmsdp_is_supported_crypto_suite(crypto_suite)) {
+            /* this one is we can not support, look further */
+            continue;
+        }
+        /* get crypto tag */
+        tag = sdp_attr_get_sdescriptions_tag(sdp_p, level, 0, *crypto_inst);
+        if (tag == SDP_INVALID_VALUE) {
+            /* no tag associated with this crypto attribute */
+            continue;
+        }
+
+        /* Check common crypto parameters */
+        if (!gsmsdp_check_common_crypto_param(dcb_p, sdp_p, level,
+                                              *crypto_inst, TRUE)) {
+            /* Some thing is wrong with the common crypto parameters */
+            continue;
+        }
+
+        /* Found a good crypto attribute to use */
+        return (TRUE);
+    }
+
+    GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+              "Failed finding supported crypto attribute for media level %d\n",
+              dcb_p->line, dcb_p->call_id, fname, level);
+    return (FALSE);
+}
+
+/*
+ *  Function: gsmsdp_check_answer_crypto_param
+ *
+ *  Description:
+ *      The function processes the answer crypto attributes.
+ *
+ *  Parameters:
+ *      dcb_p       - pointer to the DCB whose local SDP is to be updated
+ *      cc_sdp_p    - pointer to cc_sdp_t structure
+ *      media       - pointer to the media for the SDP of the media line
+ *      crypto_inst - pointer to crypto attribute instance number of the
+ *                    answer crypto line.
+ *
+ *  Returns:
+ *      TRUE  - when matching crypto parameters are found
+ *      FALSE - when matching crypto parameters are not found
+ */
+static boolean
+gsmsdp_check_answer_crypto_param (fsmdef_dcb_t *dcb_p, cc_sdp_t * cc_sdp_p,
+                                  fsmdef_media_t *media, uint16_t *crypto_inst)
+{
+    const char     *fname = "gsmsdp_check_answer_crypto_param";
+    uint16_t        num_attrs = 0; /* number of attributes */
+    uint16_t        attr;
+    uint16_t        num_crypto_attr = 0;
+    int32_t         dest_crypto_tag, offered_tag;
+    sdp_attr_e      attr_type;
+    sdp_result_e    rc;
+    sdp_srtp_crypto_suite_t crypto_suite;
+    vcm_crypto_algorithmID algorithmID;
+    void           *dest_sdp = cc_sdp_p->dest_sdp;
+    uint16_t        temp_inst, inst = 0;
+    uint16_t        level;
+
+    level        = media->level;
+    /* Clear the crypto instance */
+    *crypto_inst = 0;
+
+    /* Find the number of attributes at this level of media line */
+    if (sdp_get_total_attrs(dest_sdp, level, 0, &num_attrs) != SDP_SUCCESS) {
+        GSM_DEBUG(DEB_L_C_F_PREFIX
+                  "Failed finding attributes for media level %d\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), level);
+        return (FALSE);
+    }
+
+    /*
+     * Check to make sure that there is only crypto attribute in the
+     * answer SDP.
+     */
+    for (attr = 1, num_crypto_attr = 0; attr <= num_attrs; attr++) {
+        rc = sdp_get_attr_type(dest_sdp, level, 0, attr, &attr_type,
+                               &temp_inst);
+        if ((rc == SDP_SUCCESS) && (attr_type == SDP_ATTR_SDESCRIPTIONS)) {
+            num_crypto_attr++;
+            inst = temp_inst;
+        }
+    }
+    if (num_crypto_attr != 1) {
+        /* The remote answers with zero or more than one crypto attribute */
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "Answer SDP contains invalid number of"
+                  " crypto attributes %d for media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname,
+                                 num_crypto_attr, level);
+        return (FALSE);
+    }
+
+    /*
+     * Check to make sure that the tag in the answer SDP is one of the
+     * offered SDP.
+     * Note: At this time of implementation there is only one crypto
+     *       line sent out. When more than one cypher suites are supported,
+     *       then the answer tag should be used to find the corresponding
+     *       local crypto parameter.
+     */
+    dest_crypto_tag = sdp_attr_get_sdescriptions_tag(dest_sdp, level, 0, inst);
+
+    /*
+     * Selecting tag, if we have made an offer and have not received an answer
+     * before this one then tag to match is from the offered tag otherwise
+     * use the negotiated tag to match. The later case can occur as the
+     * following:
+     * phone  ---  INVITE+SDP  ---->  CCM
+     *       <---  100         -----
+     *       <---  183+SDP     -----
+     *       <---  200+SDP     -----
+     * The first 183+SDP will conclude the offer/answer. The 200+SDP
+     * is also an answer which also contains negotiated tag.
+     */
+    if (gsmsdp_local_offer_srtp(media)) {
+        /*
+         * We have made an offered and has not received an answer before
+         * this one, use the tag from the local crypto.
+         */
+        offered_tag = media->local_crypto.tag;
+        algorithmID = media->local_crypto.algorithmID;
+    } else {
+        /* offer/answer has complete, use negotiated crypto */
+        offered_tag = media->negotiated_crypto.tag;
+        algorithmID = media->negotiated_crypto.algorithmID;
+    }
+    if (dest_crypto_tag != offered_tag) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "Answer SDP contains wrong tag %d vs %d"
+                  " for the media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname,
+                                 dest_crypto_tag, offered_tag, level);
+        return (FALSE);
+    }
+
+    /* Check make sure that crypto suite is the same one that is offered */
+    crypto_suite = sdp_attr_get_sdescriptions_crypto_suite(dest_sdp, level,
+                                                           0, inst);
+    if (gsmsdp_crypto_suite_to_algorithmID(crypto_suite) != algorithmID) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "Answer SDP mismatch crypto suite %s at media level %d\n",
+                  dcb_p->line, dcb_p->call_id, fname,
+                  gsmsdp_crypto_suite_string(crypto_suite), level);
+        return (FALSE);
+    }
+
+    /* Check common crypto parameters */
+    if (!gsmsdp_check_common_crypto_param(dcb_p, dest_sdp, level,
+                                          inst, FALSE)) {
+        /* Some thing is wrong with the common crypto parameters */
+        return (FALSE);
+    }
+
+    *crypto_inst = inst;
+    return (TRUE);
+}
+
+/*
+ *
+ *  Function: gsmsdp_negotiate_offer_crypto
+ *
+ *  Description:
+ *      The function handles crypto parameters negotiation for
+ *      an offer SDP.
+ *
+ *  Parameters:
+ *     dcb_p       - pointer to the DCB whose local SDP is to be updated
+ *     cc_sdp_p    - pointer to cc_sdp_t structure
+ *     media       - pointer to fsmdef_media_t where the media transport is.
+ *     crypto_inst - pointer to crypto attribute instance number of the
+ *                   selected crypto line
+ *
+ *  Returns:
+ *      transport - sdp_transport_e for RTP or SRTP or invalid
+ */
+static sdp_transport_e
+gsmsdp_negotiate_offer_crypto (fsmdef_dcb_t *dcb_p, cc_sdp_t *cc_sdp_p,
+                               fsmdef_media_t *media, uint16_t *crypto_inst, uint16 dest_level)
+{
+    sdp_transport_e remote_transport;
+    sdp_transport_e negotiated_transport = SDP_TRANSPORT_INVALID;
+    void           *sdp_p = cc_sdp_p->dest_sdp;
+    int            sdpmode = 0;
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    *crypto_inst     = 0;
+    remote_transport = sdp_get_media_transport(sdp_p, dest_level);
+
+    /* negotiate media transport */
+    switch (remote_transport) {
+    case SDP_TRANSPORT_RTPAVP:
+        /* Remote offers RTP for media transport, negotiated transport to RTP */
+        negotiated_transport = SDP_TRANSPORT_RTPAVP;
+        break;
+
+    case SDP_TRANSPORT_RTPSAVP:
+
+        /* Remote offer SRTP for media transport */
+        if (((sip_regmgr_get_sec_level(dcb_p->line) == ENCRYPTED) &&
+            FSM_CHK_FLAGS(media->flags, FSM_MEDIA_F_SUPPORT_SECURITY)) || sdpmode) {
+            /* The signalling with this line is encrypted, try to use SRTP */
+            if (gsmsdp_select_offer_crypto(dcb_p, sdp_p, dest_level, crypto_inst)) {
+                /* Found a suitable crypto line from the remote offer */
+                negotiated_transport = SDP_TRANSPORT_RTPSAVP;
+            }
+        }
+
+        if (negotiated_transport == SDP_TRANSPORT_INVALID) {
+            /*
+             * Unable to find a suitable crypto or the signaling is
+             * not secure. Fall back to RTP if fallback is enabled.
+             */
+            if (sip_regmgr_srtp_fallback_enabled(dcb_p->line)) {
+                negotiated_transport = SDP_TRANSPORT_RTPAVP;
+            }
+        }
+        break;
+
+    case SDP_TRANSPORT_RTPSAVPF:
+        /* Remote offers Extended SRTP for media transport */
+        negotiated_transport = SDP_TRANSPORT_RTPSAVPF;
+        break;
+
+    case SDP_TRANSPORT_SCTPDTLS:
+        negotiated_transport = SDP_TRANSPORT_SCTPDTLS;
+        break;
+
+    default:
+        /* Unknown */
+        break;
+    }
+    return (negotiated_transport);
+}
+
+/*
+ *
+ *  Function: gsmsdp_negotiate_answer_crypto
+ *
+ *  Description:
+ *      The function handles crypto parameters negotiation for
+ *      an answer SDP.
+ *
+ *  Parameters:
+ *     dcb_p       - pointer to the DCB whose local SDP is to be updated
+ *     cc_sdp_p    - pointer to cc_sdp_t structure
+ *     media       - pointer to fsmdef_media_t where the media transport is.
+ *     crypto_inst - pointer to crypto attribute instance number of the
+ *                   answer crypto line
+ *
+ *  Returns:
+ *      transport - sdp_transport_e for RTP or SRTP or invalid
+ */
+static sdp_transport_e
+gsmsdp_negotiate_answer_crypto (fsmdef_dcb_t *dcb_p, cc_sdp_t *cc_sdp_p,
+                                fsmdef_media_t *media, uint16_t *crypto_inst)
+{
+    const char     *fname = "gsmsdp_check_answer_crypto";
+    sdp_transport_e remote_transport, local_transport;
+    sdp_transport_e negotiated_transport = SDP_TRANSPORT_INVALID;
+    uint16_t        level;
+
+    level            = media->level;
+    *crypto_inst     = 0;
+    remote_transport = sdp_get_media_transport(cc_sdp_p->dest_sdp, level);
+    local_transport  = sdp_get_media_transport(cc_sdp_p->src_sdp, level);
+    GSM_DEBUG(GSM_F_PREFIX "remote transport %d\n", fname, remote_transport);
+    GSM_DEBUG(GSM_F_PREFIX "local transport %d\n", fname, local_transport);
+
+    /* negotiate media transport */
+    switch (remote_transport) {
+    case SDP_TRANSPORT_RTPAVP:
+        /* Remote answer with RTP */
+        if (local_transport == SDP_TRANSPORT_RTPSAVP) {
+            if (sip_regmgr_srtp_fallback_enabled(dcb_p->line)) {
+                /* Fall back allows, falls back to RTP */
+                negotiated_transport = SDP_TRANSPORT_RTPAVP;
+            }
+        } else {
+            /* local and remote are using RTP */
+            negotiated_transport = SDP_TRANSPORT_RTPAVP;
+        }
+        break;
+
+    case SDP_TRANSPORT_RTPSAVP:
+        GSM_DEBUG(GSM_F_PREFIX "remote SAVP case\n", fname);
+        /* Remote answer with SRTP */
+        if (local_transport == SDP_TRANSPORT_RTPSAVP) {
+        GSM_DEBUG(GSM_F_PREFIX "local SAVP case\n", fname);
+            /* Remote and local media transport are using SRTP */
+            if (gsmsdp_check_answer_crypto_param(dcb_p, cc_sdp_p, media,
+                                                 crypto_inst)) {
+                /* Remote's answer crypto parameters are ok */
+                negotiated_transport = SDP_TRANSPORT_RTPSAVP;
+                GSM_DEBUG(GSM_F_PREFIX "crypto params verified\n", fname);
+            }
+        } else {
+            /* we offered RTP but remote comes back with SRTP, fail */
+        }
+        break;
+
+    case SDP_TRANSPORT_RTPSAVPF:
+        negotiated_transport = SDP_TRANSPORT_RTPSAVPF;
+        break;
+
+    case SDP_TRANSPORT_SCTPDTLS:
+        negotiated_transport = SDP_TRANSPORT_SCTPDTLS;
+        break;
+
+    default:
+        /* Unknown */
+        break;
+    }
+    GSM_DEBUG(GSM_F_PREFIX "negotiated transport %d\n", fname, negotiated_transport);
+    return (negotiated_transport);
+}
+
+/*
+ *
+ *  Function: gsmsdp_negotiate_media_transport
+ *
+ *  Description:
+ *      The function is a wrapper for negotiation RTP or SRTP for offer
+ *      or answer SDP. The negotiated crypto attributes instance number
+ *      will be returned via pointer crypto_inst.
+ *
+ *  Parameters:
+ *     dcb_p       - pointer to the DCB whose local SDP is to be updated.
+ *     cc_sdp_p    - pointer to cc_sdp_t structure that contains remote SDP.
+ *     offer       - boolean indicates the remote SDP is an offer SDP.
+ *     media       - pointer to fsmdef_media_t where the media transport is.
+ *     crypto_inst - pointer to crypto attribute instance number of the
+ *                   selected crypto line.
+ *
+ *  Returns:
+ *      transport - sdp_transport_e for RTP or SRTP or invalid
+ */
+sdp_transport_e
+gsmsdp_negotiate_media_transport (fsmdef_dcb_t *dcb_p, cc_sdp_t *cc_sdp_p,
+                                  boolean offer, fsmdef_media_t *media,
+                                  uint16_t *crypto_inst, uint16 level)
+{
+    sdp_transport_e transport;
+
+    /* negotiate media transport based on offer or answer from the remote */
+    if (offer) {
+        transport = gsmsdp_negotiate_offer_crypto(dcb_p, cc_sdp_p, media,
+                                                  crypto_inst, level);
+    } else {
+        transport = gsmsdp_negotiate_answer_crypto(dcb_p, cc_sdp_p, media,
+                                                   crypto_inst);
+    }
+    return (transport);
+}
+
+/*
+ *
+ *  Function: gsmsdp_add_single_crypto_attr
+ *
+ *  Description:
+ *      The function adds a single crypto attributes to the SDP.
+ *
+ *  Parameters:
+ *     cc_sdp_p     - pointer to SDP
+ *     level        - the media level of the SDP where the media transport is
+ *     crypto_suite - crypto suite
+ *     key          - pointer to SRTP key (fsmdef_crypto_key_t)
+ *     lifetime     - pointer to string const for the life time
+ *
+ *  Returns:
+ *      sdp_result_e
+ */
+static sdp_result_e
+gsmsdp_add_single_crypto_attr (void *sdp_p, uint16_t level, int32_t tag,
+                               sdp_srtp_crypto_suite_t crypto_suite,
+                               vcm_crypto_key_t * key, char *lifetime)
+{
+    sdp_result_e rc;
+    uint16       inst_num;
+
+    if (key == NULL) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /*
+     * Add crypto attributes tag to the  SDP, the tag is using
+     * from the remote tag which should have been negotiated.
+     */
+    rc = sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_SDESCRIPTIONS, &inst_num);
+    if (rc != SDP_SUCCESS) {
+        return (rc);
+    }
+
+    rc = sdp_attr_set_sdescriptions_tag(sdp_p, level, 0, inst_num, tag);
+    if (rc != SDP_SUCCESS) {
+        return (rc);
+    }
+
+    /* Add crypto suite */
+    rc = sdp_attr_set_sdescriptions_crypto_suite(sdp_p, level, 0, inst_num,
+                                                 crypto_suite);
+    if (rc != SDP_SUCCESS) {
+        return (rc);
+    }
+
+    /* Add local key */
+    rc = sdp_attr_set_sdescriptions_key(sdp_p, level, 0, inst_num,
+                                        (char *) key->key);
+    if (rc != SDP_SUCCESS) {
+        return (rc);
+    }
+
+    rc = sdp_attr_set_sdescriptions_key_size(sdp_p, level, 0, inst_num,
+                                             key->key_len);
+    if (rc != SDP_SUCCESS) {
+        return (rc);
+    }
+
+    /* Add salt */
+    rc = sdp_attr_set_sdescriptions_salt(sdp_p, level, 0, inst_num,
+                                         (char *) key->salt);
+    if (rc != SDP_SUCCESS) {
+        return (rc);
+    }
+
+    rc = sdp_attr_set_sdescriptions_salt_size(sdp_p, level, 0, inst_num,
+                                              key->salt_len);
+    if (rc != SDP_SUCCESS) {
+        return (rc);
+    }
+
+    if (lifetime != NULL) {
+        rc = sdp_attr_set_sdescriptions_lifetime(sdp_p, level, 0, inst_num,
+                                                 lifetime);
+    }
+    return (rc);
+}
+
+/*
+ *
+ *  Function: gsmsdp_add_all_crypto_lines
+ *
+ *  Parameters:
+ *
+ *      dcb_p  - pointer to the DCB whose local SDP is to be updated.
+ *      sdp_p  - pointer to SDP.
+ *      media  - pointer to fsmdef_media_t where the media transport is.
+ *
+ *  Description:
+ *      The function adds all crypto lines to the SDP.
+ *
+ *  Returns:
+ *      N/A.
+ */
+static void
+gsmsdp_add_all_crypto_lines (fsmdef_dcb_t *dcb_p, void *sdp_p,
+                             fsmdef_media_t *media)
+{
+    const char *fname = "gsmsdp_add_all_crypto_lines";
+    sdp_srtp_crypto_suite_t crypto_suite;
+
+    /*
+     * Add all crypto lines, currently there is only one crypto
+     * line to add.
+     */
+    media->local_crypto.tag = 1;
+    media->local_crypto.algorithmID = GSMSDP_DEFAULT_ALGORITHM_ID;
+
+    /* Generate key */
+    gsmsdp_generate_key(media->local_crypto.algorithmID,
+                        &media->local_crypto.key);
+
+    /* Get the crypto suite based on the algorithm ID */
+    crypto_suite =
+        gsmsdp_algorithmID_to_crypto_suite(media->local_crypto.algorithmID);
+    if (gsmsdp_add_single_crypto_attr(sdp_p, media->level,
+            media->local_crypto.tag, crypto_suite, &media->local_crypto.key,
+            GSMSDP_DEFALT_KEY_LIFETIME) != SDP_SUCCESS) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "Failed to add crypto attributes\n",
+                  dcb_p->line, dcb_p->call_id, fname);
+    }
+}
+
+/*
+ *  Function: gsmsdp_init_crypto_context
+ *
+ *  Description:
+ *      Initializes crypto context.
+ *
+ *  Parameters:
+ *      media - pointer to the fsmdef_media_t for the media entry.
+ *
+ *  Returns:
+ *      none
+ */
+static void
+gsmsdp_init_crypto_context (fsmdef_media_t *media)
+{
+    /* initialize local crypto parameters */
+    media->local_crypto.tag = SDP_INVALID_VALUE;
+    media->local_crypto.algorithmID = VCM_NO_ENCRYPTION;
+    media->local_crypto.key.key_len  = 0;
+    media->local_crypto.key.salt_len = 0;
+
+    /* Initialized negotiated crypto parameter */
+    media->negotiated_crypto.tag = SDP_INVALID_VALUE;
+    media->negotiated_crypto.algorithmID = VCM_NO_ENCRYPTION;
+    media->negotiated_crypto.tx_key.key_len  = 0;
+    media->negotiated_crypto.tx_key.salt_len = 0;
+    media->negotiated_crypto.rx_key.key_len  = 0;
+    media->negotiated_crypto.rx_key.salt_len = 0;
+
+    media->negotiated_crypto.flags = 0;
+}
+
+/*
+ *  Function: gsmsdp_init_sdp_media_transport
+ *
+ *  Description:
+ *      The prepares or initializes crypto context for a fresh negotiation.
+ *
+ *  Parameters:
+ *      dcb_p    - pointer to the fsmdef_dcb_t
+ *      sdp      - pointer to SDP (void)
+ *      media    - pointer to the fsmdef_media_t for the media entry.
+ *
+ *  Returns:
+ *      N/A
+ */
+void
+gsmsdp_init_sdp_media_transport (fsmdef_dcb_t *dcb_p, void *sdp_p,
+                                 fsmdef_media_t *media)
+{
+    int  rtpsavpf = 0;
+    int  sdpmode = 0;
+
+    /* Initialize crypto context */
+    gsmsdp_init_crypto_context(media);
+
+    config_get_value(CFGID_RTPSAVPF, &rtpsavpf, sizeof(rtpsavpf));
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    if (SDP_MEDIA_APPLICATION == media->type) {
+        media->transport = SDP_TRANSPORT_SCTPDTLS;
+    } else if (rtpsavpf) {
+        media->transport = SDP_TRANSPORT_RTPSAVPF;
+    } else if (sdpmode) {
+        media->transport = SDP_TRANSPORT_RTPSAVP;
+    } else     if ((sip_regmgr_get_sec_level(dcb_p->line) != ENCRYPTED) ||
+        (!FSM_CHK_FLAGS(media->flags, FSM_MEDIA_F_SUPPORT_SECURITY))) {
+        /*
+         * The signaling is not encrypted or this media can not support
+         * security.
+         */
+        media->transport = SDP_TRANSPORT_RTPAVP;
+    } else {
+        media->transport = SDP_TRANSPORT_RTPSAVP;
+    }
+}
+
+/*
+ *  Function: gsmsdp_reset_sdp_media_transport
+ *
+ *  Description:
+ *      The function resets media transport during mid call such as resume.
+ *      The media transport may change from the current negotiated media.
+ *
+ *  Parameters:
+ *      dcb_p    - pointer to the fsmdef_dcb_t
+ *      sdp      - pointer to SDP (void)
+ *      media    - pointer to fsmdef_media_t where the media transport is.
+ *      hold     - indicates call is being hold
+ *
+ *  Returns:
+ *      N/A.
+ */
+void
+gsmsdp_reset_sdp_media_transport (fsmdef_dcb_t *dcb_p, void *sdp_p,
+                                  fsmdef_media_t *media, boolean hold)
+{
+    if (hold) {
+        /* We are sending out hold, uses the same transport as negotiated */
+    } else {
+        /* Resuming a call */
+        if ((sip_regmgr_get_cc_mode(dcb_p->line) == REG_MODE_CCM)) {
+            /*
+             * In CCM mode, try to resume with full SRTP offer again
+             * if the current signalling is encrypted. In CCM mode,
+             * CCM will assist in crypto negotiation and fall back to
+             * RTP if necessary. This is useful when resuming a different
+             * end point and the new end point support SRTP even when the
+             * current media is not SRTP. For an example, when holding
+             * a call that was transferred to a new end point which
+             * it may be able to support SRTP.
+             */
+            gsmsdp_init_sdp_media_transport(dcb_p,
+                                            dcb_p->sdp ? dcb_p->sdp->src_sdp : NULL,
+                                            media);
+        } else {
+            /*
+             * In non CCM mode, we do not know whether the new end
+             * point supports SRTP fall back and no assistance from
+             * CCM in negotiating of the crypto parameter, play safe by
+             * resuming with the same media transport.
+             */
+        }
+    }
+}
+
+/*
+ *  Function: gsmsdp_set_media_transport_for_option
+ *
+ *  Parameters:
+ *      sdp   - pointer to SDP (void).
+ *      level - the media level of the SDP where the media transport is.
+ *
+ *  Description:
+ *      The function sets media transport for OPTION message.
+ *
+ *  Returns:
+ *      N/A.
+ */
+void
+gsmsdp_set_media_transport_for_option (void *sdp_p, uint16_t level)
+{
+    const char      *fname = "gsmsdp_set_media_transport_for_option";
+    uint32_t         algorithmID;
+    vcm_crypto_key_t key;
+    sdp_srtp_crypto_suite_t crypto_suite;
+
+
+    /* Use line 1 to determine whether to have RTP or SRTP */
+    if (sip_regmgr_get_sec_level(1) != ENCRYPTED) {
+        /* line one is none secure, use RTP */
+        (void) sdp_set_media_transport(sdp_p, level, SDP_TRANSPORT_RTPAVP);
+        return;
+    }
+
+    /* Advertise SRTP with default attributes */
+    (void) sdp_set_media_transport(sdp_p, level, SDP_TRANSPORT_RTPSAVP);
+
+    /* Generate dummy key */
+    crypto_suite = SDP_SRTP_AES_CM_128_HMAC_SHA1_32;
+    algorithmID = gsmsdp_crypto_suite_to_algorithmID(crypto_suite);
+    gsmsdp_generate_key(algorithmID, &key);
+
+    /* Add crypto attributes */
+    if (gsmsdp_add_single_crypto_attr(sdp_p, level, 1, crypto_suite, &key,
+            GSMSDP_DEFALT_KEY_LIFETIME) != SDP_SUCCESS) {
+        GSM_DEBUG_ERROR(GSM_F_PREFIX
+                  "Failed to add crypto attributes\n",
+                  fname);
+    }
+}
+
+/*
+ *  Function: gsmsdp_update_local_sdp_media_transport
+ *
+ *  Description:
+ *      The function updates the crypto attributes to the local SDP.
+ *
+ *  Parameters:
+ *      dcb_p - pointer to the fsmdef_dcb_t
+ *      sdp   - pointer to SDP (void)
+ *      media - pointer to fsmdef_media_t where the media transport is.
+ *      sdp_transport_e - current media transport
+ *      all   - indicates that the update for new offer SDP
+ *
+ *  Returns:
+ *      none
+ */
+void
+gsmsdp_update_local_sdp_media_transport (fsmdef_dcb_t *dcb_p, void *sdp_p,
+                                         fsmdef_media_t *media,
+                                         sdp_transport_e transport, boolean all)
+{
+    const char *fname = "gsmsdp_update_local_sdp_media_transport";
+    sdp_srtp_crypto_suite_t crypto_suite;
+    uint16_t level;
+
+    level = media->level;
+    /* Get the current transport before delete the media line */
+    if (transport == SDP_TRANSPORT_INVALID) {
+        /*
+         * The transport is not specified, get it from the negotiated
+         * transport. This condition can occur when receive initial
+         * offered SDP.
+         */
+        transport = media->transport;
+    }
+
+    /*
+     * set media transport in local SDP only first time so that we remember what we offered.
+     */
+    if (sdp_get_media_transport(sdp_p, level) == SDP_TRANSPORT_INVALID) {
+        (void) sdp_set_media_transport(sdp_p, level, transport);
+    }
+
+    if (transport != SDP_TRANSPORT_RTPSAVP) {
+        /*
+         * transport is not SRTP, if this is the fall back to RTP then
+         * the existing crypto lines should have been removed by the
+         * media transport negotiation and thus no code is added here.
+         * The reason for this is not to call to the remove all crypto
+         * line all the time i.e. only removed them when related to
+         * SRTP media transport.
+         */
+        return;
+    }
+
+    /* Add crypto attributes to local SDP. */
+    if (all || (media->negotiated_crypto.tag == SDP_INVALID_VALUE)) {
+        /* This is new offer or mid call media change occurs ,
+         * or could be the result of sending full offer during mid-call invite
+         * without SDP (delayed media invite) as well
+         */
+        if (media->negotiated_crypto.tag == SDP_INVALID_VALUE) {
+            /*
+             * This is the first time or fresh offering add all crypto
+             * lines.
+             */
+            gsmsdp_add_all_crypto_lines(dcb_p, sdp_p, media);
+            return;
+        } else {
+            /* Fall through below and use existing crypto parameters */
+        }
+    }
+
+    /*
+     * tag and algorithm ID are from the negotiated crypto parameters.
+     */
+    crypto_suite =
+        gsmsdp_algorithmID_to_crypto_suite(
+            media->negotiated_crypto.algorithmID);
+    if (gsmsdp_add_single_crypto_attr(sdp_p, level,
+                                      media->negotiated_crypto.tag,
+                                      crypto_suite,
+                                      &media->negotiated_crypto.tx_key,
+                                      GSMSDP_DEFALT_KEY_LIFETIME)
+            != SDP_SUCCESS) {
+        GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX
+                  "Failed to add crypto attributes\n",
+                  dcb_p->line, dcb_p->call_id, fname);
+    }
+}
+
+/*
+ *  Function: gsmsdp_update_crypto_transmit_key
+ *
+ *  Description:
+ *      The function updates transmit key for SRTP session.
+ *
+ *  Parameters:
+ *      dcb_p         - pointer to the fsmdef_dcb_t
+ *      media         - pointer to fsmdef_media_t where the media transport is.
+ *      offer         - boolean indicates it is an offer
+ *      initial_offer - boolean indicates it is an initial offer
+ *      direction     - new offered media direction
+ *
+ *  Returns:
+ *      none
+ */
+void
+gsmsdp_update_crypto_transmit_key (fsmdef_dcb_t *dcb_p,
+                                   fsmdef_media_t *media,
+                                   boolean offer,
+                                   boolean initial_offer,
+                                   sdp_direction_e direction)
+{
+    const char *fname = "gsmsdp_update_crypto_transmit_key";
+    boolean     generate_key = FALSE;
+
+    if (media->transport != SDP_TRANSPORT_RTPSAVP) {
+        return;
+    }
+
+    if (initial_offer || offer) {
+        /* This is an offer SDP, see if a new key needs to be generated */
+        if (initial_offer) {
+            /* An initial offer always needs new key */
+            generate_key = TRUE;
+        } else if ((util_compare_ip(&(media->previous_sdp.dest_addr),
+                                &(media->dest_addr)) == FALSE) &&
+                   media->dest_addr.type != CPR_IP_ADDR_INVALID) {
+            //Todo IPv6: IPv6 does not support 0.0.0.0 hold.
+
+            GSM_DEBUG(DEB_L_C_F_PREFIX
+                      "Received offer with dest. address changes\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+            /*
+             * This is just an offer and the destination address changes to
+             * is not 0.0.0.0 (hold) address, new key may be needed. There
+             * is a scenario where we get a delay media INVITE during the
+             * resume call (from the CCM for an example) and we have sent
+             * out 200 OK (offer SDP) with crypto parameters. Upon receiving
+             * ACK with SDP, the SIP stack in this notify GSM as a
+             * FEATURE MEDIA to GSM.  GSM treats this as an offer SDP.
+             * This code path is entered with offer flag set where it should
+             * be an answer to our 200 OK offer early on. Check  for this
+             * condition to see if we have an offer sent prior and if so
+             * do not generate new key if we already sent an offer out
+             * and has not get and answer SDP. Otherwise we will be generating
+             * a new key and do not sent out the key to the remote end which
+             * result in wrong local transmit key is being used.
+             */
+            if (gsmsdp_local_offer_srtp(media)) {
+                /*
+                 * We have sent out an offer but has not got an answer,
+                 * use the already generated key
+                 */
+                media->negotiated_crypto.tx_key = media->local_crypto.key;
+                GSM_DEBUG(DEB_L_C_F_PREFIX
+                          "Local offered SDP has been sent, use offered key\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+            } else {
+                generate_key = TRUE;
+            }
+        } else if ((media->direction == SDP_DIRECTION_INACTIVE) &&
+                   (direction != SDP_DIRECTION_INACTIVE)) {
+            if (!gsmsdp_local_offer_srtp(media)) {
+                /*
+                 * Received an offer that changes the direction from
+                 * inactive to some thing other than inactive, then
+                 * generate a new key. The phone could be transferred
+                 * to a new endpoint without destination address changes.
+                 * This is possible if there is a passthrough MTP in
+                 * between. The remote end point may be changed
+                 * behind MTP but from this phone the destination addr.
+                 * stays the same.
+                 */
+                generate_key = TRUE;
+                GSM_DEBUG(DEB_L_C_F_PREFIX
+                          "Received direction changes from inactive\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+            }
+        } else if (media->negotiated_crypto.tx_key.key_len == 0) {
+            if (gsmsdp_local_offer_srtp(media)) {
+                /*
+                 * This an answer to our offer sent similar to
+                 * the address change scenario above (delayed media, we
+                 * sent SDP in 200OK and got SDP in ACK but GSM treats
+                 * SDP in ACK case as an offer rather than an answer).
+                 * Use the key in the offered SDP.
+                 */
+                media->negotiated_crypto.tx_key = media->local_crypto.key;
+                GSM_DEBUG(DEB_L_C_F_PREFIX
+                          "Local offered SDP has been sent, use offered key\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+            } else {
+                /*
+                 * Do not have negotiated transmit key.
+                 *
+                 * The scenario that this can occur is when the
+                 * call transition from RTP to SRTP in mid-call.
+                 */
+                generate_key = TRUE;
+                GSM_DEBUG(DEB_L_C_F_PREFIX
+                          "Received offer but no tx key, generate new key\n",
+                          DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+            }
+        } else {
+            /* No need to generate new key */
+        }
+        if (generate_key) {
+            /*
+             * This is an initial offer or mid-call offer and
+             * some conditions changes , generate a new transmit key.
+             */
+            gsmsdp_generate_key(media->negotiated_crypto.algorithmID,
+                                &media->negotiated_crypto.tx_key);
+            GSM_DEBUG(DEB_L_C_F_PREFIX
+                      "Generate tx key\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+            media->negotiated_crypto.flags |= FSMDEF_CRYPTO_TX_CHANGE;
+        }
+    } else {
+        /* This is an answer to our offer, set the tx key to the local key
+         *
+         * Note that when adding support to offer multiple crypto suite to
+         * the remote end, we need to select the corresponding local offer
+         * SDP from the matching tag. At this point, we only support one
+         * crypto to offer and just get to that entry
+         */
+        if (gsmdsp_cmp_key(&media->local_crypto.key,
+                           &media->negotiated_crypto.tx_key)) {
+            media->negotiated_crypto.flags |= FSMDEF_CRYPTO_TX_CHANGE;
+            GSM_DEBUG(DEB_L_C_F_PREFIX
+                      "tx key changes in answered SDP\n",
+                      DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+        }
+        media->negotiated_crypto.tx_key = media->local_crypto.key; /* tx key */
+    }
+    /* Complete offer/answer, clear condition that we have made an offered */
+    gsmsdp_clear_local_offer_srtp(media);
+}
+
+/*
+ *  Function: gsmsdp_update_negotiated_transport
+ *
+ *  Description:
+ *      The function updates the transport and crypto parameters after the
+ *      all negotiated parameters has been done.
+ *
+ *  Parameters:
+ *      dcb_p - pointer to the fsmdef_dcb_t
+ *      sdp   - pointer to cc_sdp_t structure
+ *      level - pointer to fsmdef_media_t where the media transport is.
+ *      crypto_inst     - pointer to crypto attribute instance number of the
+ *                        selected crypto line
+ *      sdp_transport_e - negotiated media transport
+ *
+ *  Returns:
+ *      none
+ */
+void
+gsmsdp_update_negotiated_transport (fsmdef_dcb_t *dcb_p,
+                                    cc_sdp_t *cc_sdp_p,
+                                    fsmdef_media_t *media,
+                                    uint16_t crypto_inst,
+                                    sdp_transport_e transport,
+                                    uint16 dest_level)
+{
+    const char *fname = "gsmsdp_update_negotiated_transport";
+    sdp_srtp_crypto_suite_t crypto_suite;
+    void                   *dest_sdp = cc_sdp_p->dest_sdp;
+    vcm_crypto_algorithmID  algorithmID;
+    vcm_crypto_key_t        key;
+    uint16_t                level;
+
+    level = dest_level;
+    /*
+     * Also detect changes of the crypto parameters for Tx and Rx.
+     * It is done here to avoid adding last crypto parameters for Tx and
+     * Rx into the dcb structure since the parameters include key which
+     * are rather long. This minimize the dcb's increasing.
+     */
+    /* reset changes flags */
+    media->negotiated_crypto.flags &= ~(FSMDEF_CRYPTO_TX_CHANGE |
+                                        FSMDEF_CRYPTO_RX_CHANGE);
+    /*
+     * Detect the transport change between RTP to SRTP or ignore
+     * the first time transition from invalid transport. This
+     * prevents the change bits to be set for first time in RTP
+     * connection and causes the RX to be closed unnecessary.
+     */
+    if ((media->transport != SDP_TRANSPORT_INVALID) &&
+        (transport != media->transport)) {
+        /* We could fallback to RTP or resume from RTP to SRTP */
+        media->negotiated_crypto.flags |= (FSMDEF_CRYPTO_TX_CHANGE |
+                                           FSMDEF_CRYPTO_RX_CHANGE);
+        GSM_DEBUG(DEB_L_C_F_PREFIX
+                  "SDP media transport changed to %d\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), transport);
+    }
+    /* Update the negotiated media transport */
+    media->transport = transport;
+
+    if (media->transport != SDP_TRANSPORT_RTPSAVP) {
+        /*
+         * negotiate media transport to RTP, clear local offer SDP
+         * condition.
+         */
+        GSM_DEBUG(DEB_L_C_F_PREFIX
+                  "SDP media transport is RTP\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+        return;
+    }
+
+    GSM_DEBUG(DEB_L_C_F_PREFIX "SDP media transport is SRTP\n",
+              DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+
+    /*
+     * Get the crypto parameters from the remote's SDP,
+     * the parameters should already be validated during the
+     * media transport negotiation.  Update the negotiated crypto parameter.
+     */
+    crypto_suite = sdp_attr_get_sdescriptions_crypto_suite(dest_sdp, level,
+                                                           0, crypto_inst);
+
+    /* Save the negotiated algorithm ID */
+    algorithmID = gsmsdp_crypto_suite_to_algorithmID(crypto_suite);
+    if (algorithmID != media->negotiated_crypto.algorithmID) {
+        media->negotiated_crypto.flags |= (FSMDEF_CRYPTO_TX_CHANGE |
+                                           FSMDEF_CRYPTO_RX_CHANGE);
+        GSM_DEBUG(DEB_L_C_F_PREFIX "SDP algorithm ID change to %d\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname), algorithmID);
+    }
+    media->negotiated_crypto.algorithmID = algorithmID;
+
+    /* Save the negotiated crypto line tag */
+    media->negotiated_crypto.tag = sdp_attr_get_sdescriptions_tag(dest_sdp,
+                                                                  level, 0,
+                                                                  crypto_inst);
+
+    /* Get the remote's key */
+    (void) gsmsdp_get_key_from_sdp(dcb_p, dest_sdp, level, crypto_inst, &key);
+    if (gsmdsp_cmp_key(&key, &media->negotiated_crypto.rx_key)) {
+        media->negotiated_crypto.flags |= FSMDEF_CRYPTO_RX_CHANGE;
+        GSM_DEBUG(DEB_L_C_F_PREFIX "SDP rx key changes\n",
+                  DEB_L_C_F_PREFIX_ARGS(GSM, dcb_p->line, dcb_p->call_id, fname));
+    }
+    media->negotiated_crypto.rx_key = key;
+}
+
+/*
+ *  Function: gsmsdp_is_crypto_ready
+ *
+ *  Parameters:
+ *      media - pointer to fsmdef_media_t where the media transport is.
+ *      rx    - boolean indicates receiver.
+ *
+ *  Description:
+ *      The function returns to the caller whether receiver/transmitter is
+ * ready for open.
+ *
+ *  Returns:
+ *      TRUE  - when the receiver is ready to received.
+ *      FALSE - when the receive is not ready.
+ */
+boolean
+gsmsdp_is_crypto_ready (fsmdef_media_t *media, boolean rx)
+{
+    /*
+     * If we offered SRTP then we need to wait for the negotiated key before
+     * allowing the Rx/Tx to be opened.  If we did not offer (we received
+     * an offered SDP instead then we should have key negotiated).
+     */
+    if (media->transport == SDP_TRANSPORT_RTPAVP || media->transport == SDP_TRANSPORT_RTPSAVPF) {
+        return (TRUE);
+    }
+
+    /*
+     * Local has offered SRTP, check to see if the key is available.
+     */
+    if (rx) {
+        if (media->negotiated_crypto.rx_key.key_len == 0) {
+            /* Have not received remote's crypto parameter yet, can't open Rx */
+            return (FALSE);
+        }
+    } else {
+        if (media->negotiated_crypto.tx_key.key_len == 0) {
+            /* Have not received remote's crypto parameter yet, can't open Tx */
+            return (FALSE);
+        }
+    }
+    /* Have remote's key */
+    return (TRUE);
+}
+
+/*
+ *  Function: gsmsdp_is_media_encrypted
+ *
+ *  Description:
+ *      The function returns to the caller whether the media is
+ *      encrypted or not.
+ *
+ *  Parameters:
+ *      dcb_p - pointer to the fsmdef_dcb_t.
+ *
+ *  Returns:
+ *      TRUE  - when the media is encrypted.
+ *      FALSE - when the media is not encrypted.
+ */
+boolean
+gsmsdp_is_media_encrypted (fsmdef_dcb_t *dcb_p)
+{
+    fsmdef_media_t *media;
+    uint8_t num_encrypted;
+
+    if (dcb_p == NULL) {
+        return(FALSE);
+    }
+    num_encrypted = 0;
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            continue;
+        }
+
+        if (media->transport == SDP_TRANSPORT_RTPSAVP || media->transport == SDP_TRANSPORT_RTPSAVPF) {
+            num_encrypted++;
+        }
+    }
+
+    if ((num_encrypted == 0) ||
+        (num_encrypted != GSMSDP_MEDIA_COUNT(dcb_p))) {
+        /*
+         * the call does not have any media that is encrypted or
+         * there are some medias that are not encrypted. This is
+         * considered as non secure leg.
+         */
+        return (FALSE);
+    }
+    return (TRUE);
+}
+
+/*
+ *  Function: gsmsdp_crypto_params_change
+ *
+ *  Description:
+ *      The function returns to the caller whether the crypto parameters
+ *      change from the previous SDP or not.
+ *
+ *  Parameters:
+ *      rcv_only - If TRUE, check for receive port perspective.
+ *      media    - pointer to fsmdef_media_t where the media transport is.
+ *
+ *  Returns:
+ *      TRUE  - when crypto parameters changed
+ *      FALSE - when crypto parameters did not change
+ */
+boolean
+gsmsdp_crypto_params_change (boolean rcv_only, fsmdef_media_t *media)
+{
+    if (rcv_only) {
+        if (media->negotiated_crypto.flags & FSMDEF_CRYPTO_RX_CHANGE) {
+            return (TRUE);
+        }
+    } else {
+        if (media->negotiated_crypto.flags & FSMDEF_CRYPTO_TX_CHANGE) {
+            return (TRUE);
+        }
+    }
+    return (FALSE);
+}
+
+/**
+ * The function resets crypto parameters change status.
+ *
+ * @param[in] media - pointer to fsmdef_media_t.
+ *
+ * @return            None.
+ *
+ * @pre               (media not_eq NULL)
+ */
+void
+gsmsdp_crypto_reset_params_change (fsmdef_media_t *media)
+{
+    media->negotiated_crypto.flags &= ~(FSMDEF_CRYPTO_RX_CHANGE |
+                                        FSMDEF_CRYPTO_TX_CHANGE);
+}
diff --git a/libs/sipcc/core/gsm/h/fim.h b/libs/sipcc/core/gsm/h/fim.h
new file mode 100755 (executable)
index 0000000..5eeb26a
--- /dev/null
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _FIM_H_
+#define _FIM_H_
+
+#include "sm.h"
+#include "fsm.h"
+
+/*
+ * This is an overlay structure.
+ * Every entity cb must have these two fields at the start of the cb
+ */
+typedef struct fim_cb_hdr_ {
+    callid_t call_id;
+    int state;
+} fim_cb_hdr_t;
+
+#ifndef fim_icb_t__
+#define fim_icb_t__
+struct fim_icb_t_;
+typedef struct fim_icb_t_ fim_icb_t;
+#endif
+
+typedef void (*fim_func_t)(fim_icb_t *elem, callid_t call_id);
+
+typedef struct fim_scb_t_ {
+    fsm_types_t type;
+    sm_table_t  *sm;
+    fim_func_t  get_cb;
+    fim_func_t  free_cb;
+} fim_scb_t;
+
+struct fim_icb_t_ {
+    struct fim_icb_t_ *next_chn;
+    struct fim_icb_t_ *next_icb;
+    callid_t          call_id;
+    boolean           ui_locked;
+    void              *cb;
+    fim_scb_t         *scb;
+};
+
+
+const char *fim_event_name(int event);
+boolean fim_process_event(void *data, boolean cac_passed);
+void fim_free_event(void *data);
+void fim_init(void);
+void fim_shutdown(void);
+void fsmcnf_free_cb(fim_icb_t *icb, callid_t call_id);
+void fsmxfr_free_cb(fim_icb_t *icb, callid_t call_id);
+void fsmdef_free_cb(fim_icb_t *icb, callid_t call_id);
+void fsmb2bcnf_free_cb(fim_icb_t *icb, callid_t call_id);
+
+void fim_lock_ui(callid_t call_id);
+void fim_unlock_ui(callid_t call_id);
+
+cc_causes_t
+fsm_cac_process_bw_avail_resp(void);
+cc_causes_t
+fsm_cac_process_bw_failed_resp(void);
+cc_causes_t
+fsm_cac_call_bandwidth_req(callid_t call_id, uint32_t sessions,
+                            void *msg);
+
+#endif /* _FIM_H_ */
diff --git a/libs/sipcc/core/gsm/h/fsm.h b/libs/sipcc/core/gsm/h/fsm.h
new file mode 100755 (executable)
index 0000000..c009179
--- /dev/null
@@ -0,0 +1,768 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _FSM_H_
+#define _FSM_H_
+
+#include "cpr_types.h"
+#include "sm.h"
+#include "ccapi.h"
+#include "vcm.h"
+#include "ccsip_core.h"
+#include "sll_lite.h"
+#include "sessionConstants.h"
+#include "ccsdp.h"
+
+/* TODO: BLASBERG
+ * fsm.h only needs the following from ccsip_core.h
+ * should put basic sip types into a separate hdr file
+typedef enum {
+    ALERTING_NONE,
+    ALERTING_OLD,
+    ALERTING_TONE,
+    ALERTING_RING
+} alertingType;
+*/
+
+
+#define FSMCNF_MAX_CCBS            (LSM_MAX_LINES)
+#define FSMXFR_MAX_XCBS            (LSM_MAX_LINES)
+#define FSM_NO_ID                  (0)
+#define FSMDEF_NO_DCB              (NULL)
+#define FSMDEF_ERR_ONHOOK_TMR_SECS (20)
+
+#define FSMDEF_MAX_DIGEST_ALG_LEN  10
+#define FSMDEF_MAX_DIGEST_LEN      32 * 3
+
+// Should match define for SIP stack MAX_SIP_URL_LENGTH
+#define FSMDEF_MAX_CALLER_ID_LEN (256)
+
+#ifndef fim_icb_t__
+#define fim_icb_t__
+struct fim_icb_t_;
+typedef struct fim_icb_t_ fim_icb_t;
+#endif
+
+typedef enum {
+    PRIMARY,
+    MONITOR,
+    LOCAL_CONF,
+    WHISPER_COACHING
+} fsm_session_t;
+
+typedef enum {
+    DIAL_MODE_NUMERIC,
+    DIAL_MODE_URL
+} dialMode_t;
+
+typedef enum {
+    FSMDEF_CALL_TYPE_MIN = -1,
+    FSMDEF_CALL_TYPE_NONE = CC_CALL_TYPE_NONE,
+    FSMDEF_CALL_TYPE_INCOMING = CC_CALL_TYPE_INCOMING,
+    FSMDEF_CALL_TYPE_OUTGOING = CC_CALL_TYPE_OUTGOING,
+    FSMDEF_CALL_TYPE_FORWARD = CC_CALL_TYPE_FORWARDED,
+    FSMDEF_CALL_TYPE_MAX
+} fsmdef_call_types_t;
+
+typedef enum {
+    FSMDEF_S_MIN = -1,
+    FSMDEF_S_IDLE,
+    FSMDEF_S_COLLECT_INFO,
+    FSMDEF_S_CALL_SENT,
+    FSMDEF_S_OUTGOING_PROCEEDING,
+    FSMDEF_S_KPML_COLLECT_INFO,
+    FSMDEF_S_OUTGOING_ALERTING,
+    FSMDEF_S_INCOMING_ALERTING,
+    FSMDEF_S_CONNECTING,
+    FSMDEF_S_JOINING,
+    FSMDEF_S_CONNECTED,
+    FSMDEF_S_CONNECTED_MEDIA_PEND,
+    FSMDEF_S_RELEASING,
+    FSMDEF_S_HOLD_PENDING,
+    FSMDEF_S_HOLDING,
+    FSMDEF_S_RESUME_PENDING,
+    FSMDEF_S_PRESERVED,
+    FSMDEF_S_MAX
+} fsmdef_states_t;
+
+typedef enum {
+    FSMDEF_MRTONE_NO_ACTION = 0,
+    FSMDEF_MRTONE_PLAYED_MONITOR_TONE,
+    FSMDEF_MRTONE_PLAYED_RECORDER_TONE,
+    FSMDEF_MRTONE_PLAYED_BOTH_TONES,
+    FSMDEF_MRTONE_RESUME_MONITOR_TONE,
+    FSMDEF_MRTONE_RESUME_RECORDER_TONE,
+    FSMDEF_MRTONE_RESUME_BOTH_TONES
+} fsmdef_monrec_tone_action_e;
+
+typedef enum {
+    FSMDEF_PLAYTONE_NO_ACTION = 0,
+    FSMDEF_PLAYTONE_ZIP
+} fsmdef_play_tone_action_e;
+
+/* Local crypto parameter is the local parameters to offer */
+typedef struct fsmdef_crypto_param_t_ {
+    int32_t                tag;           /* crypto attribute tag    */
+    vcm_crypto_algorithmID algorithmID;   /* encryption algorithm.   */
+    vcm_crypto_key_t       key;           /* local key               */
+} fsmdef_crypto_param_t;
+
+/* Negotiated crypto parameter */
+#define FSMDEF_CRYPTO_TX_CHANGE   (1 << 0) /* crypto Tx parms. change */
+#define FSMDEF_CRYPTO_RX_CHANGE   (1 << 1) /* crypto Tx parms. change */
+typedef struct fsmdef_negotiated_crypto_t_ {
+    int32_t                 tag;           /* crypto attribute tag    */
+    vcm_crypto_algorithmID  algorithmID;   /* algorithm ID            */
+    vcm_crypto_key_t        tx_key;        /* tx key                  */
+    vcm_crypto_key_t        rx_key;        /* rx key                  */
+    uint32_t                flags;         /* misc. flags.            */
+    char                    algorithm[FSMDEF_MAX_DIGEST_ALG_LEN];
+    char                    digest[FSMDEF_MAX_DIGEST_LEN];
+} fsmdef_negotiated_crypto_t;
+
+/*
+ * Saved attributes of interest from previously received SDP
+ */
+typedef struct fsmdef_previous_sdp_ {
+    uint16_t        dest_port;
+    cpr_ip_addr_t   dest_addr;
+    int32_t         avt_payload_type;
+
+    /*
+     * This field contains the number of elements in the payloads field.
+     */
+    int32_t num_payloads;
+    vcm_payload_info_t* payloads;
+
+    uint16_t        packetization_period;
+    uint16_t        max_packetization_period;
+    sdp_direction_e direction;
+    int32_t         tias_bw;
+    int32_t         profile_level;
+} fsmdef_previous_sdp_t;
+
+typedef struct fsmdef_media_t_ {
+    sll_lite_node_t node;     /* link node, must be first member of struct */
+    media_refid_t   refid;    /* media reference id                        */
+    sdp_media_e     type;     /* audio, video etc. media                   */
+    sdp_addrtype_e  addr_type;/* ipv4, ipv6                                */
+    int32_t         avt_payload_type;
+    vcm_vad_t       vad;
+    uint16_t        packetization_period;
+    uint16_t        max_packetization_period;
+    uint16_t        mode;
+    uint16_t        level;
+    boolean         direction_set;
+    sdp_direction_e direction;         /* current negotiated direction    */
+    sdp_direction_e support_direction; /* supported direction             */
+    sdp_transport_e transport;
+    uint16_t        src_port;   /* source port for this media stream      */
+    cpr_ip_addr_t   src_addr;   /* source addr for this media stream      */
+    uint16_t        dest_port;  /* destination port for this media stream */
+    cpr_ip_addr_t   dest_addr;  /* destination addr for this media straam */
+    /* Flag to indicate if Multicast */
+    boolean         is_multicast;
+    uint16_t        multicast_port;
+    /*
+     * rcv_chan indicates if the receive media stream has been opened
+     */
+    boolean         rcv_chan;
+    /*
+     * xmit_chan indicates if the transmit media stream has been opened
+     */
+    boolean         xmit_chan;
+
+    /*
+     * SRTP support.
+     */
+    fsmdef_negotiated_crypto_t negotiated_crypto;
+
+    /*
+     * Local crypto holds the local offered crypto set, keeps it in the
+     * dcb for fast access. The alternative to be kept in the
+     * SDP structure but is slower in retrieving it. In the future,
+     * it is possible that more than 1 crypto lines are offered but
+     * for now it is one.
+     */
+    fsmdef_crypto_param_t local_crypto;
+
+    /*
+     * Used to track previously received SDP for comparisons to
+     * newly received media to determine if RTP port recycling is
+     * required. Eliminates unnecessary recycling of ports which
+     * causes breaks in the audio stream.
+     */
+    fsmdef_previous_sdp_t previous_sdp;
+
+    /*
+     * hold tracks the hold state. The flag is a bit map so it is possible that
+     * the phone may have multiple holding states, ie. local and remote
+     */
+    uint32_t hold;
+    /*
+     * Flags fields for various bit flags
+     */
+#define FSM_MEDIA_F_SUPPORT_SECURITY (1 << 0)  /* supported security */
+    uint32_t flags;
+
+    /*
+     * capability index. The index into the media capbilty table
+     * that this media entry is coresponding to.
+     */
+    uint8_t         cap_index;
+
+    /* Values cached from attributes */
+    int32_t         tias_bw;
+    int32_t         profile_level;
+
+    void *video;
+
+    /* ICE Candidates */
+    char **candidatesp;
+    int candidate_ct;
+
+    /*
+     * rtcp-mux indicates media stream is muxed for RTP and RTCP
+     */
+    boolean        rtcp_mux;
+
+    /*
+     * port number used in m= data channel line
+     */
+    uint16_t       sctp_port;
+
+    /*
+     * Data Channel properties
+     */
+    uint32         streams;
+    char          *protocol;
+
+    /*
+     * This field contains the number of elements in the payloads field.
+     */
+    int32_t num_payloads;
+
+    /*
+     * List of active lists of payloads negotiated
+     */
+    vcm_payload_info_t* payloads;
+
+} fsmdef_media_t;
+
+struct fsm_fcb_t_;
+
+typedef struct {
+    callid_t        call_id;
+    callid_t        join_call_id;
+    line_t          line;
+    cc_caller_id_t  caller_id;
+    groupid_t       group_id;
+    int             digit_cnt;
+    fsmdef_call_types_t call_type;
+    fsm_session_t   session;
+    boolean         send_release;
+    int             msgs_sent;
+    int             msgs_rcvd;
+    boolean         onhook_received;
+
+    /*
+     * inband indicates if inband alerting is active
+     */
+    boolean inband;
+
+    /*
+     * inband_received indicates if inband alerting has been received.
+     * Once set, this bool stays set until dcb is reset.
+     */
+    boolean inband_received;
+
+    /*
+     * outofband tracks the payload type for outofband DTMF
+     */
+    int outofband;
+
+    /*
+     * Boolean indication of whether call was originated by phone or
+     * far end party.
+     */
+    boolean inbound;
+
+    /*
+     * The following data tracks the RTP info
+     */
+    boolean        remote_sdp_present;
+    boolean        remote_sdp_in_ack;
+    uint16_t       src_sdp_version;
+    cc_sdp_t       *sdp;
+
+    /* media list corresponding to m lines */
+    sll_lite_list_t media_list;
+
+    /*
+     * dial_mode tracks the state of the dialing mode icon, ie. alphanumeric or
+     * numeric
+     */
+    dialMode_t dial_mode;
+
+    /*
+     * pd_updated tracks whether or not the personal directory has
+     * been updated
+     */
+    boolean pd_updated;
+
+    /* tracks the ringing pattern to play */
+    vcm_ring_mode_t alerting_ring;
+
+    /* tracks the tone to play */
+    vcm_tones_t alerting_tone;
+
+    /* tracks the direction to play the tone. */
+    uint16_t tone_direction;
+
+    /* Was an alert-info header present, if so what did it contain */
+    cc_alerting_type alert_info;
+
+    /* used to determine when SIP stack releases the call early. */
+    boolean early_error_release;
+
+    /* used to determine when we are played a tone via the dialplan */
+    boolean dialplan_tone;
+
+    /* active tone (i.e. tone currently being played or requested to be played) */
+    vcm_tones_t active_tone;
+
+    /* indicates the action of monitor and recorder tones */
+    fsmdef_monrec_tone_action_e monrec_tone_action;
+
+    /* monitor/recorder tone direction to play out to */
+    uint16_t monitor_tone_direction;
+    uint16_t recorder_tone_direction;
+
+    /* indicates the action of play tone for a single time */
+    fsmdef_play_tone_action_e play_tone_action;
+
+    struct fsm_fcb_t_ *fcb;
+
+    /* Feature that is currently active */
+    cc_features_t active_feature;
+
+    /* Reason for hold */
+    cc_hold_resume_reason_e hold_reason;
+
+    /* Feature invocation state.
+     * Each feature will correspond to unique bit in variable.
+     * Bit will be Set if feature is invoked and awaiting feature ACK.
+     * Bit will be Cleared if feature is ACKed or not yet invoked.
+     * Each array element will hold invocation state for 32 features.
+     * The resource manager utility is utilized to maintain the bit settings.
+     */
+    void *feature_invocation_state;
+
+    /* TRUE if CCM has requested phone to show ringout UI */
+    boolean spoof_ringout_requested;
+    /* TRUE if GSM has applied ringout due to CCMs request to show ringout UI */
+    boolean spoof_ringout_applied;
+
+    /* Timer to go on hook after any call error */
+    cprTimer_t err_onhook_tmr;
+
+    /* Request pending timer */
+    cprTimer_t req_pending_tmr;
+
+    /* Ringback delay timer */
+    cprTimer_t ringback_delay_tmr;
+
+    /*
+     * save of orientation from callInfo to update UI at any time
+     * other than during call info. update such as after Tx start in
+     * order to update security icon.
+     */
+    cc_orientation_e orientation;
+
+    /*
+     * This boolean is used to short circuit sending UI update requests to the platform
+     * so that requests are only made when one of the call ui components requires
+     * updating. The same is done for placed call history.
+     */
+    boolean ui_update_required;
+    boolean placed_call_update_required;
+
+    boolean is_conf_call;
+
+    cc_security_e security;
+    cc_policy_e policy;
+
+    /* auto answer timer */
+    cprTimer_t autoAnswerTimer;
+    int32_t    reversionInterval;
+    cprTimer_t revertTimer;
+
+    boolean dsp_out_of_resources;
+
+    boolean selected;
+
+    boolean select_pending;
+
+    boolean call_not_counted_in_mnc_bt;
+
+    /*
+     * The media_cap holds the current media caps of the call
+     */
+    cc_media_cap_table_t *media_cap_tbl;
+
+    /*
+     * Holds the remote stream track information to be passed to UI
+     */
+    cc_media_remote_stream_table_t *remote_media_stream_tbl;
+
+    /*
+     * Holds the local stream track information passed in from the UI
+     */
+    cc_media_local_track_table_t *local_media_track_tbl;
+
+#define FSMDEF_F_HOLD_REQ_PENDING  (1 << 0)/* hold feature pending    */
+#define FSMDEF_F_XFER_COMPLETE     (1 << 1)/* hold feature pending    */
+    uint32_t                flags;         /* misc. flags.            */
+
+    int log_disp;
+
+    uint8_t cur_video_avail;
+    sdp_direction_e  video_pref;
+    unsigned int callref;      /* Callref (CI) from CUCM */
+
+    char peerconnection[PC_HANDLE_SIZE];  /* A handle to the peerconnection */
+    boolean peerconnection_set;
+
+    char *ice_ufrag;
+    char *ice_pwd;
+    char ice_default_candidate_addr[MAX_IPADDR_STR_LEN];
+
+    char digest_alg[FSMDEF_MAX_DIGEST_ALG_LEN];
+    char digest[FSMDEF_MAX_DIGEST_LEN];
+
+} fsmdef_dcb_t;
+
+typedef enum fsm_types_t_ {
+    FSM_TYPE_MIN = -1,
+    FSM_TYPE_NONE = FSM_TYPE_MIN,
+    FSM_TYPE_HEAD,
+    FSM_TYPE_CNF,
+    FSM_TYPE_B2BCNF,
+    FSM_TYPE_XFR,
+    FSM_TYPE_DEF,
+    FSM_TYPE_MAX
+} fsm_types_t;
+
+typedef enum fsm_hold_t_ {
+    FSM_HOLD_MIN = -1,
+    FSM_HOLD_NONE = 0,
+    FSM_HOLD_LCL = 1,
+    FSM_HOLD_MAX = 2
+} fsm_hold_t;
+
+typedef struct fsm_data_def_t_ {
+    int hold;
+} fsm_data_def_t;
+
+typedef struct fsm_data_xfr_t_ {
+    int xfr_id;
+} fsm_data_xfr_t;
+
+typedef struct fsm_data_t_ {
+    union {
+        fsm_data_def_t def;
+        fsm_data_xfr_t xfr;
+    } data;
+} fsm_data_t;
+
+typedef struct fsmcnf_ccb_t_ {
+    cc_srcs_t cnf_orig;
+    int      cnf_id;
+    callid_t cnf_call_id;
+    callid_t cns_call_id;
+    line_t   cnf_line;
+    line_t   cns_line;
+    boolean  active;
+    boolean  bridged;
+
+/* The following field encodes flags */
+#define JOINED  0x1
+#define XFER    0x2
+#define LCL_CNF    0x4
+    uint32_t flags;
+    boolean  cnf_ftr_ack;
+} fsmcnf_ccb_t;
+
+typedef enum fsmxfr_types_t_ {
+    FSMXFR_TYPE_MIN = -1,
+    FSMXFR_TYPE_NONE,
+    FSMXFR_TYPE_XFR,
+    FSMXFR_TYPE_BLND_XFR,
+    FSMXFR_TYPE_DIR_XFR,
+    FSMXFR_TYPE_MAX
+} fsmxfr_types_t;
+
+typedef enum fsmxfr_modes_t_ {
+    FSMXFR_MODE_MIN = -1,
+    FSMXFR_MODE_TRANSFEROR,
+    FSMXFR_MODE_TRANSFEREE,
+    FSMXFR_MODE_TARGET
+} fsmxfr_modes_t;
+
+struct fsmxfr_xcb_t_;
+typedef struct fsmxfr_xcb_t_ {
+    cc_srcs_t         xfr_orig;
+    int               xfr_id;
+    callid_t          xfr_call_id;
+    callid_t          cns_call_id;
+    line_t            xfr_line;
+    line_t            cns_line;
+    fsmxfr_types_t    type;
+    cc_xfer_methods_t method;
+    char              *dialstring;
+    char              *queued_dialstring;
+    char              *referred_by;
+    boolean           active;
+    boolean           cnf_xfr;
+    boolean           xfer_comp_req;
+    fsmxfr_modes_t    mode;
+    struct fsmxfr_xcb_t_ *xcb2;
+} fsmxfr_xcb_t;
+
+
+typedef struct fsm_fcb_t_ {
+    callid_t call_id;
+
+    int state;
+    int old_state;
+
+    fsm_types_t fsm_type;
+
+
+    /*
+     * fsmdef specific data
+     */
+    fsmdef_dcb_t *dcb;
+
+    /*
+     * fsmxfr specific data
+     */
+
+    fsmxfr_xcb_t *xcb;
+    /*
+     * fsmcnf specific data
+     */
+    fsmcnf_ccb_t *ccb;
+
+    /*
+     * fsmb2bcnf specific data
+     */
+    fsmcnf_ccb_t *b2bccb;
+
+} fsm_fcb_t;
+
+typedef enum {
+    FSMDEF_MSG_MIN              = -1,
+    FSMDEF_MSG_NONE             = 0,
+    FSMDEF_MSG_SETUP            = 1,
+    FSMDEF_MSG_SETUP_ACK        = 2,
+    FSMDEF_MSG_PROCEEDING       = 4,
+    FSMDEF_MSG_ALERTING         = 8,
+    FSMDEF_MSG_CONNECTED        = 16,
+    FSMDEF_MSG_CONNECTED_ACK    = 32,
+    FSMDEF_MSG_RELEASE          = 64,
+    FSMDEF_MSG_RELEASE_COMPLETE = 128,
+    FSMDEF_MSG_MAX
+} fsmdef_msgs_t;
+
+#define FSM_FOR_ALL_CBS(cb, cbs, max_cbs) \
+    for ((cb) = (cbs); (cb) <= &((cbs)[(max_cbs-1)]); (cb)++)
+
+#define FSM_CHK_FLAGS(flags, flag)   ((flags) &   (flag))
+#define FSM_SET_FLAGS(flags, flag)   ((flags) |=  (flag))
+#define FSM_RESET_FLAGS(flags, flag) ((flags) &= ~(flag))
+void         *fsmdef_feature_timer_timeout(cc_features_t feature_id, void *);
+void         fsmdef_end_call(fsmdef_dcb_t *dcb, cc_causes_t cause);
+void         fsm_sm_ftr(cc_features_t ftr_id, cc_srcs_t src_id);
+void         fsm_sm_ignore_ftr(fsm_fcb_t *fcb, int fname,
+                               cc_features_t ftr_id);
+void fsm_sm_ignore_src(fsm_fcb_t *fcb, int fname, cc_srcs_t src_id);
+const char *fsm_state_name(fsm_types_t type, int id);
+const char *fsm_type_name(fsm_types_t type);
+fsmdef_dcb_t *fsm_get_dcb(callid_t call_id);
+void fsm_init_scb(fim_icb_t *icb, callid_t call_id);
+fsm_fcb_t *fsm_get_fcb_by_call_id(callid_t call_id);
+fsm_fcb_t *fsm_get_fcb_by_call_id_and_type(callid_t call_id, fsm_types_t type);
+void
+fsm_get_fcb_by_selected_or_connected_call_fcb(callid_t call_id, fsm_fcb_t **con_fcb_found,
+                                               fsm_fcb_t **sel_fcb_found);
+fsm_fcb_t *fsm_get_new_fcb(callid_t call_id, fsm_types_t fsm_type);
+void fsm_init(void);
+void fsm_shutdown(void);
+void fsm_release(fsm_fcb_t *fcb, int fname, cc_causes_t cause);
+void fsm_change_state(fsm_fcb_t *fcb, int fname, int new_state);
+void fsm_init_fcb(fsm_fcb_t *fcb, callid_t call_id, fsmdef_dcb_t *dcb,
+                  fsm_types_t type);
+void fsm_display_no_free_lines(void);
+void fsm_display_use_line_or_join_to_complete(void);
+void fsm_display_feature_unavailable(void);
+void fsm_set_call_status_feature_unavailable(callid_t call_id, line_t line);
+
+cc_causes_t fsm_get_new_outgoing_call_context(callid_t call_id, line_t line,
+                                              fsm_fcb_t *fcb, boolean expline);
+cc_causes_t fsm_get_new_incoming_call_context(callid_t call_id, fsm_fcb_t *fcb,
+                                              const char *called_number,
+                                              boolean expline);
+sm_rcs_t fsmdef_release(fsm_fcb_t *fcb, cc_causes_t cause,
+                        boolean send_release);
+int fsmdef_get_call_type_by_call_id(callid_t call_id);
+fsmdef_call_types_t fsmdef_get_call_id_by_call_ref(int call_ref);
+fsmdef_dcb_t *fsmdef_get_dcb_by_call_id(callid_t call_id);
+void fsmdef_init_dcb(fsmdef_dcb_t *dcb, callid_t call_id,
+                     fsmdef_call_types_t call_type,
+                     const char *called_number, line_t line,
+                     fsm_fcb_t *fcb);
+cc_causes_t fsm_set_fcb_dcbs (fsmdef_dcb_t *dcb);
+fsmdef_dcb_t *fsmdef_get_new_dcb(callid_t call_id);
+void fsmdef_init(void);
+int fsmdef_get_active_call_cnt(callid_t callId);
+fsmdef_dcb_t *fsmdef_get_connected_call(void);
+boolean fsmdef_are_join_calls_on_same_line(line_t line);
+boolean fsmdef_are_there_selected_calls_onotherline(line_t line);
+fsmdef_dcb_t *fsmdef_get_other_dcb_by_line(callid_t call_id, line_t line);
+int fsmdef_get_dcbs_in_held_state(fsmdef_dcb_t **dcb,
+                                  callid_t ignore_call_id);
+sm_rcs_t fsmdef_offhook(fsm_fcb_t *fcb, cc_msgs_t msg_id, callid_t call_id,
+                        line_t line, const char *dial_string, sm_event_t *event,
+                        char *global_call_id, callid_t prim_call_id,
+                        cc_hold_resume_reason_e consult_reason,
+                        monitor_mode_t monitor_mode);
+
+sm_rcs_t fsmdef_dialstring(fsm_fcb_t *fcb, const char *dialstring,
+                           cc_redirect_t *redirect, boolean replace,
+                           cc_call_info_t *call_info);
+
+fsmcnf_ccb_t *fsmcnf_get_ccb_by_call_id(callid_t call_id);
+callid_t fsmcnf_get_other_call_id(fsmcnf_ccb_t *ccb, callid_t call_id);
+
+void fsmxfr_update_xfr_context(fsmxfr_xcb_t *xcb, callid_t old_call_id,
+                               callid_t new_call_id);
+fsmxfr_xcb_t *fsmxfr_get_xcb_by_call_id(callid_t call_id);
+callid_t fsmxfr_get_other_call_id(fsmxfr_xcb_t *xcb, callid_t call_id);
+fsmxfr_types_t fsmxfr_get_xfr_type(callid_t call_id);
+cc_features_t fsmxfr_type_to_feature(fsmxfr_types_t type);
+
+#ifdef _WIN32
+extern void NotifyStateChange(callid_t callid, int32_t state);
+#define NOTIFY_STATE_CHANGE(fcb,callid,state) NotifyStateChange(callid,state);dcsm_update_gsm_state(fcb,callid,state)
+#else
+#define NOTIFY_STATE_CHANGE(fcb,callid,state) dcsm_update_gsm_state(fcb,callid,state)
+#endif
+
+const char *fsmdef_state_name(int id);
+const char *fsmxfr_state_name(int id);
+const char *fsmcnf_state_name(int id);
+
+void fsm_cac_init(void);
+void fsmcnf_init(void);
+void fsmxfr_init(void);
+void fsmdef_init(void);
+void fsmcnf_shutdown(void);
+void fsmxfr_shutdown(void);
+void fsmdef_shutdown(void);
+void fsm_cac_shutdown(void);
+void fsm_cac_call_release_cleanup(callid_t call_id);
+
+void fsmdef_reversion_timeout(callid_t call_id);
+void fsm_cac_process_bw_fail_timer(void *tmr_data);
+void fsmdef_auto_answer_timeout(void *);
+const char *fsmb2bcnf_state_name(int id);
+void fsmb2bcnf_init(void);
+void fsmb2bcnf_shutdown(void);
+int fsmutil_is_b2bcnf_consult_call(callid_t call_id);
+callid_t fsmxfr_get_consult_call_id(callid_t call_id);
+callid_t fsmb2bcnf_get_consult_call_id(callid_t call_id);
+callid_t fsmb2bcnf_get_primary_call_id(callid_t call_id);
+boolean fsmdef_check_if_ok_for_dial_call(line_t line);
+boolean fsmdef_check_if_ok_to_ans_call(line_t line, callid_t call_id);
+boolean fsmdef_check_if_ok_to_resume_call(line_t line, callid_t call_id);
+boolean fsmdef_check_if_ok_to_hold_call(line_t line, callid_t call_id);
+boolean fsmdef_check_if_ok_to_monitor_update_call(line_t line, callid_t call_id);
+boolean fsmdef_check_if_ok_to_run_feature(line_t line, callid_t call_id);
+fsmdef_dcb_t *fsmdef_get_dcb_by_call_instance_id(line_t line,
+                                                 uint16 call_instance_id);
+boolean fsmb2bcnf_check_if_ok_to_setup_conf (callid_t call_id);
+boolean fsmdef_check_if_chaperone_call_exist (void);
+void fsmdef_call_cc_state_dialing(fsmdef_dcb_t *dcb, boolean suppressStutter);
+/* This macro is to identify incoming joining call */
+#define fsm_is_joining_call(feat_data)  \
+      ((feat_data.newcall.join.join_call_id != CC_NO_CALL_ID) && \
+      ((feat_data.newcall.cause == CC_CAUSE_BARGE) || \
+      (feat_data.newcall.cause == CC_CAUSE_MONITOR)))
+/* These macros are for SRTP support */
+#define FSM_GET_SECURITY_STATUS(dcb) (dcb->security)
+#define FSM_SET_SECURITY_STATUS(dcb, status) (dcb->security = status)
+#define FSM_GET_POLICY(dcb) (dcb->policy)
+#define FSM_SET_POLICY(dcb, status) (dcb->policy = status)
+#define FSM_GET_CACHED_ORIENTATION(dcb) (dcb->orientation)
+#define FSM_SET_CACHED_ORIENTATION(dcb, value) (dcb->orientation = value)
+#define FSM_NEGOTIATED_CRYPTO_ALGORITHM_ID(media)                  \
+       ((media->transport == SDP_TRANSPORT_RTPSAVP  ||       \
+         media->transport == SDP_TRANSPORT_RTPSAVPF) ?        \
+        media->negotiated_crypto.algorithmID : VCM_NO_ENCRYPTION)
+#define FSM_NEGOTIATED_CRYPTO_RX_KEY(media)       \
+       &media->negotiated_crypto.rx_key
+#define FSM_NEGOTIATED_CRYPTO_TX_KEY(media)       \
+       &media->negotiated_crypto.tx_key
+
+#define FSM_NEGOTIATED_CRYPTO_DIGEST_ALGORITHM(media)       \
+       media->negotiated_crypto.algorithm
+
+#define FSM_NEGOTIATED_CRYPTO_DIGEST(media)       \
+       media->negotiated_crypto.digest
+
+int fsmutil_get_call_attr(fsmdef_dcb_t *dcb, line_t line, callid_t call_id);
+uint16_t fsmutil_get_ci_id(line_t line);
+void fsmutil_init_ci_map(void);
+void fsmdef_platform_dcb_init(fsmdef_dcb_t *dcb);
+void fsmutil_free_all_ci_id(void);
+void fsmutil_free_ci_id(uint16_t id, line_t line);
+void fsmutil_set_ci_id(uint16_t id, line_t line);
+void fsmutil_free_ci_map(void);
+void fsmutil_show_ci_map(void);
+void fsmutil_init_shown_calls_ci_map(void);
+void fsmutil_free_all_shown_calls_ci_map(void);
+void fsmutil_clear_shown_calls_ci_element(uint16_t id, line_t line);
+void fsmutil_set_shown_calls_ci_element(uint16_t id, line_t line);
+boolean fsmutil_is_shown_calls_ci_element_set(uint16_t id, line_t line);
+
+
+callid_t fsmxfr_get_primary_call_id(callid_t call_id);
+uint16_t fsmutil_get_num_selected_calls(void);
+int fsmutil_is_cnf_consult_call(callid_t call_id);
+int fsmutil_is_cnf_consult_leg(callid_t call_id, fsmcnf_ccb_t *fsmcnf_ccbs,
+                               uint16_t max_ccbs);
+int fsmutil_is_xfr_consult_call(callid_t call_id);
+int fsmutil_is_xfr_consult_leg(callid_t call_id, fsmxfr_xcb_t *fsmxfr_xcbs,
+                               uint16_t max_xcbs);
+void fsmutil_init_groupid(fsmdef_dcb_t *dcb, callid_t call_id,
+                          fsmdef_call_types_t call_type);
+void fsmutil_process_feature_ack(fsmdef_dcb_t *dcb, cc_features_t feature_id);
+void fsmutil_clear_all_feature_invocation_state(fsmdef_dcb_t *dcb);
+void fsmutil_init_feature_invocation_state(fsmdef_dcb_t *dcb);
+void fsmutil_free_feature_invocation_state(fsmdef_dcb_t *dcb);
+
+void fsmdef_error_onhook_timeout(void *data);
+
+int fsmutil_is_xfr_leg(callid_t call_id, fsmxfr_xcb_t *fsmxfr_xcbs,
+                       unsigned short max_xcbs);
+int fsmutil_is_cnf_leg(callid_t call_id, fsmcnf_ccb_t *fsmcnf_ccbs,
+                       unsigned short max_ccbs);
+
+void fsm_display_control_ringin_calls(boolean hide);
+void fsmdef_update_media_cap_feature_event(cc_feature_t *msg);
+boolean fsmcnd_conf_call_id_valid(fsmcnf_ccb_t   *ccb);
+
+boolean fsmdef_check_retain_fwd_info_state(void);
+#endif
diff --git a/libs/sipcc/core/gsm/h/gsm.h b/libs/sipcc/core/gsm/h/gsm.h
new file mode 100755 (executable)
index 0000000..ba6cafc
--- /dev/null
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _GSM_H_
+#define _GSM_H_
+
+#include "cpr_types.h"
+#include "cpr_memory.h"
+#include "cpr_ipc.h"
+#include "cpr_stdio.h"
+
+#define GSM_ERR_MSG err_msg
+typedef void(* media_timer_callback_fp) (void);
+void gsm_set_media_callback(media_timer_callback_fp* callback);
+
+void gsm_set_initialized(void);
+cpr_status_e gsm_send_msg(uint32_t cmd, cprBuffer_t buf, uint16_t len);
+cprBuffer_t gsm_get_buffer(uint16_t size);
+boolean gsm_is_idle(void);
+
+/*
+ * List of timers that the GSM task is responsible for.
+ * CPR will send a msg to the GSM task when these
+ * timers expire. CPR expects a timer id when the timer
+ * is created, this enum serves that purpose.
+ */
+typedef enum {
+    GSM_ERROR_ONHOOK_TIMER,
+    GSM_AUTOANSWER_TIMER,
+    GSM_DIAL_TIMEOUT_TIMER,
+    GSM_KPML_INTER_DIGIT_TIMER,
+    GSM_KPML_CRITICAL_DIGIT_TIMER,
+    GSM_KPML_EXTRA_DIGIT_TIMER,
+    GSM_KPML_SUBSCRIPTION_TIMER,
+    GSM_MULTIPART_TONES_TIMER,
+    GSM_CONTINUOUS_TONES_TIMER,
+    GSM_REQ_PENDING_TIMER,
+    GSM_RINGBACK_DELAY_TIMER,
+    GSM_REVERSION_TIMER,
+    GSM_FLASH_ONCE_TIMER,
+    GSM_CAC_FAILURE_TIMER,
+    GSM_TONE_DURATION_TIMER
+} gsmTimerList_t;
+
+/*
+ * The common code creating the GSM timers needs to have
+ * access to the gsm_msg_queue variable since CPR
+ * needs to know where to send the timer expiration
+ * message.
+ */
+extern cprMsgQueue_t gsm_msg_queue;
+
+extern void kpml_process_msg(uint32_t cmd, void *msg);
+extern void dp_process_msg(uint32_t cmd, void *msg);
+extern void kpml_init(void);
+extern void kpml_shutdown(void);
+
+#endif
diff --git a/libs/sipcc/core/gsm/h/gsm_sdp.h b/libs/sipcc/core/gsm/h/gsm_sdp.h
new file mode 100644 (file)
index 0000000..25ae4cf
--- /dev/null
@@ -0,0 +1,139 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _GSM_SDP_H_
+#define _GSM_SDP_H_
+
+
+#define GSMSDP_VERSION_STR_LEN (20)
+
+#define GSMSDP_MEDIA_VALID(media)  \
+    ((media != NULL) && (media->refid != CC_NO_MEDIA_REF_ID))
+
+#define GSMSDP_MEDIA_ENABLED(media) \
+    (GSMSDP_MEDIA_VALID(media) && (media->src_port != 0))
+
+#define GSMSDP_MEDIA_COUNT(dcb) \
+    (SLL_LITE_NODE_COUNT(&dcb->media_list))
+
+#define GSMSDP_FIRST_MEDIA_ENTRY(dcb) \
+    ((fsmdef_media_t *)SLL_LITE_LINK_HEAD(&dcb->media_list))
+
+#define GSMSDP_NEXT_MEDIA_ENTRY(media) \
+    ((fsmdef_media_t *)SLL_LITE_LINK_NEXT_NODE(media))
+
+#define GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb) \
+    for (media = start_media; (media != NULL);                \
+         media = (media != end_media ?                        \
+                   GSMSDP_NEXT_MEDIA_ENTRY(media) : NULL))
+
+#define GSMSDP_FOR_ALL_MEDIA(media, dcb) \
+    for (media = GSMSDP_FIRST_MEDIA_ENTRY(dcb); (media != NULL); \
+         media = GSMSDP_NEXT_MEDIA_ENTRY(media))
+
+typedef struct {
+    const char *name;
+    int         value;
+} gsmsdp_key_table_entry_t;
+
+typedef enum constraints_ {
+    OfferToReceiveAudio     = 0,
+    OfferToReceiveVideo     = 1,
+    VoiceActivityDetection  = 2
+} constraints;
+
+static const gsmsdp_key_table_entry_t constraints_table[] = {
+    {"OfferToReceiveAudio",         OfferToReceiveAudio},
+    {"OfferToReceiveVideo",         OfferToReceiveVideo},
+    {"VoiceActivityDetection",      VoiceActivityDetection}
+};
+
+cc_causes_t gsmsdp_create_local_sdp(fsmdef_dcb_t *dcb_p, boolean force_streams_enabled,
+                                    boolean audio, boolean video, boolean data, boolean offer);
+void gsmsdp_create_options_sdp(cc_sdp_t **sdp_pp);
+void gsmsdp_reset_local_sdp_media(fsmdef_dcb_t *dcb, fsmdef_media_t *media,
+                                  boolean hold);
+void gsmsdp_set_local_sdp_direction(fsmdef_dcb_t *dcb_p, fsmdef_media_t *media,
+                                    sdp_direction_e direction);
+void gsmsdp_set_local_hold_sdp(fsmdef_dcb_t *dcb, fsmdef_media_t *media);
+void gsmsdp_set_local_resume_sdp(fsmdef_dcb_t *dcb, fsmdef_media_t *media);
+cc_causes_t gsmsdp_negotiate_answer_sdp(fsm_fcb_t *fcb,
+                                        cc_msgbody_info_t *msg_body);
+cc_causes_t gsmsdp_negotiate_offer_sdp(fsm_fcb_t *fcb,
+                                       cc_msgbody_info_t *msg_body,
+                                       boolean init);
+cc_causes_t gsmsdp_process_offer_sdp(fsm_fcb_t *fcb,
+                                     cc_msgbody_info_t *msg_body,
+                                     boolean init);
+cc_causes_t
+gsmsdp_negotiate_media_lines (fsm_fcb_t *fcb_p, cc_sdp_t *sdp_p, boolean initial_offer,
+                              boolean offer, boolean notify_stream_added, boolean create_answer);
+
+boolean gsmsdp_sdp_differs_from_previous_sdp(boolean rcv_only,
+                                             fsmdef_media_t *media);
+cc_causes_t gsmsdp_encode_sdp(cc_sdp_t *sdp_p, cc_msgbody_info_t *msg_body);
+cc_causes_t gsmsdp_encode_sdp_and_update_version(fsmdef_dcb_t *dcb_p,
+                                                 cc_msgbody_info_t *msg_body);
+void gsmsdp_free(fsmdef_dcb_t *dcb_p);
+fsmdef_media_t *gsmsdp_find_audio_media(fsmdef_dcb_t *dcb_p);
+
+#define gsmsdp_is_srtp_supported()          (TRUE)
+extern void gsmsdp_init_sdp_media_transport(fsmdef_dcb_t *dcb,
+                                            void *sdp,
+                                            fsmdef_media_t *media);
+extern void gsmsdp_reset_sdp_media_transport(fsmdef_dcb_t *dcb,
+                                             void *sdp,
+                                             fsmdef_media_t *media,
+                                             boolean hold);
+extern sdp_transport_e gsmsdp_negotiate_media_transport(fsmdef_dcb_t *dcb_p,
+                                                        cc_sdp_t *cc_sdp_p,
+                                                        boolean offer,
+                                                        fsmdef_media_t *media,
+                                                        uint16_t *inst_num,
+                                                        uint16 level);
+extern void gsmsdp_update_local_sdp_media_transport(fsmdef_dcb_t *dcb_p,
+                                                    void *sdp_p,
+                                                    fsmdef_media_t *media,
+                                                    sdp_transport_e transport,
+                                                    boolean all);
+extern void gsmsdp_update_negotiated_transport(fsmdef_dcb_t *dcb_p,
+                                               cc_sdp_t *cc_sdp_p,
+                                               fsmdef_media_t *media,
+                                               uint16_t crypto_inst,
+                                               sdp_transport_e transport,
+                                               uint16 level);
+extern void gsmsdp_update_crypto_transmit_key(fsmdef_dcb_t *dcb_p,
+                                              fsmdef_media_t *media,
+                                              boolean offer,
+                                              boolean initial_offer,
+                                              sdp_direction_e direction);
+extern void gsmsdp_set_media_transport_for_option(void *sdp, uint16 level);
+extern boolean gsmsdp_is_crypto_ready(fsmdef_media_t *media, boolean rx);
+extern boolean gsmsdp_is_media_encrypted(fsmdef_dcb_t *dcb_p);
+extern boolean gsmsdp_crypto_params_change(boolean rcv_only,
+                                           fsmdef_media_t *media);
+extern void gsmsdp_crypto_reset_params_change(fsmdef_media_t *media);
+extern void gsmsdp_cache_crypto_keys(void);
+extern boolean gsmsdp_create_free_media_list(void);
+extern void gsmsdp_destroy_free_media_list(void);
+extern void gsmsdp_init_media_list(fsmdef_dcb_t *dcb_p);
+extern void gsmsdp_clean_media_list(fsmdef_dcb_t *dcb);
+extern fsmdef_media_t *gsmsdp_find_media_by_refid(fsmdef_dcb_t *dcb_p,
+                                                  media_refid_t refid);
+extern boolean gsmsdp_handle_media_cap_change(fsmdef_dcb_t *dcb_p,
+                                              boolean refresh, boolean hold);
+extern boolean gsmsdp_update_local_sdp_media_capability(fsmdef_dcb_t *dcb_p,
+                                              boolean refresh, boolean hold);
+boolean is_gsmsdp_media_ip_updated_to_latest( fsmdef_dcb_t * dcb );
+
+void gsmsdp_add_remote_stream(uint16_t idx, int pc_stream_id, fsmdef_dcb_t * dcb, fsmdef_media_t *media);
+cc_causes_t gsmsdp_install_peer_ice_attributes(fsm_fcb_t *fcb_p);
+cc_causes_t gsmsdp_configure_dtls_data_attributes(fsm_fcb_t *fcb_p);
+cc_causes_t gsmsdp_find_level_from_mid(fsmdef_dcb_t * dcb, const char * mid, uint16_t *level);
+void gsmsdp_process_cap_constraints(fsmdef_dcb_t *dcb, const cc_media_constraints_t* constraints);
+cc_causes_t
+gsmsdp_get_offered_media_types (fsm_fcb_t *fcb_p, cc_sdp_t *sdp_p, boolean *has_audio, boolean *has_video, boolean *has_data);
+fsmdef_media_t* gsmsdp_find_media_by_media_type(fsmdef_dcb_t *dcb, sdp_media_e         media_type);
+#endif
+
diff --git a/libs/sipcc/core/gsm/h/lsm.h b/libs/sipcc/core/gsm/h/lsm.h
new file mode 100755 (executable)
index 0000000..b0df529
--- /dev/null
@@ -0,0 +1,180 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _LSM_H_
+#define _LSM_H_
+
+#include "cpr_types.h"
+#include "ccapi.h"
+#include "uiapi.h"
+#include "fsm.h"
+
+
+#define LSM_NO_LINE     (0)
+#define LSM_NO_INSTANCE (0)
+#define NO_LINES_AVAILABLE (0)
+
+/* FIXME GS - Breaking my own rule - need to figure out how to do a platform
+ * specific include file here
+ */
+#define LSM_MAX_LINES         MAX_REG_LINES
+#define LSM_MAX_INSTANCES     (MAX_CALLS_PER_LINE-1)
+#define LSM_MAX_EXP_INSTANCES MAX_CALLS_PER_LINE
+#define LSM_MAX_CALLS         MAX_CALLS
+
+#define NO_FREE_LINES_TIMEOUT  (10)
+#define CALL_ALERT_TIMEOUT  (10)
+
+/*
+ * Used to play busy verfication tone
+ */
+#define BUSY_VERIFICATION_DELAY       (10000)
+
+/*
+ * Temp until added to config
+ */
+#define TOH_DELAY                (10000)
+
+/*
+ * Used to play msg waiting and stutter dialtones.
+ * Both tones are 100ms on/off repeating 10 and
+ * 3 times respectively, followed by steady dialtone.
+ * Due to DSP limitations we first tell the DSP to
+ * play the 100ms on/off pairs the correct number of
+ * times, set a timer, and then tell it to play dialtone.
+ */
+#define MSG_WAITING_DELAY   (2050)
+#define STUTTER_DELAY       (650)
+
+/*
+ * Used to play the busy verfication tone which
+ * is two seconds of dialtone followed by the
+ * callwaiting tone every ten seconds.
+ */
+#define BUSY_VERIFY_DELAY   (12000)
+
+
+/* LSM states */
+typedef enum {
+    LSM_S_MIN  = -1,
+    LSM_S_NONE = LSM_S_MIN,
+    LSM_S_IDLE,
+    LSM_S_PENDING,
+    LSM_S_OFFHOOK,
+    LSM_S_ONHOOK,
+    LSM_S_PROCEED,
+    LSM_S_RINGOUT,
+    LSM_S_RINGIN,
+    LSM_S_CONNECTED,
+    LSM_S_BUSY,
+    LSM_S_CONGESTION,
+    LSM_S_HOLDING,
+    LSM_S_CWT,
+    LSM_S_XFER,
+    LSM_S_ATTN_XFER,
+    LSM_S_CONF,
+    LSM_S_INVALID_NUMBER,
+    LSM_S_MAX
+} lsm_states_t;
+
+boolean lsm_is_phone_idle(void);
+int lsm_get_instances_available_cnt(line_t line, boolean expline);
+void lsm_increment_call_chn_cnt(line_t line);
+void lsm_decrement_call_chn_cnt(line_t line);
+line_t lsm_get_newcall_line(line_t line);
+callid_t lsm_get_active_call_id(void);
+line_t lsm_get_line_by_call_id(callid_t call_id);
+/*
+ * The lsm_get_facility_by_called_number() and the lsm_get_facility_by_line()
+ * below are defined to allow be used by fsm state mechine to bind the
+ * LSM to the FSM default state machine only.
+ */
+cc_causes_t lsm_get_facility_by_called_number(callid_t call_id,
+                                              const char *called_number,
+                                              line_t *free_line,
+                                              boolean expline, void *dcb);
+cc_causes_t lsm_get_facility_by_line(callid_t call_id, line_t line,
+                                     boolean exp, void *dcb);
+char lsm_digit2ch(int digit);
+void lsm_set_active_call_id(callid_t call_id);
+void lsm_init(void);
+void lsm_shutdown(void);
+void lsm_reset(void);
+void lsm_ui_display_notify(const char *pNotifyStr, unsigned long timeout);
+void lsm_ui_display_status(const char *pStatusStr, line_t line,
+                           callid_t call_id);
+string_t lsm_parse_displaystr(string_t displaystr);
+void lsm_speaker_mode(short mode);
+void lsm_add_remote_stream (line_t line, callid_t call_id, fsmdef_media_t *media, int *pc_stream_id);
+
+#ifdef _WIN32
+void terminate_active_calls(void);
+#endif
+
+const char *lsm_state_name(lsm_states_t id);
+void lsm_set_cfwd_all_nonccm(line_t line, char *callfwd_dialstring);
+void lsm_set_cfwd_all_ccm(line_t line, char *callfwd_dialstring);
+void lsm_clear_cfwd_all_nonccm(line_t line);
+void lsm_clear_cfwd_all_ccm(line_t line);
+int lsm_check_cfwd_all_nonccm(line_t line);
+int lsm_check_cfwd_all_ccm(line_t line);
+char *lsm_is_phone_forwarded(line_t line);
+void lsm_tmr_tones_callback(void *);
+void lsm_update_placed_callinfo(void *dcb);
+void lsm_start_multipart_tone_timer(vcm_tones_t tone, uint32_t delay,
+                                    callid_t callId);
+void lsm_start_continuous_tone_timer(vcm_tones_t tone, uint32_t delay,
+                                     callid_t callId);
+void lsm_start_tone_duration_timer(vcm_tones_t tone, uint32_t delay,
+                                   cc_call_handle_t call_handle);
+void lsm_stop_multipart_tone_timer(void);
+void lsm_stop_continuous_tone_timer(void);
+void lsm_stop_tone_duration_timer(void);
+void lsm_tone_duration_tmr_callback(void *data);
+void lsm_tone_start_with_duration (vcm_tones_t tone, short alert_info, cc_call_handle_t call_handle, groupid_t group_id,
+                                   streamid_t stream_id, uint16_t direction, uint32_t duration);
+void lsm_update_active_tone(vcm_tones_t tone, callid_t call_id);
+boolean lsm_is_tx_channel_opened(callid_t call_id);
+void lsm_update_monrec_tone_action (vcm_tones_t tone, callid_t call_id, uint16_t direction);
+void lsm_downgrade_monrec_tone_action(vcm_tones_t tone, callid_t call_id);
+char *lsm_get_gdialed_digits();
+void lsm_set_hold_ringback_status(callid_t call_id, boolean ringback_status);
+
+extern callid_t lsm_get_ui_id(callid_t call_id);
+extern cc_call_handle_t lsm_get_ms_ui_call_handle(line_t line, callid_t call_id, callid_t ui_id);
+extern void lsm_set_ui_id(callid_t call_id, callid_t ui_id);
+extern lsm_states_t lsm_get_state(callid_t call_id);
+
+extern void lsm_set_lcb_dusting_call(callid_t call_id);
+extern void lsm_set_lcb_call_priority(callid_t call_id);
+extern boolean lsm_is_it_priority_call(callid_t call_id);
+extern void lsm_play_tone(cc_features_t feature_id);
+extern boolean lsm_callwaiting(void);
+extern void lsm_display_control_ringin_call (callid_t call_id, line_t line, boolean hide);
+extern line_t lsm_find_next_available_line(line_t line, boolean same_dn, boolean incoming);
+extern line_t lsm_get_available_line (boolean incoming);
+extern boolean lsm_is_line_available(line_t line, boolean incoming);
+extern void lsm_ui_display_notify_str_index(int str_index);
+extern cc_causes_t lsm_allocate_call_bandwidth(callid_t call_id, int sessions);
+extern void lsm_update_gcid(callid_t call_id, char * gcid);
+extern void lsm_set_lcb_prevent_ringing(callid_t call_id);
+extern void lsm_remove_lcb_prevent_ringing(callid_t call_id);
+extern void lsm_set_lcb_dialed_str_flag(callid_t call_id);
+void lsm_update_video_avail(line_t line, callid_t call_id, int dir);
+void lsm_update_video_offered(line_t line, callid_t call_id, int dir);
+callid_t lsm_get_callid_from_ui_id (callid_t uid);
+void lsm_set_video_mute (callid_t call_id, int mute);
+int lsm_get_video_mute (callid_t call_id);
+void lsm_set_video_window (callid_t call_id, int flags, int x, int y, int h, int w);
+void lsm_get_video_window (callid_t call_id, int *flags, int *x, int *y, int *h, int *w);
+boolean lsm_is_kpml_subscribed (callid_t call_id);
+void
+lsm_util_tone_start_with_speaker_as_backup (vcm_tones_t tone, short alert_info,
+                                    cc_call_handle_t call_handle, groupid_t group_id,
+                                    streamid_t stream_id, uint16_t direction);
+
+void lsm_data_channel_negotiated (line_t line, callid_t call_id, fsmdef_media_t *media, int *pc_stream_id);
+
+#endif //_LSM_H_
+
diff --git a/libs/sipcc/core/gsm/h/lsm_private.h b/libs/sipcc/core/gsm/h/lsm_private.h
new file mode 100644 (file)
index 0000000..438cc86
--- /dev/null
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _LSM_PRIV_H_
+#define _LSM_PRIV_H_
+
+#include "phone_types.h"
+#include "ccapi.h"
+
+#define LSM_ERR_MSG err_msg
+
+#define LSM_DEFAULT_LINE     (1)
+#define LSM_DEFAULT_INSTANCE (1)
+#define LSM_DEFAULT_PLANE    (1)
+#define LSM_DEFAULT_SPEAKER  (0)
+
+#define LSM_MAX_LCBS         (LSM_MAX_CALLS)
+
+#define LSM_FLAGS_SECURE_MEDIA         (1 << 0) /* encrypted tx media              */
+#define LSM_FLAGS_SECURE_MEDIA_UPDATE  (1 << 1) /* update secure media icon needed */
+#define LSM_FLAGS_ANSWER_PENDING       (1 << 2) /* answer is pending */
+#define LSM_FLAGS_DIALED_STRING        (1 << 3) /* dialed a string */
+#define LSM_FLAGS_CALL_PRIORITY_URGENT  (1 << 4)
+#define LSM_FLAGS_PREVENT_RINGING       (1 << 5)
+#define LSM_FLAGS_DUSTING               (1 << 6) /* the call is a dusting call */
+
+
+typedef struct lsm_lcb_t_ {
+    callid_t     call_id;
+    line_t       line;
+    call_events  previous_call_event;
+    lsm_states_t state;
+    int          mru;
+    boolean      enable_ringback;
+    callid_t     ui_id;
+    uint32_t     flags;
+    fsmdef_dcb_t *dcb; /* the corresponding DCB chain for this lcm */
+    char         *gcid; /*global call identifier */
+    int           vid_mute;
+    int           vid_flags;
+    int           vid_x;
+    int           vid_y;
+    int           vid_h;
+    int           vid_w;
+} lsm_lcb_t;
+
+typedef struct lsm_info_t_ {
+    int      call_in_progress;
+    callid_t active_call_id;
+    int      active_call_plane;
+    line_t   primary_line;
+    int      speaker;
+} lsm_info_t;
+
+lsm_lcb_t *lsm_get_lcb_by_call_id(callid_t call_id);
+
+#endif
diff --git a/libs/sipcc/core/gsm/h/sm.h b/libs/sipcc/core/gsm/h/sm.h
new file mode 100755 (executable)
index 0000000..840aced
--- /dev/null
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SM_H_
+#define _SM_H_
+
+#include "phone_debug.h"
+
+typedef enum {
+    SM_RC_MIN = -2,
+    SM_RC_ERROR = -1,
+    SM_RC_SUCCESS,
+    SM_RC_CONT,
+    SM_RC_DEF_CONT,
+    SM_RC_END,
+    SM_RC_CLEANUP,
+    SM_RC_FSM_ERROR,
+    SM_RC_CSM_ERROR,
+    SM_RC_MAX
+} sm_rcs_t;
+
+typedef struct _sm_event_t {
+    int  state;
+    int  event;
+    void *data;
+    void *msg;
+} sm_event_t;
+
+typedef sm_rcs_t (*sm_function_t)(sm_event_t *event);
+
+typedef struct _sm_table_t {
+    int min_state;
+    int max_state;
+    int min_event;
+    int max_event;
+    sm_function_t *table;
+} sm_table_t;
+
+sm_rcs_t sm_process_event(sm_table_t *tbl, sm_event_t *event );
+sm_rcs_t sm_process_event2(int state_id, int event_id, sm_table_t *tbl,
+                           void *msg, void *cb);
+
+#endif
diff --git a/libs/sipcc/core/gsm/lsm.c b/libs/sipcc/core/gsm/lsm.c
new file mode 100755 (executable)
index 0000000..5f234c0
--- /dev/null
@@ -0,0 +1,6609 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <time.h>
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_locks.h"
+#include "cpr_stdio.h"
+#include "cpr_timers.h"
+#include "cpr_in.h"
+#include "cpr_errno.h"
+#include "phone_types.h"
+#include "phone.h"
+#include "sdp.h"
+#include "lsm.h"
+#include "phone_debug.h"
+#include "fsm.h"
+#include "gsm_sdp.h"
+#include "vcm.h"
+#include "ccsip_pmh.h"
+#include "dtmf.h"
+#include "debug.h"
+#include "rtp_defs.h"
+#include "lsm_private.h"
+#include "dialplanint.h"
+#include "kpmlmap.h"
+#include "prot_configmgr.h"
+#include "dialplan.h"
+#include "sip_interface_regmgr.h"
+#include "gsm.h"
+#include "phntask.h"
+#include "fim.h"
+#include "util_string.h"
+#include "platform_api.h"
+
+#ifndef NO
+#define NO  (0)
+#endif
+
+#ifndef YES
+#define YES (1)
+#endif
+
+#define CALL_INFO_NONE  (string_t)""
+
+#define FROM_NOTIFY_PRI   1 // Same as SCCP phone behavior
+#define LSM_DISPLAY_STR_LEN 256
+
+static cc_rcs_t lsm_stop_tone (lsm_lcb_t *lcb, cc_action_data_tone_t *data);
+
+extern cc_media_cap_table_t g_media_table;
+
+static lsm_lcb_t *lsm_lcbs;
+static uint32_t lsm_call_perline[MAX_REG_LINES];
+boolean lsm_mnc_reached[MAX_REG_LINES]; // maxnumcalls reached
+static boolean lsm_bt_reached[MAX_REG_LINES]; //busy trigger reached
+
+/* This variable is used locally to reflect the CFA state (set/clear)
+ * when in CCM mode.
+ */
+static boolean cfwdall_state_in_ccm_mode[MAX_REG_LINES+1] ;
+
+static const char *lsm_state_names[LSM_S_MAX] = {
+    "IDLE",
+    "PENDING",
+    "OFFHOOK",
+    "ONHOOK",
+    "PROCEED",
+    "RINGOUT",
+    "RINGIN",
+    "CONNECTED",
+    "BUSY",
+    "CONGESTION",
+    "HOLDING",
+    "CWT",
+    "XFER",
+    "ATTN_XFER",
+    "CONF",
+    "INVALID_NUMBER"
+};
+
+static const char *cc_state_names[] = {
+    "OFFHOOK",
+    "DIALING",
+    "DIALING_COMPLETED",
+    "CALL_SENT",
+    "FAR_END_PROCEEDING",
+    "FAR_END_ALERTING",
+    "CALL_RECEIVED",
+    "ALERTING",
+    "ANSWERED",
+    "CONNECTED",
+    "HOLD",
+    "RESUME",
+    "ONHOOK",
+    "CALL_FAILED",
+    "HOLD_REVERT",
+    "STATE_UNKNOWN"
+};
+
+
+static const char *cc_action_names[] = {
+    "SPEAKER",
+    "DIAL_MODE",
+    "MWI",
+    "MWI_LAMP",
+    "OPEN_RCV",
+    "UPDATE_UI",
+    "MEDIA",
+    "RINGER",
+    "LINE_RINGER",
+    "PLAY_TONE",
+    "STOP_TONE",
+    "STOP_MEDIA",
+    "START_RCV",
+    "ANSWER_PENDING",
+    "PLAY_BLF_ALERT_TONE"
+};
+
+/* names are corresponds to vcm_ring_mode_t structure */
+static const char *vm_alert_names[] = {
+    "NONE",
+//    "RINGER_OFF",
+    "VCM_RING_OFF",
+    "VCM_INSIDE_RING",
+    "VCM_OUTSIDE_RING",
+    "VCM_FEATURE_RING",
+    "VCM_BELLCORE_DR1",
+    "VCM_RING_OFFSET",
+    "VCM_BELLCORE_DR2",
+    "VCM_BELLCORE_DR3",
+    "VCM_BELLCORE_DR4",
+    "VCM_BELLCORE_DR5",
+    "VCM_BELLCORE_MAX",
+    "VCM_FLASHONLY_RING",
+    "VCM_STATION_PRECEDENCE_RING",
+    "VCM_MAX_RING"
+};
+
+/* Enum just to make code read better */
+/* the following values must be in sync with the values listed in edcs-387610 */
+typedef enum {
+    DISABLE = 1,
+    FLASH_ONLY = 2,
+    RING_ONCE = 3,
+    RING = 4,
+    BEEP_ONLY = 5
+} config_value_type_t;
+
+cprTimer_t lsm_tmr_tones;
+cprTimer_t lsm_continuous_tmr_tones;
+cprTimer_t lsm_tone_duration_tmr;
+static uint32_t lsm_tmr_tones_ticks;
+static int callWaitingDelay;
+static int ringSettingIdle;
+static int ringSettingActive;
+
+/* Ring mode set by remote-cc app */
+static cc_rcc_ring_mode_e cc_line_ringer_mode[MAX_REG_LINES+1] =
+    {CC_RING_DEFAULT};
+// Following data has to be non-stack b/c the way SIP stack uses it.
+// It is used by the lsm_is_phone_forwarded() function only.
+static char cfwdall_url[MAX_URL_LENGTH];
+
+static void lsm_update_inalert_status(line_t line, callid_t call_id,
+                                      cc_state_data_alerting_t * data,
+                                      boolean notify);
+static void lsm_util_start_tone(vcm_tones_t tone, short alert_info,
+        cc_call_handle_t call_handle, groupid_t group_id,
+        streamid_t stream_id, uint16_t direction);
+
+const char *
+lsm_state_name (lsm_states_t id)
+{
+    if ((id <= LSM_S_MIN) || (id >= LSM_S_MAX)) {
+        return get_debug_string(GSM_UNDEFINED);
+    }
+
+    return (lsm_state_names[id]);
+}
+
+
+static const char *
+cc_state_name (cc_states_t id)
+{
+    if ((id <= CC_STATE_MIN) || (id >= CC_STATE_MAX)) {
+        return (get_debug_string(GSM_UNDEFINED));
+    }
+
+    return (cc_state_names[id]);
+}
+
+
+static const char *
+cc_action_name (cc_actions_t id)
+{
+    if ((id <= CC_ACTION_MIN) || (id >= CC_ACTION_MAX)) {
+        return (get_debug_string(GSM_UNDEFINED));
+    }
+
+    return (cc_action_names[id]);
+}
+
+
+char
+lsm_digit2ch (int digit)
+{
+    switch (digit) {
+    case 0x0:
+    case 0x1:
+    case 0x2:
+    case 0x3:
+    case 0x4:
+    case 0x5:
+    case 0x6:
+    case 0x7:
+    case 0x8:
+    case 0x9:
+        return (char)(digit + '0');
+    case 0x0e:
+        return ('*');
+    case 0x0f:
+        return ('#');
+    default:
+        return ('x');
+    }
+}
+
+
+void
+lsm_debug_entry (callid_t call_id, line_t line, const char *fname)
+{
+    LSM_DEBUG(get_debug_string(LSM_DBG_ENTRY), call_id, line, fname);
+}
+
+static void
+lsm_ui_call_state (call_events event, line_t line, lsm_lcb_t *lcb, cc_causes_t cause)
+{
+    if (lcb->previous_call_event != event) {
+        lcb->previous_call_event = event;
+
+        /* For local conference case, the second call is hidden
+         * so do not show that when the call is held, resumed,
+         * or moved to other state. This should be done only
+         * when local bridge is active
+         */
+        ui_call_state(event, line, lcb->ui_id, cause);
+    }
+    else if(event == evConnected) {
+       //This is for Chaperone Conference case, if conference changed to a normal call,
+       //then need to update the call state to refresh the key's status. like re-enable
+       //Confrn key
+        ui_call_state(event, line, lcb->ui_id, cause);
+    }
+}
+
+/**
+ * This function will control the display of the ringingin call based on the hide arg.
+ *
+ * @param[in] call_id -  call id
+ * @param[in] line - line on which call is ringing.
+ * @param[in] hide - whether to hide or not
+ *
+ * @return none
+ *
+ * @pre (call_id != CC_NO_CALL_ID) and (line != 0)
+ */
+void lsm_display_control_ringin_call (callid_t call_id, line_t line, boolean hide)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        ui_call_state(evRingIn, line, lcb->ui_id, CC_CAUSE_NORMAL);
+    }
+}
+
+/**
+ * This function will be invoked by DEF SM to set if it is a dusting call.
+ * @param[in] call_id - GSM call id.
+ *
+ * @return none
+ *
+ * @pre (call_id != CC_NO_CALL_ID)
+ */
+void lsm_set_lcb_dusting_call (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        FSM_SET_FLAGS(lcb->flags, LSM_FLAGS_DUSTING);
+    }
+}
+
+/**
+ * This function will be invoked by DEF SM to set call priority.
+ *
+ * @param[in] call_id - GSM call id.
+ *
+ * @return none
+ *
+ * @pre (call_id != CC_NO_CALL_ID)
+ */
+void lsm_set_lcb_call_priority (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        FSM_SET_FLAGS(lcb->flags, LSM_FLAGS_CALL_PRIORITY_URGENT);
+    }
+}
+
+
+/**
+ * This function sets the LSM_FLAGS_DIALED_STRING bit in lcb->flags
+ *
+ * @param[in] call_id - GSM call id.
+ *
+ * @return none
+ *
+ * @pre (call_id != CC_NO_CALL_ID)
+ */
+void lsm_set_lcb_dialed_str_flag (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        FSM_SET_FLAGS(lcb->flags, LSM_FLAGS_DIALED_STRING);
+    }
+}
+
+/**
+ * This function will be invoked by DEF SM to set gcid in lcb.
+ *
+ * @param[in] call_id - GSM call id.
+ * @param[in] gcid - GCID provided by CUCM.
+ *
+ * @return none
+ *
+ * @pre (call_id != CC_NO_CALL_ID)
+ */
+void lsm_update_gcid (callid_t call_id, char * gcid)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        if (lcb->gcid == NULL) {
+            lcb->gcid = (char *)cpr_malloc(CC_GCID_LEN);
+            sstrncpy(lcb->gcid, gcid, CC_GCID_LEN);
+        }
+    }
+
+}
+/**
+ * This function will be invoked by DEF SM.
+ * it will check if there is a RINGIN call
+ * with the same GCID. If so, it will set a flag to prevent ringing.
+ *
+ * @param[in] call_id - GSM call id.
+ *
+ * @return none
+ *
+ * @pre (call_id != CC_NO_CALL_ID)
+ */
+void lsm_set_lcb_prevent_ringing (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+    char *gcid;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb == NULL) {
+        return;
+    }
+
+    gcid = lcb->gcid;
+    if (gcid == NULL) {
+        return;
+    }
+
+    LSM_DEBUG(DEB_L_C_F_PREFIX"gcid=%d.\n",
+              DEB_L_C_F_PREFIX_ARGS(LSM, lcb->line, call_id, "lsm_set_lcb_prevent_ringing"), gcid);
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if (lcb->state == LSM_S_RINGIN) {
+            if ((lcb->gcid != NULL) && (strncmp(gcid, lcb->gcid, CC_GCID_LEN) == 0)) {
+                LSM_DEBUG(DEB_L_C_F_PREFIX"found ringing call.\n",
+                          DEB_L_C_F_PREFIX_ARGS(LSM, lcb->line, lcb->call_id, "lsm_set_lcb_prevent_ringing"), gcid);
+                FSM_SET_FLAGS(lcb->flags, LSM_FLAGS_PREVENT_RINGING);
+            }
+            break;
+        }
+    }
+}
+
+void lsm_remove_lcb_prevent_ringing (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+    char *gcid;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb == NULL) {
+        return;
+    }
+
+    gcid = lcb->gcid;
+    if (gcid == NULL) {
+        return;
+    }
+
+    LSM_DEBUG(DEB_L_C_F_PREFIX"gcid=%d.\n",
+              DEB_L_C_F_PREFIX_ARGS(LSM, lcb->line, call_id, "lsm_remove_lcb_prevent_ringing"), gcid);
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if (lcb->state == LSM_S_RINGIN) {
+            if ((lcb->gcid != NULL) && (strncmp(gcid, lcb->gcid, CC_GCID_LEN) == 0)) {
+                //FSM_RESET_FLAGS(lcb->flags, LSM_FLAGS_ANSWER_PENDING);
+                lcb->flags = 0;
+                LSM_DEBUG(DEB_L_C_F_PREFIX"found ringing call, gcid=%d, lcb->flags=%d.\n",
+                          DEB_L_C_F_PREFIX_ARGS(LSM, lcb->line, lcb->call_id, "lsm_remove_lcb_prevent_ringing"), gcid, lcb->flags);
+            }
+            break;
+        }
+    }
+}
+
+/**
+ * This function finds if the call is a priority call.
+ *
+ * @param[in] call_id - GSM call id.
+ *
+ * @return none
+ *
+ * @pre (call_id != CC_NO_CALL_ID)
+ */
+boolean lsm_is_it_priority_call (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb == NULL) {
+        return FALSE;
+    }
+    if (FSM_CHK_FLAGS(lcb->flags, LSM_FLAGS_CALL_PRIORITY_URGENT)) {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ *  Function: lsm_internal_update_call_info
+ *
+ *  Parameters:
+ *     lcb - pointer to lsm_lcb_t.
+ *     dcb - pointer to fsmdef_dcb_t.
+ *
+ *  Description: This is an internal function of LSM for updating call
+ *               information to the UI that is not directly driven by the
+ *               the explicit CALL INFO event from call control.
+ *
+ *               It is a convenient function that used by some of the
+ *               LSM handling function that needs to update call information
+ *               to the UI during media changes.
+ *
+ *  Returns:
+ *     None.
+ *
+ */
+static void
+lsm_internal_update_call_info (lsm_lcb_t *lcb, fsmdef_dcb_t *dcb)
+{
+    boolean inbound;
+    calltype_t call_type;
+    fsmcnf_ccb_t   *ccb;
+
+    if ((lcb == NULL) || (dcb == NULL)) {
+        return;
+    }
+
+    if (!dcb->ui_update_required) {
+        return;
+    }
+
+    /* For local conference, do not update the primary
+     * call bubbles call-info. Primary call is already
+     * displaying To conference in this case
+     * But dcb-> caller_id should be updated to
+     * refresh the UI when the call is dropped
+     */
+    ccb = fsmcnf_get_ccb_by_call_id(lcb->call_id);
+    if (ccb && (ccb->flags & LCL_CNF) && (ccb->active)
+             && (ccb->cnf_call_id == lcb->call_id)) {
+        return;
+    }
+    dcb->ui_update_required = FALSE;
+
+    /* Derive orientation of the call */
+    switch (dcb->orientation) {
+    case CC_ORIENTATION_FROM:
+        inbound = TRUE;
+        break;
+
+    case CC_ORIENTATION_TO:
+        inbound = FALSE;
+        break;
+
+    default:
+        /*
+         * No orientation available, use the direction when call was started
+         */
+        inbound = dcb->inbound;
+        break;
+    }
+
+    if ((dcb->call_type == FSMDEF_CALL_TYPE_FORWARD)
+        && fsmdef_check_retain_fwd_info_state()) {
+        call_type = (inbound) ? (calltype_t)dcb->call_type:FSMDEF_CALL_TYPE_OUTGOING;
+    } else {
+        if (inbound) {
+            call_type = FSMDEF_CALL_TYPE_INCOMING;
+        } else {
+            call_type = FSMDEF_CALL_TYPE_OUTGOING;
+        }
+    }
+
+    ui_call_info(dcb->caller_id.calling_name,
+                 dcb->caller_id.calling_number,
+                 dcb->caller_id.alt_calling_number,
+                 dcb->caller_id.display_calling_number,
+                 dcb->caller_id.called_name,
+                 dcb->caller_id.called_number,
+                 dcb->caller_id.display_called_number,
+                 dcb->caller_id.orig_called_name,
+                 dcb->caller_id.orig_called_number,
+                 dcb->caller_id.last_redirect_name,
+                 dcb->caller_id.last_redirect_number,
+                 call_type,
+                 lcb->line, lcb->ui_id,
+                 dcb->caller_id.call_instance_id,
+                 FSM_GET_SECURITY_STATUS(dcb),
+                 FSM_GET_POLICY(dcb));
+
+}
+
+/**
+ * The function opens receive channel or allocates receive port. It depends
+ * on the "keep" member of the cc_action_data_open_rcv_t structure set
+ * up by the caller to whether opens a receive channel or just
+ * to allocate a receive port.
+ *
+ * @param[in] lcb       - pointer to the lsm_lcb_t.
+ * @param[in/out] data  - pointer to the cc_action_data_open_rcv_t.
+ *                        Upon a successful return, the port element
+ *                        of this structure will be filled with the actual
+ *                        receive port.
+ * @param[in]     media - pointer to the fsmdef_media_t if a specific
+ *                        media to be operated on.
+ *
+ * @return              CC_RC_ERROR or CC_RC_SUCCESS.
+ *
+ * @pre (lcb is_not NULL) and (data is_not NULL)
+ */
+static cc_rcs_t
+lsm_open_rx (lsm_lcb_t *lcb, cc_action_data_open_rcv_t *data,
+             fsmdef_media_t *media)
+{
+    static const char fname[] = "lsm_open_rx";
+    int           port_allocated = 0;
+    cc_rcs_t      rc = CC_RC_ERROR;
+    fsmdef_dcb_t *dcb;
+    int           sdpmode = 0;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (rc);
+    }
+
+    /*
+     * P2: At this point, it is a guess that refid from media structure
+     *     may be needed. If it turns out to be not the case, then the
+     *     code below that looks up media should be removed including
+     *     the media parameter that is passed in.
+     */
+    if (media == NULL) {
+        /* no explicit media parameter specified, look up based on refID */
+        if (data->media_refid != CC_NO_MEDIA_REF_ID) {
+            media = gsmsdp_find_media_by_refid(dcb,
+                                               data->media_refid);
+        }
+        if (media == NULL) {
+            LSM_DEBUG(get_debug_string(LSM_DBG_INT1), lcb->call_id,
+                      lcb->line, fname, "no media refID %d found",
+                      data->media_refid);
+            return (rc);
+        }
+    }
+
+    LSM_DEBUG(get_debug_string(LSM_DBG_INT1), lcb->call_id, lcb->line, fname,
+              "requested port", data->port);
+
+
+    sdpmode = 0;
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    if (data->keep == TRUE) {
+      if (sdpmode && strlen(dcb->peerconnection)) {
+        /* If we are doing ICE, don't try to re-open */
+        port_allocated = data->port;
+      }
+      else {
+        //Todo IPv6: Add interface call for IPv6
+        (void) vcmRxOpen(media->cap_index, dcb->group_id, media->refid,
+          lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id), data->port,
+          media->is_multicast ? &media->dest_addr:&media->src_addr, data->is_multicast,
+          &port_allocated);
+      }
+      if (port_allocated != -1) {
+        data->port = (uint16_t)port_allocated;
+        rc = CC_RC_SUCCESS;
+      }
+    } else {
+
+      if (sdpmode) {
+        if (!strlen(dcb->peerconnection)) {
+          vcmRxAllocPort(media->cap_index, dcb->group_id, media->refid,
+            lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id),
+            data->port,
+            &port_allocated);
+          if (port_allocated != -1) {
+            data->port = (uint16_t)port_allocated;
+            rc = CC_RC_SUCCESS;
+          }
+        } else {
+          char **candidates;
+          int candidate_ct;
+          char *default_addr;
+
+          vcmRxAllocICE(media->cap_index, dcb->group_id, media->refid,
+            lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id),
+            dcb->peerconnection,
+            media->level,
+            &default_addr, &port_allocated,
+            &candidates, &candidate_ct);
+
+          // Check that we got a valid address and port
+          if (default_addr && (strlen(default_addr) > 0) && (port_allocated != -1)) {
+            sstrncpy(dcb->ice_default_candidate_addr, default_addr, sizeof(dcb->ice_default_candidate_addr));
+
+            data->port = (uint16_t)port_allocated;
+            media->candidate_ct = candidate_ct;
+            media->candidatesp = candidates;
+            rc = CC_RC_SUCCESS;
+          }
+        }
+      }
+    }
+
+    LSM_DEBUG(get_debug_string(LSM_DBG_INT1), lcb->call_id, lcb->line, fname,
+              "allocated port", port_allocated);
+
+    return (rc);
+}
+/*
+ * This function updates the dscp value based on whether video is enable or not
+ * and video is active or not.
+ * @param[in] dcb       - pointer to the fsmdef_dcb.
+ */
+
+void lsm_update_dscp_value(fsmdef_dcb_t   *dcb)
+{
+    static const char fname[] = "lsm_update_dscp_value";
+    int dscp = 184;   /* default 184 used for DSCP */
+    // depending upon video is enabled or disabled ,set the dscp value.
+    if (dcb != NULL && dcb->cur_video_avail != SDP_DIRECTION_INACTIVE ) {
+        config_get_value(CFGID_DSCP_VIDEO, (int *)&dscp, sizeof(dscp));
+    } else {
+        config_get_value(CFGID_DSCP_AUDIO, (int *)&dscp, sizeof(dscp));
+    }
+    // We would use DSCP for video for both audio and video streams if this is a video call
+    if (dcb != NULL) {
+        LSM_DEBUG(DEB_L_C_F_PREFIX"Setting dscp=%d for Rx group_id=%d \n",
+            DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname), dscp,  dcb->group_id);
+        vcmSetRtcpDscp(dcb->group_id, dscp);
+    }
+}
+
+/**
+ * The function closes receive channel for a given media entry.
+ * The receive channel may not be closed if the caller intents to
+ * fresh the channel i.e close if needed but otherwise leave it open.
+ * When the caller indicates refreshing, the receive channel
+ * will be closed only when there is a difference in current SDP and
+ * the previous SDP.
+ *
+ * @param[in] lcb       - pointer to the lsm_lcb_t.
+ * @param[in] refresh   - channel to be refreshed i.e. close if necessary.
+ * @param[in] media     - pointer to the fsmdef_media_t for the
+ *                        media entry to be refresh.
+ *
+ *                        If the value of media is NULL, it indicates that
+ *                        all current inused media entries.
+ *
+ * @return              None.
+ *
+ * @pre (lcb is_not NULL)
+ */
+static void
+lsm_close_rx (lsm_lcb_t *lcb, boolean refresh, fsmdef_media_t *media)
+{
+    static const char fname[] = "lsm_close_rx";
+    fsmdef_media_t *start_media, *end_media;
+    fsmdef_dcb_t   *dcb;
+    int             sdpmode = 0;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname);
+        return;
+    }
+
+    LSM_DEBUG(DEB_L_C_F_PREFIX"Called with refresh set to %d\n",
+              DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname), refresh);
+
+    if (media == NULL) {
+        /* NULL value of the given media indicates for all media */
+        start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb);
+        end_media   = NULL; /* NULL means till the end of the list */
+    } else {
+        /* given media, uses the provided media */
+        start_media = media;
+        end_media   = media;
+    }
+
+    /* close receive port on the media(s) */
+    GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb) {
+        if (media->rcv_chan) {
+            /*
+             * If caller is releasing the port or if the caller is
+             * recycling the receive port and the codec has changed, close the
+             * receive port. Also stop bridging of media streams.
+             */
+            if (!refresh ||
+                (refresh &&
+                 gsmsdp_sdp_differs_from_previous_sdp(TRUE, media))) {
+                LSM_DEBUG(get_debug_string(LSM_DBG_INT1), dcb->call_id,
+                          dcb->line, fname, "port closed",
+                          media->src_port);
+
+                config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+                if (!sdpmode) {
+
+                    vcmRxClose(media->cap_index, dcb->group_id, media->refid,
+                             lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id));
+                }
+                media->rcv_chan = FALSE;
+            }
+        }
+    }
+}
+
+/**
+ * The function closes transmit channel for a given media entry.
+ * The transmit channel may not be closed if the caller intents to
+ * fresh the port i.e close if needed but otherwise leave it open.
+ * When the caller indicates refreshing, the transmit channel
+ * will be closed only when there is a difference in current SDP and
+ * the previous SDP.
+ *
+ * @param[in] lcb       - pointer to the lsm_lcb_t.
+ * @param[in] refresh   - channel to be refreshed i.e. close if necessary.
+ * @param[in] media     - pointer to the fsmdef_media_t for the
+ *                        media entry to be refresh.
+ *
+ *                        If the value of media is NULL, it indicates that
+ *                        all current inused media entries.
+ *
+ * @return              None.
+ *
+ * @pre (lcb is_not NULL)
+ */
+static void
+lsm_close_tx (lsm_lcb_t *lcb, boolean refresh, fsmdef_media_t *media)
+{
+    fsmdef_media_t *start_media, *end_media;
+    fsmdef_dcb_t   *dcb;
+    static const char fname[] = "lsm_close_tx";
+    int             sdpmode = 0;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname);
+        return;
+    }
+    LSM_DEBUG(DEB_L_C_F_PREFIX"called with refresh set to %d\n",
+              DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname), refresh);
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    if (media == NULL) {
+        /* NULL value of the given media indicates for all media */
+        start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb);
+        end_media   = NULL; /* NULL means till the end of the list */
+    } else {
+        /* given media, uses the provided media */
+        start_media = media;
+        end_media   = media;
+    }
+
+    /*
+     * Close the RTP, but only if this call is using it and the capabilities
+     * have changed.
+     */
+    GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb) {
+        if (media->xmit_chan == TRUE) {
+
+            if (!refresh ||
+                (refresh &&
+                 gsmsdp_sdp_differs_from_previous_sdp(FALSE, media))) {
+
+                if (!sdpmode) {
+                    vcmTxClose(media->cap_index, dcb->group_id, media->refid,
+                        lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id));
+                }
+
+                if (dcb->active_tone == VCM_MONITORWARNING_TONE || dcb->active_tone == VCM_RECORDERWARNING_TONE) {
+                    LSM_DEBUG(DEB_L_C_F_PREFIX"%s: Found active_tone: %d being played, current monrec_tone_action: %d. Need stop tone. \n",
+                              DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname), fname,
+                              dcb->active_tone, dcb->monrec_tone_action);
+                    (void) lsm_stop_tone(lcb, NULL);
+                }
+                media->xmit_chan = FALSE;
+                LSM_DEBUG(DEB_L_C_F_PREFIX"closed",
+                          DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname));
+            }
+        }
+    }
+}
+
+/**
+ * The function starts receive channel for a given media entry.
+ *
+ * @param[in] lcb       - pointer to the lsm_lcb_t.
+ * @param[in] fname     - pointer to to const. char for the name
+ *                        of the function that calls to this function.
+ *                        It is for debuging purpose.
+ * @param[in] media     - pointer to the fsmdef_media_t for the
+ *                        media entry to be refresh.
+ *
+ *                        If the value of media is NULL, it indicates that
+ *                        all current inused media entries.
+ *
+ * @return              None.
+ *
+ * @pre (lcb is_not NULL)
+ */
+static void
+lsm_rx_start (lsm_lcb_t *lcb, const char *fname, fsmdef_media_t *media)
+{
+    static const char fname1[] = "lsm_rx_start";
+    cc_action_data_open_rcv_t open_rcv;
+    uint16_t port;
+    groupid_t         group_id = CC_NO_GROUP_ID;
+    callid_t          call_id  = lcb->call_id;
+    vcm_mixing_mode_t mix_mode = VCM_NO_MIX;
+    vcm_mixing_party_t mix_party = VCM_PARTY_NONE;
+    int ret_val;
+    fsmdef_media_t *start_media, *end_media;
+    boolean has_checked_conference = FALSE;
+    fsmdef_dcb_t   *dcb, *grp_id_dcb;
+    vcm_mediaAttrs_t attrs;
+    int              sdpmode = 0;
+    int pc_stream_id = 0;
+    int pc_track_id = 0;
+    attrs.video.opaque = NULL;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname1);
+        return;
+    }
+    group_id = dcb->group_id;
+    if (media == NULL) {
+        /* NULL value of the given media indicates for all media */
+        start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb);
+        end_media   = NULL; /* NULL means till the end of the list */
+    } else {
+        /* given media, uses the provided media */
+        start_media = media;
+        end_media   = media;
+    }
+
+    /* Start receive channel for the media(s) */
+    GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            /* this entry is not enabled */
+            continue;
+        }
+
+        /*
+         * Check to see if the receive port can be opened.
+         * For SRTP, the receive can not be opened if the remote's crypto
+         * parameters are not received yet.
+         */
+        if (!gsmsdp_is_crypto_ready(media, TRUE)) {
+            LSM_DEBUG(DEB_L_C_F_PREFIX"%s: Not ready to open receive port (%d)\n",
+                      DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname, media->src_port);
+            continue;
+        }
+
+        /*
+         * Open the RTP receive channel if it is not already open.
+         */
+        LSM_DEBUG(get_debug_string(LSM_DBG_INT1), dcb->call_id, dcb->line,
+                  fname1, "rcv chan", media->rcv_chan);
+        if (media->rcv_chan == FALSE) {
+
+            memset(&open_rcv, 0, sizeof(open_rcv));
+            port = media->src_port;
+
+            if (media->is_multicast &&
+                (media->direction == SDP_DIRECTION_RECVONLY)) {
+                open_rcv.is_multicast = media->is_multicast;
+                open_rcv.listen_ip    = media->dest_addr;
+                port                  = media->multicast_port;
+            }
+            open_rcv.port = port;
+            open_rcv.keep = TRUE;
+            open_rcv.media_type = media->type;
+
+            if (!has_checked_conference) {
+                switch(dcb->session)
+                {
+                    case WHISPER_COACHING:
+                        mix_mode  = VCM_MIX;
+                        mix_party = VCM_PARTY_TxBOTH_RxNONE;
+                        grp_id_dcb = fsmdef_get_dcb_by_call_id(dcb->join_call_id);
+                        if (grp_id_dcb == NULL) {
+                            LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname1);
+                        } else {
+                            group_id = grp_id_dcb->group_id;
+                        }
+                        break;
+
+                    case MONITOR:
+                    case LOCAL_CONF:
+                       //AgentGreeting is MIX RXBOTH, SilentMonitoring is MIX TXBOTH
+                       //so we have to use VCM_PARTY_BOTH for case MONITOR
+                        mix_mode  = VCM_MIX;
+                        mix_party = VCM_PARTY_BOTH;
+                        break;
+                    case PRIMARY:
+                    default:
+                        mix_mode  = VCM_NO_MIX;
+                        mix_party = VCM_PARTY_NONE;
+                        break;
+                }
+                has_checked_conference = TRUE;
+            }
+
+            if (lsm_open_rx(lcb, &open_rcv, media) != CC_RC_SUCCESS) {
+                LSM_ERR_MSG(LSM_L_C_F_PREFIX"%s: open receive port (%d) failed.\n",
+                            dcb->line, dcb->call_id, fname1,
+                                                       fname, media->src_port);
+            } else {
+                /* successful open receive channel */
+                media->rcv_chan = TRUE; /* recevied channel is created */
+                /* save the source RX port */
+                if (media->is_multicast) {
+                    media->multicast_port = open_rcv.port;
+                } else {
+                    media->src_port = open_rcv.port;
+                }
+
+                /* TODO(ekr@rtfm.com): Needs changing for when we have > 2 streams */
+                if ( media->cap_index == CC_VIDEO_1 ) {
+                    attrs.video.opaque = media->video;
+                    pc_stream_id = 1;
+                } else {
+                    attrs.audio.packetization_period = media->packetization_period;
+                    attrs.audio.max_packetization_period = media->max_packetization_period;
+                    attrs.audio.avt_payload_type = media->avt_payload_type;
+                    attrs.audio.mixing_mode = mix_mode;
+                    attrs.audio.mixing_party = mix_party;
+                    pc_stream_id = 0;
+                }
+                pc_track_id = 0;
+                dcb->cur_video_avail &= ~CC_ATTRIB_CAST;
+
+                config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+                if (dcb->peerconnection) {
+                    ret_val = vcmRxStartICE(media->cap_index, group_id, media->refid,
+                    media->level,
+                    pc_stream_id,
+                    pc_track_id,
+                    lsm_get_ms_ui_call_handle(dcb->line, call_id, CC_NO_CALL_ID),
+                    dcb->peerconnection,
+                    media->num_payloads,
+                    media->payloads,
+                    FSM_NEGOTIATED_CRYPTO_DIGEST_ALGORITHM(media),
+                    FSM_NEGOTIATED_CRYPTO_DIGEST(media),
+                    &attrs);
+                } else if (!sdpmode) {
+                    if (media->payloads == NULL) {
+                        LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname1);
+                        return;
+                    }
+                    ret_val =  vcmRxStart(media->cap_index, group_id, media->refid,
+                                          lsm_get_ms_ui_call_handle(dcb->line, call_id, CC_NO_CALL_ID),
+                                          media->payloads,
+                                          media->is_multicast ? &media->dest_addr:&media->src_addr,
+                                          port,
+                                          FSM_NEGOTIATED_CRYPTO_ALGORITHM_ID(media),
+                                          FSM_NEGOTIATED_CRYPTO_RX_KEY(media),
+                                          &attrs);
+                    if (ret_val == -1) {
+                        dcb->dsp_out_of_resources = TRUE;
+                        return;
+                    }
+                } else {
+                    ret_val = CC_RC_ERROR;
+                }
+
+                lsm_update_dscp_value(dcb);
+
+                if (dcb->play_tone_action == FSMDEF_PLAYTONE_ZIP)
+                {
+                    vcm_tones_t tone = VCM_ZIP;
+                    uint16_t    direction = dcb->tone_direction;
+
+                    LSM_DEBUG(DEB_L_C_F_PREFIX"%s: Found play_tone_action: %d. Need to play tone.\n",
+                              DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname), fname, dcb->play_tone_action);
+
+                    // reset to initialized values
+                    dcb->play_tone_action = FSMDEF_PLAYTONE_NO_ACTION;
+                    dcb->tone_direction = VCM_PLAY_TONE_TO_EAR;
+
+                    lsm_util_tone_start_with_speaker_as_backup(tone, VCM_ALERT_INFO_OFF,
+                                                               lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID),
+                                                               dcb->group_id,
+                                                               ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                                               direction);
+                }
+            }
+        }
+    }
+}
+
+/**
+ * The function starts transmit channel for a given media entry.
+ *
+ * @param[in] lcb       - pointer to the lsm_lcb_t.
+ * @param[in] fname     - pointer to to const. char for the name
+ *                        of the function that calls to this function.
+ *                        It is for debuging purpose.
+ * @param[in] media     - pointer to the fsmdef_media_t for the
+ *                        media entry to be refresh.
+ *
+ *                        If the value of media is NULL, it indicates that
+ *                        all current inused media entries.
+ *
+ * @return              None.
+ *
+ * @pre (lcb is_not NULL)
+ */
+
+#define LSM_TMP_VAD_LEN 64
+
+static void
+lsm_tx_start (lsm_lcb_t *lcb, const char *fname, fsmdef_media_t *media)
+{
+    static const char fname1[] = "lsm_tx_start";
+    int           dscp = 184;   /* default 184 used for DSCP */
+    char          tmp[LSM_TMP_VAD_LEN];
+    fsmcnf_ccb_t *ccb = NULL;
+    groupid_t         group_id;
+    callid_t          call_id  = lcb->call_id;
+    vcm_mixing_mode_t mix_mode = VCM_NO_MIX;
+    vcm_mixing_party_t mix_party = VCM_PARTY_NONE;
+    fsmdef_media_t *start_media, *end_media;
+    boolean        has_checked_conference = FALSE;
+    fsmdef_dcb_t   *dcb;
+    vcm_mediaAttrs_t attrs;
+    int              sdpmode;
+    long strtol_result;
+    char *strtol_end;
+
+    attrs.video.opaque = NULL;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname1);
+        return;
+    }
+    // Set the DSCP value for RTP stream.
+    if ( media != NULL ){
+        // We would use DSCP for video for both audio and video streams if this
+        // is a video call
+        if ( dcb->cur_video_avail != SDP_DIRECTION_INACTIVE ) {
+            config_get_value(CFGID_DSCP_VIDEO, (int *)&dscp, sizeof(dscp));
+        } else if ( CC_IS_AUDIO(media->cap_index)){
+            // audio stream for audio only call shall use the DSCP for audio
+            // value.
+            config_get_value(CFGID_DSCP_AUDIO, (int *)&dscp, sizeof(dscp));
+        }
+    }
+    group_id = dcb->group_id;
+    LSM_DEBUG(DEB_L_C_F_PREFIX"invoked\n", DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1));
+
+    if (media == NULL) {
+        /* NULL value of the given media indicates for all media */
+        start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb);
+        end_media   = NULL; /* NULL means till the end of the list */
+    } else {
+        /* given media, uses the provided media */
+        start_media = media;
+        end_media   = media;
+    }
+
+    /* Start receive channel for the media(s) */
+    GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            /* this entry is not enabled */
+            continue;
+        }
+        /*
+         * Check to see if the transmit port can be opened.
+         * For SRTP, the transmit port can not be opened if the remote's crypto
+         * parameters are not received yet.
+         */
+        if (!gsmsdp_is_crypto_ready(media, FALSE)) {
+            LSM_DEBUG(DEB_L_C_F_PREFIX"%s: Not ready to open transmit port\n",
+                      DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname);
+            continue;
+        }
+        if (media->xmit_chan == FALSE && dcb->remote_sdp_present &&
+            media->dest_addr.type != CPR_IP_ADDR_INVALID && media->dest_port) {
+
+            /* evaluate the mode and group id once for all media entries */
+            if (!has_checked_conference) {
+                switch(dcb->session)
+                {
+                    case WHISPER_COACHING:
+                        mix_mode  = VCM_MIX;
+                        mix_party = VCM_PARTY_TxBOTH_RxNONE;
+                        group_id = fsmdef_get_dcb_by_call_id(dcb->join_call_id)->group_id;
+                        break;
+
+                    case MONITOR:
+                    case LOCAL_CONF:
+                       //AgentGreeting is MIX RXBOTH, SilentMonitoring is MIX TXBOTH
+                       //so we have to use VCM_PARTY_BOTH for case MONITOR
+                        mix_mode  = VCM_MIX;
+                        mix_party = VCM_PARTY_BOTH;
+                        break;
+                    case PRIMARY:
+                    default:
+                        mix_mode  = VCM_NO_MIX;
+                        mix_party = VCM_PARTY_NONE;
+                        break;
+                }
+                has_checked_conference = TRUE;
+            }
+
+            /*
+             *   Set the VAD value.
+             */
+            /* can't use vad on conference calls - the dsp can't handle it. */
+            ccb = fsmcnf_get_ccb_by_call_id(lcb->call_id);
+            if (ccb != NULL) {
+                media->vad = VCM_VAD_OFF;
+            } else {
+                config_get_string(CFGID_ENABLE_VAD, tmp, sizeof(tmp));
+
+                errno = 0;
+
+                strtol_result = strtol(tmp, &strtol_end, 10);
+
+                if (errno || tmp == strtol_end ||
+                    strtol_result < VCM_VAD_OFF || strtol_result > VCM_VAD_ON) {
+                    LSM_ERR_MSG("%s parse error of vad: %s", __FUNCTION__, tmp);
+                    return;
+                }
+
+                media->vad = (vcm_vad_t) strtol_result;
+            }
+
+            /*
+             * Open the transmit port and start sending, but only if we have
+             * the SDP for the remote end.
+             */
+
+            sdpmode = 0;
+               config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+               if (!sdpmode) {
+
+                       if (vcmTxOpen(media->cap_index, dcb->group_id, media->refid,
+                            lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id)) != 0) {
+                               LSM_DEBUG(DEB_L_C_F_PREFIX"%s: vcmTxOpen failed\n",
+                          DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname);
+                               continue;
+               }
+               }
+
+            media->xmit_chan = TRUE;
+
+            attrs.mute = FALSE;
+            if ( CC_IS_VIDEO(media->cap_index)) {
+                attrs.video.opaque = media->video;
+                if (lcb->vid_mute) {
+                    attrs.mute = TRUE;
+                }
+
+            } else if ( CC_IS_AUDIO(media->cap_index)){
+                attrs.audio.packetization_period = media->packetization_period;
+                attrs.audio.max_packetization_period = media->max_packetization_period;
+                attrs.audio.avt_payload_type = media->avt_payload_type;
+                attrs.audio.vad = media->vad;
+                attrs.audio.mixing_mode = mix_mode;
+                attrs.audio.mixing_party = mix_party;
+            }
+
+            dcb->cur_video_avail &= ~CC_ATTRIB_CAST;
+
+            if (media->payloads == NULL) {
+                LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname1);
+                return;
+            }
+            if (!strlen(dcb->peerconnection)){
+              if (vcmTxStart(media->cap_index, group_id,
+                  media->refid,
+                  lsm_get_ms_ui_call_handle(dcb->line, call_id, CC_NO_CALL_ID),
+                  media->payloads,
+                  (short)dscp,
+                  &media->src_addr,
+                  media->src_port,
+                  &media->dest_addr,
+                  media->dest_port,
+                  FSM_NEGOTIATED_CRYPTO_ALGORITHM_ID(media),
+                  FSM_NEGOTIATED_CRYPTO_TX_KEY(media),
+                  &attrs) == -1)
+              {
+                LSM_DEBUG(DEB_L_C_F_PREFIX"%s: vcmTxStart failed\n",
+                  DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname);
+                dcb->dsp_out_of_resources = TRUE;
+                return;
+              }
+            }
+            else {
+              if (vcmTxStartICE(media->cap_index, group_id,
+                  media->refid,
+                  media->level,
+                  /* TODO(emannion): his perhaps needs some error checking for validity.
+                     See gsmsdp_get_media_cap_entry_by_index. */
+                  dcb->media_cap_tbl->cap[media->cap_index].pc_stream,
+                  dcb->media_cap_tbl->cap[media->cap_index].pc_track,
+                  lsm_get_ms_ui_call_handle(dcb->line, call_id, CC_NO_CALL_ID),
+                  dcb->peerconnection,
+                  media->payloads,
+                  (short)dscp,
+                  FSM_NEGOTIATED_CRYPTO_DIGEST_ALGORITHM(media),
+                  FSM_NEGOTIATED_CRYPTO_DIGEST(media),
+                  &attrs) == -1)
+              {
+                LSM_DEBUG(DEB_L_C_F_PREFIX"%s: vcmTxStartICE failed\n",
+                  DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname);
+                dcb->dsp_out_of_resources = TRUE;
+                return;
+              }
+            }
+
+            lsm_update_dscp_value(dcb);
+
+            LSM_DEBUG(DEB_L_C_F_PREFIX"%s: vcmTxStart started\n",
+                  DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname);
+
+            if ( dcb->monrec_tone_action != FSMDEF_MRTONE_NO_ACTION)
+            {
+                vcm_tones_t tone = VCM_NO_TONE;
+                uint16_t    direction = VCM_PLAY_TONE_TO_EAR;
+                boolean     play_both_tones = FALSE;
+
+                LSM_DEBUG(DEB_L_C_F_PREFIX"%s: Found monrec_tone_action: %d. Need to restart playing tone.\n",
+                          DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname), fname, dcb->monrec_tone_action);
+
+                switch (dcb->monrec_tone_action) {
+                    case FSMDEF_MRTONE_RESUME_MONITOR_TONE:
+                        tone = VCM_MONITORWARNING_TONE;
+                        direction = dcb->monitor_tone_direction;
+                        break;
+
+                    case FSMDEF_MRTONE_RESUME_RECORDER_TONE:
+                        tone = VCM_RECORDERWARNING_TONE;
+                        direction = dcb->recorder_tone_direction;
+                        break;
+
+                    case FSMDEF_MRTONE_RESUME_BOTH_TONES:
+                        play_both_tones = TRUE;
+                        tone = VCM_MONITORWARNING_TONE;
+                        direction = dcb->monitor_tone_direction;
+                        break;
+
+                    default:
+                        break;
+                }
+
+                if (play_both_tones == TRUE) {
+                    lsm_util_tone_start_with_speaker_as_backup(VCM_RECORDERWARNING_TONE, VCM_ALERT_INFO_OFF,
+                                                          lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID),
+                                                          dcb->group_id,
+                                                          ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                                          dcb->recorder_tone_direction);
+                }
+
+                lsm_util_tone_start_with_speaker_as_backup(tone, VCM_ALERT_INFO_OFF, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID),
+                                                    dcb->group_id,
+                                                    ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                                    direction);
+            }
+        }
+    }
+}
+
+
+static cc_rcs_t
+lsm_start_tone (lsm_lcb_t *lcb, cc_action_data_tone_t *data)
+{
+    callid_t call_id = lcb->call_id;
+    fsmdef_media_t *media;
+
+    if (lcb->dcb == NULL) {
+        /* No dcb to work with */
+        return (CC_RC_ERROR);
+    }
+    media = gsmsdp_find_audio_media(lcb->dcb);
+
+    lsm_util_start_tone(data->tone, VCM_ALERT_INFO_OFF, lsm_get_ms_ui_call_handle(lcb->line, call_id, CC_NO_CALL_ID), lcb->dcb->group_id,
+                   ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                   VCM_PLAY_TONE_TO_EAR);
+
+    return (CC_RC_SUCCESS);
+}
+
+static cc_rcs_t
+lsm_stop_tone (lsm_lcb_t *lcb, cc_action_data_tone_t *data)
+{
+    /* NOTE: For now, ignore data input parameter that may contain the tone type.
+     *       We'll check active_tone in the dcb for the call_id and see if there
+     *       is a valid tone playing. If so, then and only then issue tone stop.
+     */
+    static const char fname[] = "lsm_stop_tone";
+    callid_t      call_id;
+    fsmdef_dcb_t *dcb;
+
+    if (lcb == NULL) {
+        LSM_DEBUG(DEB_F_PREFIX"NULL lcb passed\n", DEB_F_PREFIX_ARGS(LSM, fname));
+        return (CC_RC_ERROR);
+    }
+    call_id = lcb->call_id;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        LSM_DEBUG(DEB_F_PREFIX" NULL dcb passed for call_id = %d\n", DEB_F_PREFIX_ARGS(LSM, fname), call_id);
+        return (CC_RC_ERROR);
+    }
+
+    /* for tnp do call stop only if active_tone is other than VCM_NO_TONE */
+    if (dcb->active_tone != VCM_NO_TONE) {
+            fsmdef_media_t *media = gsmsdp_find_audio_media(lcb->dcb);
+            vcmToneStop(dcb->active_tone, dcb->group_id,
+                          ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                  lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id));
+        /*
+         * Both periodic tones, recording and monitoring, can be active at the
+         * same time. And because we only keep track of last tone, requested to
+         * play, through active_tone, so when the tone to be stopped is of
+         * periodic type, then it could be that both type of periodic tones
+         * could be playing and both should be stopped. If the second periodic
+         * tone is not playing then media server will ignore the stop request.
+         */
+        if (dcb->active_tone == VCM_RECORDERWARNING_TONE ||
+        dcb->active_tone == VCM_MONITORWARNING_TONE)
+        {
+            vcmToneStop(dcb->active_tone == VCM_RECORDERWARNING_TONE ?
+                VCM_MONITORWARNING_TONE : VCM_RECORDERWARNING_TONE,
+                dcb->group_id,
+                ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id));
+
+            /* in case need to play back the tone again when tx channel active */
+            switch (dcb->monrec_tone_action) {
+                case FSMDEF_MRTONE_PLAYED_MONITOR_TONE:
+                    dcb->monrec_tone_action = FSMDEF_MRTONE_RESUME_MONITOR_TONE;
+                    break;
+
+                case FSMDEF_MRTONE_PLAYED_RECORDER_TONE:
+                    dcb->monrec_tone_action = FSMDEF_MRTONE_RESUME_RECORDER_TONE;
+                    break;
+
+                case FSMDEF_MRTONE_PLAYED_BOTH_TONES:
+                    dcb->monrec_tone_action = FSMDEF_MRTONE_RESUME_BOTH_TONES;
+                    break;
+
+                default:
+                    break;
+            }
+
+            LSM_DEBUG(DEB_L_C_F_PREFIX"%s: Setting monrec_tone_action: %d so resume to play correct tone.\n",
+                              DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname), fname,
+                                         dcb->monrec_tone_action);
+        }
+        dcb->active_tone = VCM_NO_TONE;
+    } else {
+        LSM_DEBUG(DEB_L_C_F_PREFIX"Ignoring tone stop request\n",
+                  DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, call_id, fname));
+    }
+
+    return (CC_RC_SUCCESS);
+}
+
+/*
+ * Function
+ *
+ * @param[in] tone       - tone type
+ * @param[in] alert_info - alertinfo header
+ * @param[in] call_handle- call handle
+ * @param[in] direction  - network, speaker, both
+ * @param[in] duration   - length of time for tone to be played
+ *
+ * @return none
+ */
+void
+lsm_tone_start_with_duration (vcm_tones_t tone, short alert_info,
+                              cc_call_handle_t call_handle, groupid_t group_id,
+                              streamid_t stream_id, uint16_t direction,
+                                                 uint32_t duration)
+{
+
+    static const char *fname = "lsm_tone_start_with_duration";
+
+    DEF_DEBUG(DEB_L_C_F_PREFIX"tone=%-2d: direction=%-2d duration=%-2d\n",
+              DEB_L_C_F_PREFIX_ARGS(LSM, GET_LINE_ID(call_handle), GET_CALL_ID(call_handle), fname),
+              tone, direction, duration);
+
+    /*
+     * play the tone. audio path is always set by MSUI module.
+     */
+    vcmToneStart (tone, alert_info, call_handle, group_id, stream_id, direction);
+
+    lsm_update_active_tone (tone, GET_CALL_ID(call_handle));
+
+    lsm_start_tone_duration_timer (tone, duration, call_handle);
+}
+
+/*
+ * Function: lsm_get_used_instances_cnt
+ *
+ * @param line - line number
+ *
+ * Description: find the number of used instances for this particular line
+ *
+ * @return number of used instances
+ *
+ */
+int lsm_get_used_instances_cnt (line_t line)
+{
+    static const char fname[] = "lsm_get_used_instances_cnt";
+    int             used_instances = 0;
+    lsm_lcb_t      *lcb;
+
+    if (!sip_config_check_line(line)) {
+        LSM_ERR_MSG(LSM_F_PREFIX"invalid line (%d)\n", fname, line);
+
+        return (-1);
+    }
+
+    /*
+     * Count home many instances are already in use for this particular line.
+     */
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if ((lcb->call_id != CC_NO_CALL_ID) &&
+            (lcb->line == line) &&
+            (lcb->state != LSM_S_IDLE)) {
+            used_instances++;
+        }
+    }
+
+    return (used_instances);
+}
+
+/*
+ * Function: lsm_get_all_used_instances_cnt
+ *
+ * Description: find the number of used instances for all lines
+ *
+ * @return number of used instances for all lines
+ *
+ */
+int lsm_get_all_used_instances_cnt ()
+{
+    int             used_instances = 0;
+    lsm_lcb_t      *lcb;
+
+    /*
+     * Count home many instances are already in use for all lines.
+     */
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if ((lcb->call_id != CC_NO_CALL_ID) &&
+            (lcb->state != LSM_S_IDLE)) {
+            used_instances++;
+        }
+    }
+
+    return (used_instances);
+}
+
+/*
+ * Function: lsm_increment_call_chn_cnt
+ *
+ * @param line - line number
+ *
+ * Description:
+ *
+ * @return none
+ *
+ */
+void lsm_increment_call_chn_cnt (line_t line)
+{
+    uint32_t  maxnumcalls = 0;
+    uint32_t  busy_trigger = 0;
+    static const char fname[] = "lsm_increment_call_chn_cnt";
+
+    if ( line <=0 || line > MAX_REG_LINES ) {
+        LSM_ERR_MSG(LSM_F_PREFIX"invalid line (%d)\n", fname, line);
+        return;
+    }
+    lsm_call_perline[line-1]++;
+    config_get_line_value(CFGID_LINE_MAXNUMCALLS, &maxnumcalls, sizeof(maxnumcalls), line);
+    config_get_line_value(CFGID_LINE_BUSY_TRIGGER, &busy_trigger, sizeof(busy_trigger), line);
+    if (lsm_call_perline[line-1] ==  maxnumcalls) {
+        lsm_mnc_reached[line-1] = TRUE;;
+        ui_mnc_reached(line, TRUE);
+    }
+    if (lsm_call_perline[line - 1] ==  busy_trigger) {
+        lsm_bt_reached[line - 1] = TRUE;;
+    }
+
+    LSM_DEBUG(DEB_F_PREFIX"number of calls on line[%d]=%d"
+        "MaxNumCalls[%d]_reached=%s BusyTrigger[%d]_reached=%s\n",
+        DEB_F_PREFIX_ARGS(LSM, fname),
+        line, lsm_call_perline[line-1],
+        maxnumcalls, (lsm_mnc_reached[line-1] == TRUE) ? "TRUE" : "FALSE",
+        busy_trigger,(lsm_bt_reached[line-1] == TRUE) ? "TRUE" : "FALSE");
+}
+
+/*
+ * Function: lsm_decrement_call_chn_cnt
+ *
+ * @param line - line number
+ *
+ * Description:
+ *
+ * @return none
+ *
+ */
+void lsm_decrement_call_chn_cnt (line_t line)
+{
+    uint32_t  maxnumcalls = 0;
+    uint32_t  busy_trigger = 0;
+    static const char fname[] = "lsm_decrement_call_chn_cnt";
+
+    if ( line <=0 || line > MAX_REG_LINES ) {
+        LSM_ERR_MSG(LSM_F_PREFIX"invalid line (%d)\n", fname, line);
+        return;
+    }
+
+    lsm_call_perline[line-1]--;
+    config_get_line_value(CFGID_LINE_MAXNUMCALLS, &maxnumcalls, sizeof(maxnumcalls), line);
+    config_get_line_value(CFGID_LINE_BUSY_TRIGGER, &busy_trigger, sizeof(busy_trigger), line);
+    if (lsm_call_perline[line-1] <=  (maxnumcalls-1)) {
+        lsm_mnc_reached[line-1] = FALSE;
+        ui_mnc_reached(line, FALSE);
+    }
+    if (lsm_call_perline[line - 1] ==  (busy_trigger -1)) {
+        lsm_bt_reached[line - 1] = FALSE;;
+    }
+    LSM_DEBUG(DEB_F_PREFIX"number of calls on line[%d]=%d"
+        "MaxNumCalls[%d]_reached=%s BusyTrigger[%d]_reached=%s\n",
+        DEB_F_PREFIX_ARGS(LSM, fname),
+        line, lsm_call_perline[line-1],
+        maxnumcalls, (lsm_mnc_reached[line-1] == TRUE) ? "TRUE" : "FALSE",
+        busy_trigger,(lsm_bt_reached[line-1] == TRUE) ? "TRUE" : "FALSE");
+}
+
+#define NO_ROLLOVER 0
+#define ROLLOVER_ACROSS_SAME_DN 1
+#define ROLLOVER_NEXT_AVAILABLE_LINE 2
+
+/*
+ * Function: lsm_find_next_available_line
+ *
+ * @param line - line number
+ * @param same_dn - whether lines with same DN to be looked at.
+ * @param incoming - whether we are looking for an available line for an anticipated incoming call.
+ *
+ * Description:
+ *
+ *
+ * @return found line number
+ *
+ */
+line_t lsm_find_next_available_line (line_t line, boolean same_dn, boolean incoming)
+{
+    char current_line_dn_name[MAX_LINE_NAME_SIZE];
+    char dn_name[MAX_LINE_NAME_SIZE];
+    uint32_t line_feature;
+    line_t  i, j;
+    boolean *limit_reached;
+
+    /* determine whether to use MNC or BT limit */
+    if (incoming == TRUE) {
+        limit_reached = lsm_bt_reached;
+    }
+    else {
+        limit_reached = lsm_mnc_reached;
+    }
+
+    config_get_line_string(CFGID_LINE_NAME, current_line_dn_name, line, sizeof(current_line_dn_name));
+    /* This line has exhausted its  limit, start rollover */
+    /* First, search the lines on top of the current one */
+    for (i=line+1; i <= MAX_REG_LINES; i++) {
+        config_get_line_value(CFGID_LINE_FEATURE, &line_feature, sizeof(line_feature), i);
+
+        /* if it is not a DN, skip it */
+        if (line_feature != cfgLineFeatureDN) {
+            continue;
+        }
+        /* Does this line have room to take the call */
+        if (limit_reached[i-1] == FALSE) {
+            if (same_dn == TRUE) {
+                config_get_line_string(CFGID_LINE_NAME, dn_name, i, sizeof(dn_name));
+                /* Does this line have the same DN */
+                if (cpr_strcasecmp(dn_name, current_line_dn_name) == 0) {
+                    return (i);
+                }
+            } else {
+                return (i);
+            }
+        }
+    }
+    /*
+     * We went up to the top and couldn't find an available line,
+     * start from line 1 and search up to the current line, thus
+     * we are treating the available lines as a circular pool
+     */
+
+    for (j=1; j <= line; j++) {
+        config_get_line_value(CFGID_LINE_FEATURE, &line_feature, sizeof(line_feature), j);
+
+        /* if it is not a DN, skip it */
+        if (line_feature != cfgLineFeatureDN) {
+            continue;
+        }
+        /* Does this line have room to take the call */
+        if (limit_reached[j-1] == FALSE) {
+            if (same_dn == TRUE) {
+                config_get_line_string(CFGID_LINE_NAME, dn_name, j, sizeof(dn_name));
+                /* Does this line have the same DN */
+                if (cpr_strcasecmp(dn_name, current_line_dn_name) == 0) {
+                    return (j);
+                }
+            } else {
+                return (j);
+            }
+         }
+    }
+
+    return (NO_LINES_AVAILABLE);
+}
+/*
+ * Function: lsm_get_newcall_line
+ *
+ * @param line - line number
+ *
+ * Description: find out a line that has room to make a
+ *              new call based on the rollover settings
+ *
+ * @return found line number
+ *
+ */
+line_t lsm_get_newcall_line (line_t line)
+{
+    static const char fname[] = "lsm_get_newcall_line";
+    int rollover;
+    line_t found_line;
+
+    if (!lsm_mnc_reached[line-1]) {
+        /* Still room for extra calls on this line */
+        return (line);
+    }
+
+    config_get_value(CFGID_ROLLOVER, &rollover, sizeof(int));
+
+    if (rollover == NO_ROLLOVER) {
+        DEF_DEBUG(DEB_F_PREFIX"NO Rollover, no lines\n", DEB_F_PREFIX_ARGS(LSM, fname));
+        return (NO_LINES_AVAILABLE);
+    }
+
+
+    if (rollover == ROLLOVER_ACROSS_SAME_DN) {
+        /* Look for a line with the same DN */
+        return (lsm_find_next_available_line(line, TRUE, FALSE));
+    }
+
+    if (rollover == ROLLOVER_NEXT_AVAILABLE_LINE) {
+        /* Look for a line with the same DN first */
+        found_line = lsm_find_next_available_line(line, TRUE, FALSE);
+
+        if (found_line == NO_LINES_AVAILABLE) {
+            /*
+             * If nothing found, just look for any line, does
+             * not necessarily have to have the same DN
+             */
+            return (lsm_find_next_available_line(line, FALSE, FALSE));
+        } else {
+            return (found_line);
+        }
+    }
+
+    DEF_DEBUG(DEB_F_PREFIX"No lines available\n", DEB_F_PREFIX_ARGS(LSM, fname));
+
+    return (NO_LINES_AVAILABLE);
+}
+
+/*
+ * Function: lsm_get_available_line
+ *
+ * @param incoming - whether we are looking for an available line for an anticipated incoming call.
+ *
+ * Description: find out a line that has room to make a
+ *              new call starting from the first line.
+ *
+ * @return found line number
+ *
+ */
+line_t lsm_get_available_line (boolean incoming)
+{
+    line_t line = 1; /* start with line 1 */
+
+    if (incoming == FALSE) {
+        if (!lsm_mnc_reached[line-1]) {
+            /* Still room for extra calls on this line */
+            return (line);
+        }
+    }
+    else {
+        if (!lsm_bt_reached[line-1]) {
+            /* Still room for extra calls on this line */
+            return (line);
+        }
+    }
+    return (lsm_find_next_available_line(line, FALSE, incoming));
+}
+
+/*
+ * Function: lsm_is_line_available_for_outgoing_call
+ *
+ * @param line
+ * @param incoming - whether we are looking for an available line for an anticipated incoming call.
+ *
+ * Description: find out if the line  has room to make a new call
+ *
+ * @return TRUE/FALSE
+ *
+ */
+boolean lsm_is_line_available (line_t line, boolean incoming)
+{
+    if (incoming == FALSE) {
+        if (!lsm_mnc_reached[line-1]) {
+            /* Still room for extra calls on this line */
+            return (TRUE);
+        }
+    }
+    else {
+        if (!lsm_bt_reached[line-1]) {
+            /* Still room for incoming calls on this line */
+            return (TRUE);
+        }
+    }
+    return (FALSE);
+}
+
+/*
+ * lsm_get_instances_available_cnt
+ *
+ * return the number of available instances for this particular line
+ *
+ * NOTE: The function can return negative values, which the user should read
+ *       as no available lines.
+ */
+int
+lsm_get_instances_available_cnt (line_t line, boolean expline)
+{
+    static const char fname[] = "lsm_get_instances_available_cnt";
+    int             max_instances;
+    int             used_instances = 0;
+    int             free_instances;
+
+    if (!sip_config_check_line(line)) {
+        LSM_ERR_MSG(LSM_F_PREFIX"invalid line (%d)\n", fname, line);
+
+        return (-1);
+    }
+
+    used_instances = lsm_get_used_instances_cnt(line);
+
+    max_instances = (expline) ? (LSM_MAX_EXP_INSTANCES) : (LSM_MAX_INSTANCES);
+
+    free_instances = max_instances - used_instances;
+
+    if(free_instances > 0){
+         int all_used_instances = lsm_get_all_used_instances_cnt();
+         int all_max_instances = (expline) ? (LSM_MAX_CALLS) : (LSM_MAX_CALLS - 1);
+         int all_free_instances = all_max_instances - all_used_instances;
+         free_instances = ((free_instances < all_free_instances) ? free_instances : all_free_instances);
+         LSM_DEBUG("lsm_get_instances_available_cnt: line=%d, expline=%d, free=%d, all_used=%d, all_max=%d, all_free=%d\n",
+               line, expline, free_instances, all_used_instances, all_max_instances, all_free_instances);
+
+    }
+    LSM_DEBUG("lsm_get_instances_available_cnt: line=%d, expline=%d, free_instances=%d\n",
+               line, expline, free_instances);
+    return (free_instances);
+}
+
+
+static void
+lsm_init_lcb (lsm_lcb_t *lcb)
+{
+    lcb->call_id  = CC_NO_CALL_ID;
+    lcb->line     = LSM_NO_LINE;
+    lcb->previous_call_event = evMaxEvent;
+    lcb->state    = LSM_S_IDLE;
+    lcb->mru      = 0;
+    lcb->enable_ringback = TRUE;
+    lcb->flags    = 0;
+    lcb->dcb      = NULL;
+    lcb->gcid     = NULL;
+    lcb->vid_flags = 0; //set to not visible
+    lcb->ui_id    = CC_NO_CALL_ID;
+}
+
+/**
+ * Return the port back to Media service component
+ * @param [in] lcb - lsm control block
+ *
+ */
+static void lsm_release_port (lsm_lcb_t *lcb)
+{
+    static const char fname[] = "lsm_release_port";
+    fsmdef_media_t *start_media, *end_media;
+    fsmdef_dcb_t   *dcb;
+    fsmdef_media_t *media;
+    int            sdpmode = 0;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname);
+        return;
+    }
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    LSM_DEBUG(DEB_L_C_F_PREFIX,
+              DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname));
+
+    start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb);
+    end_media   = NULL; /* NULL means till the end of the list */
+
+    GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb) {
+        if (!sdpmode) {
+            vcmRxReleasePort(media->cap_index, dcb->group_id, media->refid,
+                            lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id), media->src_port);
+        }
+    }
+}
+
+static void
+lsm_free_lcb (lsm_lcb_t *lcb)
+{
+    lsm_release_port(lcb);
+    cpr_free(lcb->gcid);
+    lsm_init_lcb(lcb);
+}
+
+
+/**
+ * lsm_get_free_lcb
+ * return a free instance of the given line
+ *
+ * @param[in]call_id - gsm call id to allocate the lcb instance with.
+ * @param[in]line    - line that the lcb instnce will be associated with
+ * @param[in]dcb     - fsmdef_dcb_t structure that the lcb instance will
+ *                     be associated with.
+ * @return pointer to lsm_lcb_t if there is an available lcb otherwise
+ *         returns NULL.
+ * @pre    (dcb not_eq NULL)
+ */
+static lsm_lcb_t *
+lsm_get_free_lcb (callid_t call_id, line_t line, fsmdef_dcb_t *dcb)
+{
+    static const char fname[] = "lsm_get_free_lcb";
+    static int      mru = 0;
+    lsm_lcb_t      *lcb;
+    lsm_lcb_t      *lcb_found = NULL;
+
+    if (!sip_config_check_line(line)) {
+        LSM_ERR_MSG(LSM_F_PREFIX"invalid line (%d)\n", fname, line);
+
+        return (NULL);
+    }
+
+
+    /*
+     * Set mru (most recently used).
+     * Used to determine which call came in first.
+     */
+    if (++mru < 0) {
+        mru = 1;
+    }
+
+    /*
+     * Find a free lcb.
+     */
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if ((lcb->call_id == CC_NO_CALL_ID) && (lcb->state == LSM_S_IDLE)) {
+            lcb_found    = lcb;
+            lcb->call_id = call_id;
+            lcb->line    = line;
+            lcb->state   = LSM_S_PENDING;
+            lcb->mru     = mru;
+            lcb->dcb     = dcb;
+            // start unmuted if txPref is true
+            lcb->vid_mute = cc_media_getVideoAutoTxPref() ? FALSE : TRUE;
+
+            lcb->ui_id = call_id;   /* default UI ID is the same as call_id */
+            break;
+        }
+    }
+
+    return (lcb_found);
+}
+
+
+lsm_lcb_t *
+lsm_get_lcb_by_call_id (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+    lsm_lcb_t *lcb_found = NULL;
+    LSM_DEBUG(DEB_L_C_F_PREFIX"call_id=%d.\n",
+              DEB_L_C_F_PREFIX_ARGS(LSM, 0, call_id, "lsm_get_lcb_by_call_id"), call_id);
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if (lcb->call_id == call_id) {
+            lcb_found = lcb;
+            break;
+        }
+    }
+
+    return (lcb_found);
+}
+
+/**
+ * This function returns the LSM state for the given call_id.
+ *
+ * @param[in] call_id -  call id
+ *
+ * @return            lsm_states_t of the given call_id. If the
+ *                    there is no call associated with the given
+ *                    call ID it returns the LSM_S_NONE.
+ */
+lsm_states_t
+lsm_get_state (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+
+    if (lcb == NULL) {
+        /* there is no call for this call id */
+        return (LSM_S_NONE);
+    }
+    return (lcb->state);
+}
+
+static void
+lsm_change_state (lsm_lcb_t *lcb, int line_num, lsm_states_t new_state)
+{
+    static const char fname1[] = "lsm_change_state";
+    LSM_DEBUG(DEB_L_C_F_PREFIX"%d: %s -> %s\n",
+                         DEB_L_C_F_PREFIX_ARGS(LSM, lcb->line, lcb->call_id, fname1),
+              line_num, lsm_state_name(lcb->state), lsm_state_name(new_state));
+
+    lcb->state = new_state;
+}
+
+boolean
+lsm_is_phone_idle (void)
+{
+       static const char fname[] = "lsm_is_phone_idle";
+    boolean         idle = TRUE;
+    lsm_lcb_t      *lcb;
+
+       if(!lsm_lcbs){
+               LSM_DEBUG(DEB_F_PREFIX"No lsm line cb\n", DEB_F_PREFIX_ARGS(LSM, fname));
+               return (idle);
+       }
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if ((lcb->call_id != CC_NO_CALL_ID) && (lcb->state != LSM_S_IDLE)) {
+            idle = FALSE;
+            break;
+        }
+    }
+
+    return (idle);
+}
+
+
+
+/*
+ *  Function: lsm_is_phone_inactive
+ *
+ *  Parameters: None.
+ *
+ *  Description: Determines if the phone is inactive. Inactive means the phone
+ *               as active at some point, but now it is not - there are still
+ *               calls on the phone but they are probably in a holding state.
+ *               This is different from idle, which means that there are not
+ *               any calls on the phone.
+ *
+ *  Returns:
+ *      inactive: FALSE: phone is not inactive
+ *                TRUE:  phone is inactive
+ */
+boolean
+lsm_is_phone_inactive (void)
+{
+    boolean         inactive = TRUE;
+    lsm_lcb_t      *lcb;
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if ((lcb->call_id != CC_NO_CALL_ID) &&
+            ((lcb->state == LSM_S_OFFHOOK) ||
+             (lcb->state == LSM_S_PENDING) ||
+             (lcb->state == LSM_S_PROCEED) ||
+             (lcb->state == LSM_S_RINGOUT) ||
+             (lcb->state == LSM_S_RINGIN)  ||
+             (lcb->state == LSM_S_CONNECTED))) {
+            inactive = FALSE;
+            break;
+        }
+    }
+
+    return (inactive);
+}
+
+/*
+ *  Function: lsm_callwaiting
+ *
+ *  Parameters: None.
+ *
+ *  Description: Determines if the phone is in a state that this
+ *               call will be handled by the callwaiting code. TNP
+ *               phones allow call-waiting when dialing digits while
+ *               the legacy phones do not.
+ *
+ *  Returns:
+ *      inactive: FALSE: Treat as a normal call on an idle phone
+ *                TRUE:  Display incoming call and play call waiting tone
+ */
+boolean
+lsm_callwaiting (void)
+{
+    lsm_lcb_t *lcb;
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if (lcb->call_id != CC_NO_CALL_ID) {
+            switch (lcb->state) {
+            case LSM_S_OFFHOOK:
+            case LSM_S_PROCEED:
+            case LSM_S_RINGOUT:
+            case LSM_S_CONNECTED:
+                return (TRUE);
+
+            default:
+                break;
+            }
+        }
+    }
+
+    return (FALSE);
+}
+
+static callid_t
+lsm_find_state (lsm_states_t state)
+{
+    callid_t        found_callid = CC_NO_CALL_ID;
+    lsm_lcb_t      *lcb;
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if ((lcb->call_id != CC_NO_CALL_ID) && (lcb->state == state)) {
+            found_callid = lcb->call_id;
+            break;
+        }
+    }
+
+    return (found_callid);
+}
+
+/**
+ * lsm_get_facility_by_called_number
+ * return facility by the given called_number.
+ *
+ * @param[in]call_id            - gsm's call_id for a new call.
+ * @param[in]called_number      - pointer to the called number.
+ * @paran[in/out]free_line      - pointer to the line_t to store
+ *                                the result line number corresponding
+ *                                to the called number given.
+ * @param[in]expline            - boolean indicating extra instance
+ *                                is needed.
+ * @param[in]dcb                - pointer to void but it must be
+ *                                a pointer to fsmdef_dcb_t to bind with
+ *                                the new LCB. The reason to use a void
+ *                                pointer is the declaration of the function
+ *                                is in lsm.h. The lsm.h file is used by
+ *                                components outside gsm environment. Those
+ *                                modules would need to include the fsm.h
+ *                                which is not desirable. Using void pointer
+ *                                avoids this problem.
+ *
+ * @return cc_cause_t
+ *
+ * @pre    (called_number not_eq NULL)
+ * @pre    (free_line not_eq NULL)
+ * @pre    (dcb not_eq NULL)
+ */
+cc_causes_t
+lsm_get_facility_by_called_number (callid_t call_id,
+                                   const char *called_number,
+                                   line_t *free_line, boolean expline,
+                                   void *dcb)
+{
+    static const char fname[] = "lsm_get_facility_by_called_number";
+    line_t     line;
+    lsm_lcb_t *lcb;
+    int        free_instances;
+    line_t     madn_line;
+
+    lsm_debug_entry(call_id, 0, fname);
+    LSM_DEBUG(DEB_F_PREFIX"called_number= %s\n", DEB_F_PREFIX_ARGS(LSM, fname), called_number);
+
+    //line = sip_config_get_line_by_called_number(1, called_number);
+    line = 1;
+    if (line == 0) {
+        return (CC_CAUSE_UNASSIGNED_NUM);
+    }
+    *free_line = line;
+
+    /* check for a MADN line */
+    madn_line = sip_config_get_line_by_called_number((line_t)(line + 1),
+                                              called_number);
+
+    /*
+     * Check to see if we even have any available instances.
+     */
+    free_instances = lsm_get_instances_available_cnt(line, expline);
+
+    /* if it is a MADN line and it already has a call, then go to next
+     * line with this MADN number.
+     */
+    if ((madn_line) && (free_instances < 2)) {
+        while (madn_line) {
+            free_instances = lsm_get_instances_available_cnt(madn_line, expline);
+            if (free_instances == 2) {
+                *free_line = line = madn_line;
+                break;
+            }
+            madn_line = sip_config_get_line_by_called_number((line_t)(madn_line + 1),
+                                                      called_number);
+        }
+        if (madn_line == 0) {
+            return (CC_CAUSE_BUSY);
+        }
+    }
+
+    if (free_instances <= 0) {
+        return (CC_CAUSE_BUSY);
+    }
+
+    lcb = lsm_get_free_lcb(call_id, line, (fsmdef_dcb_t *)dcb);
+    if (lcb == NULL) {
+        return (CC_CAUSE_NO_RESOURCE);
+    }
+
+    return (CC_CAUSE_OK);
+}
+
+/**
+ * lsm_allocate_call_bandwidth
+ *
+ * @param[in] none.
+ *
+ * The wlan interface puts into unique situation where call control
+ * has to allocate the worst case bandwith before creating a
+ * inbound or outbound call. The function call will interface through
+ * media API into wlan to get the call bandwidth. The function
+ * return is asynchronous and will block till the return media
+ * callback signals to continue the execution.
+ *
+ * @return true if the bandwidth can be allocated else false.
+ * @pre    none
+ */
+
+cc_causes_t lsm_allocate_call_bandwidth (callid_t call_id, int sessions)
+{
+    //get line for vcm
+    line_t line = lsm_get_line_by_call_id(call_id);
+    //cc_feature(CC_SRC_GSM, call_id, 0, CC_FEATURE_CAC_RESP_PASS, NULL);
+
+    /* Activate the wlan before allocating bandwidth */
+    vcmActivateWlan(TRUE);
+
+    if (vcmAllocateBandwidth(lsm_get_ms_ui_call_handle(line, call_id, CC_NO_CALL_ID), sessions)) {
+        return(CC_CAUSE_OK);
+    }
+
+    return(CC_CAUSE_CONGESTION);
+}
+
+/**
+ * lsm_get_facility_by_line
+ * return facility by the given line
+ *
+ * @param[in]call_id  - gsm's call_id for a new call.
+ * @param[in]line     - line
+ * @param[in]expline  - boolean indicating extra instance
+ *                      is needed.
+ * @param[in]dcb      - pointer to void but it must be
+ *                      a pointer to fsmdef_dcb_t to bind with
+ *                      the new LCB. The reason to use a void
+ *                      pointer is the declaration of the function
+ *                      is in lsm.h. The lsm.h file is used by
+ *                      components outside gsm environment. Those
+ *                      modules would need to include the fsm.h
+ *                      which is not desirable. Using void pointer
+ *                      avoids this problem.
+ *
+ * @return cc_cause_t
+ * @pre    (dcb not_eq NULL)
+ */
+cc_causes_t
+lsm_get_facility_by_line (callid_t call_id, line_t line, boolean expline,
+                          void *dcb)
+{
+    static const char fname[] = "lsm_get_facility_by_line";
+    lsm_lcb_t      *lcb;
+    int             free_instances;
+
+    LSM_DEBUG(get_debug_string(LSM_DBG_INT1), call_id, line, fname,
+              "exp", expline);
+
+    /*
+     * Check to see if we even have any available instances
+     */
+    free_instances = lsm_get_instances_available_cnt(line, expline);
+    if (free_instances <= 0) {
+        return (CC_CAUSE_BUSY);
+    }
+
+    lcb = lsm_get_free_lcb(call_id, line, (fsmdef_dcb_t *)dcb);
+    if (lcb == NULL) {
+        return (CC_CAUSE_NO_RESOURCE);
+    }
+
+    return (CC_CAUSE_OK);
+}
+
+
+#ifdef _WIN32
+/* This function enumerates over the lcbs
+ * and attempts to terminate the call
+ * This is used by softphone when
+ * it exits and the softphone is
+ * still engaged in a call
+ */
+void
+terminate_active_calls (void)
+{
+    callid_t        call_id = CC_NO_CALL_ID;
+    lsm_lcb_t      *lcb;
+    line_t          line;
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if (lcb->call_id != CC_NO_CALL_ID) {
+            line = lsm_get_line_by_call_id(lcb->call_id);
+            /* Currently cc_feature does a better job of releasing the call
+             * compared to cc_onhook.
+             */
+            //cc_onhook(CC_SRC_UI, call_id, line);
+            cc_feature(CC_SRC_UI, call_id, line, CC_FEATURE_END_CALL, NULL);
+            call_id = lcb->call_id;
+        }
+    }
+}
+
+
+#endif
+
+line_t
+lsm_get_line_by_call_id (callid_t call_id)
+{
+    fsmdef_dcb_t   *dcb;
+    line_t          line;
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+    if (dcb != NULL) {
+        line = dcb->line;
+    } else {
+        line = LSM_DEFAULT_LINE;
+    }
+
+    return (line);
+}
+
+/*
+ * This is a callback function for those tones that are
+ * played in two parts (stutter and msgwaiting) or played
+ * every x seconds, but are not steady tones (call waiting).
+ *
+ * @param[in] data    The gsm ID (callid_t) of the call of the
+ *                    tones timer has timeout.
+ *
+ * @return            N/A
+ */
+void
+lsm_tmr_tones_callback (void *data)
+{
+    static const char fname[] = "lsm_tmr_tones_callback";
+    callid_t     call_id;
+    fsmdef_dcb_t *dcb = NULL;
+    fsmdef_media_t *media;
+
+    LSM_DEBUG(DEB_F_PREFIX"invoked", DEB_F_PREFIX_ARGS(LSM, fname));
+
+    call_id = (callid_t)(long)data;
+    if (call_id == CC_NO_CALL_ID) {
+        /* Invalid call id */
+        LSM_DEBUG(DEB_F_PREFIX"invalid call id\n", DEB_F_PREFIX_ARGS(LSM, fname));
+        return;
+    }
+
+    /*
+     * A call-waiting tone should be played if these conditions are met:
+     * 1. A line must be ringing for an incoming call
+     * 2. The phone must be in a state that we handle callwaiting
+     */
+    /* Retrieve dcb from call id */
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+    if (dcb == NULL) {
+        LSM_DEBUG(DEB_F_PREFIX"no dcb found for call_id %d\n", DEB_F_PREFIX_ARGS(LSM, fname), call_id);
+        return;
+    }
+
+    media = gsmsdp_find_audio_media(dcb);
+
+    if ((lsm_find_state(LSM_S_RINGIN) > CC_NO_CALL_ID) && (lsm_callwaiting())) {
+
+            /* Determine what tone/ringing pattern to play */
+            switch (dcb->alert_info) {
+
+            case ALERTING_RING:
+
+                /* Need to map the alerting patterns to the call waiting patterns */
+                switch (dcb->alerting_ring) {
+                case VCM_BELLCORE_DR2:
+                    lsm_util_start_tone(VCM_CALL_WAITING_2_TONE, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                                   ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                   dcb->tone_direction);
+                    break;
+                case VCM_BELLCORE_DR3:
+                    lsm_util_start_tone(VCM_CALL_WAITING_3_TONE, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                                   ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                   dcb->tone_direction);
+                    break;
+                case VCM_BELLCORE_DR4:
+                    lsm_util_start_tone(VCM_CALL_WAITING_4_TONE, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                                   ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                   dcb->tone_direction);
+                    break;
+                default:
+                    lsm_util_start_tone(VCM_CALL_WAITING_TONE, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                                   ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                   dcb->tone_direction);
+                }
+                break;
+
+            case ALERTING_TONE:
+
+                /* Busy verify is just 2 secs of dialtone followed by
+                 * a call waiting tone every 10 secs. The rest of the
+                 * tones are just played once.
+                 */
+                switch (dcb->alerting_tone) {
+                case VCM_BUSY_VERIFY_TONE:
+                    lsm_util_start_tone(VCM_CALL_WAITING_TONE, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                                   ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                   dcb->tone_direction);
+                    if (cprStartTimer(lsm_tmr_tones, BUSY_VERIFICATION_DELAY,
+                        (void *)(long)dcb->call_id) == CPR_FAILURE) {
+                        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                                  fname, "cprStartTimer", cpr_errno);
+                    }
+                    break;
+
+                case VCM_CALL_WAITING_TONE:
+                case VCM_CALL_WAITING_2_TONE:
+                case VCM_CALL_WAITING_3_TONE:
+                case VCM_CALL_WAITING_4_TONE:
+                    lsm_util_start_tone(dcb->alerting_tone, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                                   ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                   dcb->tone_direction);
+                    break;
+
+                case VCM_MSG_WAITING_TONE:
+                case VCM_STUTTER_TONE:
+                    lsm_util_start_tone(VCM_INSIDE_DIAL_TONE, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                                   ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                   dcb->tone_direction);
+                    lsm_tmr_tones_ticks = 0;
+                    break;
+                default:
+                    break;
+                }
+                break;
+
+            default:
+                lsm_util_start_tone(VCM_CALL_WAITING_TONE, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                               ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                               dcb->tone_direction);
+
+                break;
+            }
+
+    } else if (dcb->dialplan_tone) {
+        dcb->dialplan_tone = FALSE;
+        switch (dcb->alert_info) {
+
+        case ALERTING_TONE:
+            /*
+             * Currently the only supported multi-part tones
+             * played via the dialplan are Message Waiting and
+             * Stutter dialtones.
+             */
+            switch (dcb->alerting_tone) {
+            case VCM_MSG_WAITING_TONE:
+            case VCM_STUTTER_TONE:
+                lsm_util_start_tone(VCM_INSIDE_DIAL_TONE, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                               ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                               dcb->tone_direction);
+                break;
+
+            case VCM_HOLD_TONE:
+                lsm_util_start_tone(dcb->alerting_tone, NO, lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), dcb->group_id,
+                               ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                               dcb->tone_direction);
+                break;
+
+            default:
+                break;
+            }
+
+            break;
+
+        default:
+            break;
+        }
+    }
+}
+
+/*
+ * Function   : lsm_start_multipart_tone_timer
+ * Parameters : Tone: 2nd part of tone to play
+ *              Delay: Time to delay between playing the 1st and 2nd parts of the tone
+ *              CallId: Used to retrieve the dcb for this call
+ * Purpose    : This function is used to set up the dcb to play the 2nd part of
+ *              the tone. A timer is started to allow the 1st tone to played to
+ *              completion before the 2nd part is started.
+ */
+void
+lsm_start_multipart_tone_timer (vcm_tones_t tone,
+                                uint32_t delay,
+                                callid_t callId)
+{
+    static const char fname[] = "lsm_start_multipart_tone_timer";
+    fsmdef_dcb_t *dcb;
+
+    /* Set up dcb for timer callback function */
+    dcb = fsmdef_get_dcb_by_call_id(callId);
+    dcb->alert_info = ALERTING_TONE;
+    dcb->alerting_tone = tone;
+    dcb->dialplan_tone = TRUE;
+
+    if (cprCancelTimer(lsm_tmr_tones) == CPR_FAILURE) {
+        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                  fname, "cprCancelTimer", cpr_errno);
+    }
+    if (cprStartTimer(lsm_tmr_tones, delay, (void *)(long)dcb->call_id) ==
+            CPR_FAILURE) {
+        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                  fname, "cprStartTimer", cpr_errno);
+    }
+}
+
+/*
+ * Function   : lsm_stop_multipart_tone_timer
+ * Parameters : None
+ * Purpose    : Called from vcm_stop_tones. That function
+ *              will stop the 1st part of the tone, this
+ *              function cancels the timer so the 2nd part
+ *              will never be played.
+ */
+void
+lsm_stop_multipart_tone_timer (void)
+{
+    static const char fname[] = "lsm_stop_multipart_tone_timer";
+
+    if (cprCancelTimer(lsm_tmr_tones) == CPR_FAILURE) {
+        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                  fname, "cprCancelTimer", cpr_errno);
+    }
+}
+
+/*
+ * Function   : lsm_start_continuous_tone_timer
+ * Parameters : Tone: tone to play
+ *              Delay: Time to delay between playing the tone
+ *              CallId: Used to retrieve the dcb for this call
+ * Purpose    : This function is used to set up the dcb to play a tone continuously.
+ *              An example being the tone on hold tone.
+ */
+void
+lsm_start_continuous_tone_timer (vcm_tones_t tone,
+                                 uint32_t delay,
+                                 callid_t callId)
+{
+    static const char fname[] = "lsm_start_continuous_tone_timer";
+    fsmdef_dcb_t *dcb;
+
+    /* Set up dcb for timer callback function */
+    dcb = fsmdef_get_dcb_by_call_id(callId);
+    dcb->alert_info = ALERTING_TONE;
+    dcb->alerting_tone = tone;
+    dcb->dialplan_tone = TRUE;
+
+    if (cprCancelTimer(lsm_continuous_tmr_tones) == CPR_FAILURE) {
+        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                  fname, "cprCancelTimer", cpr_errno);
+    }
+    if (cprStartTimer(lsm_continuous_tmr_tones, delay, (void *)(long)dcb->call_id)
+            == CPR_FAILURE) {
+        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                  fname, "cprStartTimer", cpr_errno);
+    }
+}
+
+/*
+ * Function   : lsm_stop_continuous_tone_timer
+ * Parameters : None
+ * Purpose    : Called from vcm_stop_tones. That function
+ *              will stop the the tone, this function cancels
+ *              the timer subsequent playing of the tone is not
+ *              performed
+ */
+void
+lsm_stop_continuous_tone_timer (void)
+{
+    static const char fname[] = "lsm_stop_continuous_tone_timer";
+
+    if (cprCancelTimer(lsm_continuous_tmr_tones) == CPR_FAILURE) {
+        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                  fname, "cprCancelTimer", cpr_errno);
+    }
+}
+
+/*
+ * Function   : lsm_start_tone_duration_timer
+ * Parameters : Tone: tone type to play
+ *              Duration: length of time for tone to play
+ *              call_handle: Used to retrieve the dcb for this call
+ * Purpose    : This function is used to set up the dcb to play the tone for
+ *              a specified length of time.
+ */
+void
+lsm_start_tone_duration_timer (vcm_tones_t tone,
+                                uint32_t duration,
+                                cc_call_handle_t call_handle)
+{
+    static const char fname[] = "lsm_start_tone_duration_timer";
+    fsmdef_dcb_t *dcb;
+
+    /* Set up dcb for timer callback function */
+    dcb = fsmdef_get_dcb_by_call_id(GET_CALL_ID(call_handle));
+
+    if (cprCancelTimer(lsm_tone_duration_tmr) == CPR_FAILURE) {
+        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                  fname, "cprCancelTimer", cpr_errno);
+    }
+    if (cprStartTimer(lsm_tone_duration_tmr, duration*1000, (void *)(long)dcb->call_id) ==
+            CPR_FAILURE) {
+        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                  fname, "cprStartTimer", cpr_errno);
+    }
+}
+
+/*
+ * Function   : lsm_stop_tone_duration_timer
+ * Parameters : None
+ * Purpose    : Called from vcm_stop_tones. That function
+ *              will stop the tone.
+ */
+void
+lsm_stop_tone_duration_timer (void)
+{
+    static const char fname[] = "lsm_stop_tone_duration_timer";
+
+    if (cprCancelTimer(lsm_tone_duration_tmr) == CPR_FAILURE) {
+        LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                  fname, "cprCancelTimer", cpr_errno);
+    }
+}
+
+/*
+ * This is a callback function for those tones that are
+ * played in two parts (stutter and msgwaiting) or played
+ * every x seconds, but are not steady tones (call waiting).
+ *
+ * @param[in] data    The gsm ID (callid_t) of the call of the
+ *                    tones timer has timeout.
+ *
+ * @return            N/A
+ */
+void
+lsm_tone_duration_tmr_callback (void *data)
+{
+    static const char fname[] = "lsm_tone_duration_tmr_callback";
+    callid_t     call_id;
+    fsmdef_dcb_t *dcb = NULL;
+    fsmdef_media_t *media;
+
+    LSM_DEBUG(DEB_F_PREFIX"invoked", DEB_F_PREFIX_ARGS(LSM, fname));
+
+    call_id = (callid_t)(long)data;
+    if (call_id == CC_NO_CALL_ID) {
+        /* Invalid call id */
+        LSM_DEBUG(DEB_F_PREFIX"invalid call id\n", DEB_F_PREFIX_ARGS(LSM, fname));
+        return;
+    }
+
+    /* Retrieve dcb from call id */
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+    if (dcb == NULL) {
+        LSM_DEBUG(DEB_F_PREFIX"no dcb found for call_id %d\n", DEB_F_PREFIX_ARGS(LSM, fname), call_id);
+        return;
+    }
+
+    media = gsmsdp_find_audio_media(dcb);
+
+    vcmToneStop(dcb->active_tone, dcb->group_id,
+              ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+              lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID));
+
+    /* Up until this point, only sip core has started the call release procedure        */
+    /* since upon receipt of the BYE.  Now that tone is completed playing as requested  */
+    /* in the BYE, need to continue processing with call clearing.                      */
+
+    cc_int_release(CC_SRC_GSM, CC_SRC_GSM, call_id, dcb->line, CC_CAUSE_NORMAL, NULL, NULL);
+}
+
+/*
+ * LSM internal function that checks if any calls are in a pending
+ * answer condition. Such a condition occurs when the GSM has delayed
+ * answering an incoming call while trying to clear other calls.
+ *
+ * @return              call_id if found, else CC_NO_CALL_ID.
+ */
+static callid_t
+lsm_answer_pending (void)
+{
+    callid_t  found_callid = CC_NO_CALL_ID;
+    lsm_lcb_t *lcb;
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if ((lcb->call_id != CC_NO_CALL_ID) &&
+            (FSM_CHK_FLAGS(lcb->flags, LSM_FLAGS_ANSWER_PENDING))) {
+
+            found_callid = lcb->call_id;
+            break;
+        }
+    }
+
+    return (found_callid);
+}
+
+/**
+ *
+ * Hold Reversion Alert - plays the ringer once.
+ *
+ * @param lsm_lcb_t     lcb for this call
+ * @param callid_t      gsm_id
+ * @param line_t        line
+ *
+ * @return  none
+ *
+ * @pre     (lcb not_eq NULL)
+ */
+static void
+lsm_reversion_ringer (lsm_lcb_t *lcb, callid_t call_id, line_t line)
+{
+    vcm_ring_mode_t ringerMode = VCM_INSIDE_RING;
+    vcm_tones_t     toneMode = VCM_CALL_WAITING_TONE;
+
+    if (!lsm_callwaiting()) {
+        config_get_line_value(CFGID_LINE_RING_SETTING_IDLE,
+                              &ringSettingIdle, sizeof(ringSettingIdle),
+                              line);
+        if (cc_line_ringer_mode[line] == CC_RING_DISABLE) {
+            ringerMode = VCM_FLASHONLY_RING;
+        } else if (ringSettingIdle == DISABLE) {
+            ringerMode = VCM_RING_OFF;
+        } else if (ringSettingIdle == FLASH_ONLY) {
+            ringerMode = VCM_FLASHONLY_RING;
+        }
+
+        vcmControlRinger(ringerMode, YES, NO, line, call_id);
+
+    } else {
+        lsm_tmr_tones_ticks = 0;
+
+        config_get_line_value(CFGID_LINE_RING_SETTING_ACTIVE,
+                              &ringSettingActive, sizeof(ringSettingActive),
+                              line);
+        if (ringSettingActive == DISABLE) {
+            ringerMode = VCM_RING_OFF;
+        } else if (ringSettingActive == FLASH_ONLY) {
+            ringerMode = VCM_FLASHONLY_RING;
+        }
+
+        if (ringSettingActive == BEEP_ONLY) {
+            fsmdef_media_t *media = gsmsdp_find_audio_media(lcb->dcb);
+
+            lsm_util_start_tone(toneMode, NO, lsm_get_ms_ui_call_handle(line, call_id, CC_NO_CALL_ID), lcb->dcb->group_id,
+                           ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                           VCM_PLAY_TONE_TO_EAR);
+        } else {
+            vcmControlRinger(ringerMode, YES, NO, line, call_id);
+        }
+    }
+}
+
+/**
+ * This function will set beep only settings.
+ *
+ * @param[in] dcb - DEF S/M control block
+ * @param[out] toneMode_p - pointer to tone mode
+ *
+ * @return none
+ */
+static void
+lsm_set_beep_only_settings (fsmdef_dcb_t *dcb, vcm_tones_t *toneMode_p)
+{
+    switch (dcb->alert_info) {
+    /*
+     * Map BTS requested ring pattern to corresponding call waiting
+     * pattern if phone is already offhook. All call waiting tones
+     * must be played every ten seconds and msg waiting and stutter
+     * dialtone are multi-part tones that play and then after
+     * 100 ms give steady dialtone. Set a timer to call the tone
+     * callback function for those tones.
+     */
+    case ALERTING_RING:
+        lsm_tmr_tones_ticks = callWaitingDelay;
+        switch (dcb->alerting_ring) {
+        case VCM_BELLCORE_DR2:
+            *toneMode_p = VCM_CALL_WAITING_2_TONE;
+            break;
+
+        case VCM_BELLCORE_DR3:
+            *toneMode_p = VCM_CALL_WAITING_3_TONE;
+            break;
+
+        case VCM_BELLCORE_DR4:
+            *toneMode_p = VCM_CALL_WAITING_4_TONE;
+            break;
+
+        default:
+            break;
+        }
+        break;
+
+    /* BTS wishes to override call waiting tone */
+    case ALERTING_TONE:
+        /*
+         * In violation of the spec, BTS will send tones in the
+         * Alert-Info header and if the phone is offhook, wants
+         * the phone to play the tone specified in the Alert-Info
+         * header instead of the normal call waiting tone. If this
+         * line is connected to a call manager follow the spec and
+         * always play the call waiting tone regardless of what was
+         * received in the Alert-Info header.
+         */
+        if (sip_regmgr_get_cc_mode(dcb->line) == REG_MODE_CCM) {
+            dcb->alerting_tone = VCM_CALL_WAITING_TONE;
+            LSM_DEBUG(DEB_F_PREFIX"%s - Overriding value in Alert-Info header as line %d is \
+                      connected to a Call Manager.\n",
+                      DEB_F_PREFIX_ARGS(LSM, "lsm_set_beep_only_settings"), dcb->line);
+        }
+        *toneMode_p = dcb->alerting_tone;
+        switch (dcb->alerting_tone) {
+        case VCM_MSG_WAITING_TONE:
+            lsm_tmr_tones_ticks = MSG_WAITING_DELAY;
+            break;
+
+        case VCM_STUTTER_TONE:
+            lsm_tmr_tones_ticks = STUTTER_DELAY;
+            break;
+
+        case VCM_BUSY_VERIFY_TONE:
+            lsm_tmr_tones_ticks = BUSY_VERIFY_DELAY;
+            break;
+
+        case VCM_CALL_WAITING_TONE:
+        case VCM_CALL_WAITING_2_TONE:
+        case VCM_CALL_WAITING_3_TONE:
+        case VCM_CALL_WAITING_4_TONE:
+            lsm_tmr_tones_ticks = callWaitingDelay;
+            break;
+
+        default:
+            break;
+        }
+        break;
+
+    default:
+        lsm_tmr_tones_ticks = callWaitingDelay;
+    }
+}
+
+/**
+ *
+ * Set ringer mode based on remote-cc input and configuration parameters. If there
+ * any other call pending then it should play call waiting tone.
+ *
+ * @param lsm_lcb_t     lcb for this call
+ * @param callid_t      gsm_id
+ * @param line_t        line
+ *
+ * @return  none
+ *
+ * @pre     (lcb not_eq NULL)
+ */
+static void
+lsm_set_ringer (lsm_lcb_t *lcb, callid_t call_id, line_t line, int alerting)
+{
+    static const char fname[] = "lsm_set_ringer";
+    fsmdef_dcb_t   *dcb;
+    boolean         ringer_set = FALSE;
+    callid_t        other_call_id = CC_NO_CALL_ID;
+    callid_t        priority_call_id = CC_NO_CALL_ID;
+    int             callHoldRingback = 0;
+    int             dcb_cnt = 0;
+    int             i = 0;
+    fsmxfr_xcb_t   *xcb;
+    fsmcnf_ccb_t   *ccb;
+    lsm_lcb_t *lcb2;
+    fsmdef_dcb_t   *dcbs[LSM_MAX_CALLS];
+    vcm_ring_mode_t ringerMode = VCM_INSIDE_RING;
+    short           ringOnce = NO;
+    boolean         alertInfo = NO;
+    vcm_tones_t     toneMode = VCM_CALL_WAITING_TONE;
+    fsmdef_media_t *media;
+    boolean         isDusting = FSM_CHK_FLAGS(lcb->flags, LSM_FLAGS_DUSTING) ? TRUE : FALSE;
+    int            sdpmode = 0;
+
+
+    LSM_DEBUG(DEB_L_C_F_PREFIX"Entered, state=%d.\n",
+              DEB_L_C_F_PREFIX_ARGS(LSM, line, call_id, fname), lcb->state);
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    /*
+     * The ringer (or call-waiting tone) should be on if these
+     * conditions are met:
+     * 1. A line is ringing for an incoming call and no calls
+     *    with a pending answer
+     * 2. A line is on hold
+     * and
+     * 3. No lines are connected
+     *
+     * Otherwise, turn on the call-waiting tones.
+     *
+     */
+
+    if (priority_call_id == CC_NO_CALL_ID) {
+        /* get the call_id of the line that triggers this if it is ringing and
+           pass down the correct line variable and its ring type and let the ring
+           manager decides. Originally we only find line first line in ringing state
+           which results in issue where Flash only line follows by audio ring line
+           ringing simultaneously, the phone does not ring audibly.
+        */
+        if (lcb->state == LSM_S_RINGIN) {
+            other_call_id = call_id;
+        } else {
+            other_call_id = lsm_find_state(LSM_S_RINGIN);
+        }
+    }
+
+    if (((priority_call_id != CC_NO_CALL_ID) || (other_call_id != CC_NO_CALL_ID)) &&
+        (lsm_answer_pending() == CC_NO_CALL_ID)) {
+        /*sam
+         * may need to add (ringout and rtp open) to this check.
+         * It is possible that inband alerting is active for an outgoing call.
+         */
+        dcb = fsmdef_get_dcb_by_call_id((priority_call_id != CC_NO_CALL_ID) ?
+                                        priority_call_id : other_call_id);
+        lcb = lsm_get_lcb_by_call_id((priority_call_id != CC_NO_CALL_ID) ?
+                                        priority_call_id : other_call_id);
+        isDusting = ((lcb != NULL) && FSM_CHK_FLAGS(lcb->flags, LSM_FLAGS_DUSTING)) ? TRUE : FALSE;
+
+        /*
+         * TNP has line-based ringing so update the line parameter so
+         * if reflects what line is ringing, not which line had an action
+         * taken against it, i.e. if line 1 hangs up and line 2 is ringing,
+         * line will be equal to 1 (since that line hung-up), but it needs
+         * to be 2 since that is the line actually ringing. 40/60 can
+         * get away with this since it is device-based ringing. If there
+         * are multiple lines in the RINGIN state, the ringing will be
+         * based on the first line in the RINGIN state found. Could add
+         * the check if (other_call_id != callid) but line will equal
+         * dcb->line if the callids are the same so save a few CPU cycles
+         * by not having the check. No need to do a #ifdef TNP since
+         * it does matter which line we use on the 40/60 as it is device based.
+         */
+        line = dcb->line;
+
+        if (!lsm_callwaiting()) {
+
+            LSM_DEBUG(DEB_L_C_F_PREFIX"No call waiting, lcb->line=%d, lcb->flag=%d.\n",
+                      DEB_L_C_F_PREFIX_ARGS(LSM, line, lcb->call_id, fname),
+                      lcb->line,
+                      lcb->flags);
+
+            ringer_set = TRUE;
+            lsm_tmr_tones_ticks = 0;
+
+            /*
+             * CFGID_LINE_RING_SETTING_IDLE is a config parameter that
+             * tells the phone what action to take for an incoming
+             * call on a phone with no active calls.
+             *
+             */
+
+            if (isDusting) {
+                ringSettingIdle = FLASH_ONLY;
+            }
+            else if (FSM_CHK_FLAGS(lcb->flags, LSM_FLAGS_PREVENT_RINGING)) {
+                /*
+                 * If this phone is both calling and called device, do not play ring.
+                 */
+                ringSettingIdle = DISABLE;
+            } else if (cc_line_ringer_mode[line] == CC_RING_DISABLE) {
+                /*
+                 * Disable - no ring or flash
+                 */
+                ringSettingIdle = FLASH_ONLY;
+            } else if (cc_line_ringer_mode[line] == CC_RING_ENABLE) {
+                ringSettingIdle = RING;
+            } else {
+                config_get_line_value(CFGID_LINE_RING_SETTING_IDLE,
+                                      &ringSettingIdle, sizeof(ringSettingIdle),
+                                      line);
+            }
+            LSM_DEBUG(DEB_L_C_F_PREFIX"Ring set mode=%d.\n",
+                      DEB_L_C_F_PREFIX_ARGS(LSM, line, call_id, fname), ringSettingIdle);
+
+            /*
+             * Disable - no ring or flash
+             */
+            if (ringSettingIdle == DISABLE) {
+                ringerMode = VCM_RING_OFF;
+
+                /*
+                 * Flash Only - No ringing, just flash.
+                 */
+            } else if (ringSettingIdle == FLASH_ONLY) {
+                ringerMode = VCM_FLASHONLY_RING;
+
+                /*
+                 * Ring once - ring the phone once
+                 */
+            } else if (ringSettingIdle == RING_ONCE) {
+                ringOnce = YES;
+
+                /*
+                 * Ring - normal operation. Ring the phone until answered,
+                 * forwarded, or disconnected.
+                 */
+            } else if (ringSettingIdle == RING) {
+
+                /* Determine what tone/ringing pattern to play */
+                switch (dcb->alert_info) {
+                case ALERTING_NONE:
+                    /* This is the default case nothing to do */
+                    break;
+
+                case ALERTING_RING:
+                    ringerMode = dcb->alerting_ring;
+                    break;
+
+                case ALERTING_OLD:
+                default:
+                    alertInfo = YES;
+                }
+            } else if (ringSettingIdle == BEEP_ONLY) {
+                lsm_set_beep_only_settings (dcb, &toneMode);
+
+            }
+            LSM_DEBUG(DEB_L_C_F_PREFIX"Alert info=%d, ringSettingIdle=%d, ringerMode=%d\n",
+                                  DEB_L_C_F_PREFIX_ARGS(LSM, line, call_id, fname),
+                                  dcb->alert_info,
+                                  ringSettingIdle,
+                                  ringerMode);
+
+            /*
+             * If an active call is being held while there is an incoming
+             * call AND ringSettingBusyStationPolicy is 0, this flag will
+             * be false.
+             */
+            if (alerting) {
+                /*
+                 * If the line is connected to a CCM, Bellcore-Dr1 means
+                 * play the defined ringer once.  Bellcore-dr2 means play
+                 * the defined ringer twice.
+                 */
+                if (sip_regmgr_get_cc_mode(line) == REG_MODE_CCM) {
+                    if (ringerMode == VCM_BELLCORE_DR1) {
+                        ringerMode = VCM_INSIDE_RING;
+                    } else if (ringerMode == VCM_BELLCORE_DR2) {
+                        ringerMode = VCM_OUTSIDE_RING;
+                    }
+                }
+                if (ringSettingIdle == BEEP_ONLY) {
+
+                    LSM_DEBUG(DEB_L_C_F_PREFIX"Idle phone RING SETTING: Beep_only\n",
+                              DEB_L_C_F_PREFIX_ARGS(LSM, line, call_id, fname));
+
+                    media = gsmsdp_find_audio_media(lcb->dcb);
+                    lsm_util_tone_start_with_speaker_as_backup(toneMode, NO, lsm_get_ms_ui_call_handle(line, call_id, CC_NO_CALL_ID),
+                                                          lcb->dcb->group_id,
+                          ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                                          VCM_PLAY_TONE_TO_EAR);
+                } else {
+                    LSM_DEBUG(DEB_L_C_F_PREFIX"Idle phone RING SETTING: ringer Mode = %s,"
+                              " Ring once = %d, alertInfo = %d\n",
+                              DEB_L_C_F_PREFIX_ARGS(LSM, line, call_id, fname),
+                              vm_alert_names[ringerMode], ringOnce, alertInfo);
+
+                    vcmControlRinger(ringerMode, ringOnce, alertInfo, line, lcb->ui_id);
+                }
+            }
+
+            if ( lcb->state != LSM_S_HOLDING &&
+                 lcb->state != LSM_S_RINGIN ) {
+                ui_set_call_status(platform_get_phrase_index_str(CALL_ALERTING),
+                                   line, lcb->ui_id);
+            }
+        } else {
+
+            // Ring off all lines.
+           FSM_FOR_ALL_CBS(lcb2, lsm_lcbs, LSM_MAX_LCBS) {
+                if ((lcb2->call_id != CC_NO_CALL_ID) &&
+                    (lcb2->state == LSM_S_RINGIN) )
+                {
+            LSM_DEBUG(DEB_L_C_F_PREFIX"Call waiting RING SETTING: "
+                      "ringer Mode = RING_OFF, Ring once = NO, alertInfo = NO\n",
+                      DEB_L_C_F_PREFIX_ARGS(LSM, lcb2->line, lcb2->call_id, fname));
+                      vcmControlRinger(VCM_RING_OFF, NO, NO, lcb2->line, call_id);
+                 }
+            }
+            ringer_set = TRUE;
+            lsm_tmr_tones_ticks = 0;
+
+            /*
+             * ringSettingActive is a TNP only config parameter that
+             * tells the phone what action to take for an incoming
+             * call on a phone with an active call.
+             */
+            if (isDusting) {
+                ringSettingActive = FLASH_ONLY;
+            } else {
+                config_get_line_value(CFGID_LINE_RING_SETTING_ACTIVE,
+                                      &ringSettingActive, sizeof(ringSettingActive),
+                                      line);
+            }
+
+            /*
+             * Disable - no ring or flash
+             */
+            if (ringSettingActive == DISABLE) {
+                ringerMode = VCM_RING_OFF;
+
+                /*
+                 * Flash Only - No ringing, just flash.
+                 */
+            } else if (ringSettingActive == FLASH_ONLY) {
+                ringerMode = VCM_FLASHONLY_RING;
+
+                /*
+                 * Ring once - ring the phone once
+                 */
+            } else if (ringSettingActive == RING_ONCE) {
+                ringOnce = YES;
+
+                /*
+                 * Ring - Ring the phone until answered, forwarded or
+                 * disconnected.
+                 *
+                 * NOTE: This code is replicated above under checking
+                 * RING_SETTING_IDLE above. Putting this common code
+                 * in a function call saved a miniscule amount of memory
+                 * at the cost of an additional function call for every
+                 * call. It was decided it was not worth the cost, but
+                 * has been documented in case the phone gets very,
+                 * very low on memory in the future.
+                 */
+            } else if (ringSettingActive == RING) {
+
+                /* Determine what tone/ringing pattern to play */
+                switch (dcb->alert_info) {
+                case ALERTING_NONE:
+                    /* This is the default case nothing to do */
+                    break;
+
+                case ALERTING_RING:
+                    ringerMode = dcb->alerting_ring;
+                    break;
+
+                case ALERTING_OLD:
+                default:
+                    alertInfo = YES;
+                }
+
+                /*
+                 * BeepOnly - normal operation. Play call waiting tone.
+                 */
+            } else if (ringSettingActive == BEEP_ONLY) {
+                lsm_set_beep_only_settings (dcb, &toneMode);
+
+            }
+
+            /*
+             * If an active call is being held while there is an incoming
+             * call AND ringSettingBusyStationPolicy is 0, this flag will
+             * be false.
+             */
+            if (alerting) {
+                /*
+                 * The code above has set the variables to play either the ringer
+                 * or a tone based on the ringSettingBusy. If the config variable
+                 * is beeponly then call start_tone else call control_ringer.
+                 */
+                if (ringSettingActive == BEEP_ONLY) {
+                    media = gsmsdp_find_audio_media(dcb);
+
+                    lsm_util_start_tone(toneMode, NO, lsm_get_ms_ui_call_handle(line, call_id, CC_NO_CALL_ID), dcb->group_id,
+                                   ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                                   VCM_PLAY_TONE_TO_EAR);
+                } else {
+
+                    LSM_DEBUG(DEB_L_C_F_PREFIX"Active call RING SETTING: "
+                              "ringer Mode = %s, Ring once = %d, alertInfo = %d\n",
+                              DEB_L_C_F_PREFIX_ARGS(LSM, line, call_id, fname),
+                              vm_alert_names[ringerMode], ringOnce, alertInfo);
+
+                    vcmControlRinger(ringerMode, ringOnce, alertInfo, line, lcb->ui_id);
+                }
+            }
+
+            /*
+             * Start a timer to play multiple part tones if needed.
+             */
+            if (lsm_tmr_tones_ticks > 0) {
+                if (cprCancelTimer(lsm_tmr_tones) == CPR_FAILURE) {
+                    LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "cprCancelTimer", cpr_errno);
+                }
+                if (cprStartTimer(lsm_tmr_tones, lsm_tmr_tones_ticks,
+                                  (void *)(long)dcb->call_id) == CPR_FAILURE) {
+                    LSM_DEBUG(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "cprStartTimer", cpr_errno);
+                }
+            }
+        }
+    } else if (lcb->state == LSM_S_IDLE) {
+        /*
+         * This line just hungup so let's check to see if call hold ringback
+         * is enabled and if we have any other holding lines. If so ring to
+         * alert the user that a line is still around.
+         */
+        config_get_value(CFGID_CALL_HOLD_RINGBACK, &callHoldRingback,
+                         sizeof(callHoldRingback));
+        if (callHoldRingback & 0x1) {
+            callid_t        ui_id;
+
+            dcb_cnt = fsmdef_get_dcbs_in_held_state(dcbs, call_id);
+            for (i = 0, dcb = dcbs[i]; i < dcb_cnt; i++, dcb = dcbs[i]) {
+               ccb = fsmcnf_get_ccb_by_call_id(call_id);
+                       xcb = fsmxfr_get_xcb_by_call_id(call_id);
+                       if ((lsm_is_phone_inactive() == TRUE) &&
+                       (ccb == NULL) && (xcb == NULL) &&
+                       (lcb->enable_ringback == TRUE)) {
+                    LSM_DEBUG(DEB_L_C_F_PREFIX"Applying ringback\n",
+                               DEB_L_C_F_PREFIX_ARGS(LSM, lcb->line, lcb->call_id, fname));
+                    ringer_set = TRUE;
+
+                    LSM_DEBUG(DEB_L_C_F_PREFIX"Hold RINGBACK SETTING: ringer Mode = "
+                                     "VCM_INSIDE_RING, Ring once = YES, alertInfo = YES\n",
+                                 DEB_L_C_F_PREFIX_ARGS(LSM, line, dcb->call_id, fname));
+                    vcmControlRinger(VCM_INSIDE_RING, YES, YES, line, call_id);
+
+                    /* Find the corresponding LCB to get to the UI ID */
+                    ui_id = lsm_get_ui_id(dcb->call_id);
+                    ui_set_call_status(platform_get_phrase_index_str(CALL_INITIATE_HOLD),
+                                                 dcb->line, ui_id);
+                }
+            }
+        }
+    }
+
+    if (ringer_set == FALSE) {
+
+        LSM_DEBUG(DEB_L_C_F_PREFIX"Ringer_set = False : "
+                  "ringer Mode = VCM_RING_OFF, Ring once = NO, alertInfo = NO\n",
+                  DEB_L_C_F_PREFIX_ARGS(LSM, line, call_id, fname));
+
+
+        if (!sdpmode) {
+            vcmControlRinger(VCM_RING_OFF, NO, NO, line, call_id);
+        }
+
+    }
+}
+
+static cc_rcs_t
+lsm_offhook (lsm_lcb_t *lcb, cc_state_data_offhook_t *data)
+{
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+    fsmxfr_xcb_t   *xcb;
+    lsm_lcb_t      *lcb2;
+    callid_t        call_id2;
+    int             attr;
+    fsmdef_dcb_t   *dcb;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    lsm_change_state(lcb, __LINE__, LSM_S_OFFHOOK);
+
+    /*
+     * Disable the ringer since the user is going offhook. Only calls
+     * in the RINGIN state should have ringing enabled.
+     */
+    FSM_FOR_ALL_CBS(lcb2, lsm_lcbs, LSM_MAX_LCBS) {
+        if ((lcb2->call_id != CC_NO_CALL_ID) &&
+            (lcb2->state == LSM_S_RINGIN)) {
+
+            vcmControlRinger(VCM_RING_OFF, NO, NO, lcb2->line, lcb2->call_id);
+        }
+    }
+
+    dp_offhook(line, call_id);
+
+    attr = fsmutil_get_call_attr(dcb, line, call_id);
+
+    ui_new_call(evOffHook, line, lcb->ui_id, attr,
+                dcb->caller_id.call_instance_id,
+                (boolean)FSM_CHK_FLAGS(lcb->flags, LSM_FLAGS_DIALED_STRING));
+
+    xcb = fsmxfr_get_xcb_by_call_id(call_id);
+    if (xcb != NULL) {
+        call_id2 = ((lcb->call_id == xcb->xfr_call_id) ?
+                    (xcb->cns_call_id) : (xcb->xfr_call_id));
+        lcb2 = lsm_get_lcb_by_call_id(call_id2);
+    }
+
+    //vcmActivateWlan(TRUE);
+
+    vcmEnableSidetone(YES);
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_dialing (lsm_lcb_t *lcb, cc_state_data_dialing_t *data)
+{
+    fsmxfr_xcb_t   *xcb = NULL;
+    int             stutterMsgWaiting = 0;
+    fsmdef_dcb_t   *dcb = lcb->dcb;
+    fsmdef_media_t *media = gsmsdp_find_audio_media(dcb);
+
+
+    if ( dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    /* don't provide dial tone on transfer unless we are the transferor. */
+    xcb = fsmxfr_get_xcb_by_call_id(lcb->call_id);
+    if ((xcb != NULL) && (xcb->mode != FSMXFR_MODE_TRANSFEROR)) {
+        return (CC_RC_SUCCESS);
+    }
+
+    /*
+     * Start dial tone if no digits have been entered
+     */
+    if ((data->play_dt == TRUE)
+        && (dp_check_for_plar_line(lcb->line) == FALSE)
+        ) {
+
+        /* get line based AMWI config */
+        config_get_value(CFGID_LINE_MESSAGE_WAITING_AMWI + lcb->line - 1, &stutterMsgWaiting,
+                         sizeof(stutterMsgWaiting));
+        if ( stutterMsgWaiting != 1 && stutterMsgWaiting != 0) {
+          /* AMWI is not configured. Fallback on config for stutter dial tone */
+          config_get_value(CFGID_STUTTER_MSG_WAITING, &stutterMsgWaiting,
+                         sizeof(stutterMsgWaiting));
+          stutterMsgWaiting &= 0x1; /* LSB indicates on/off */
+        }
+
+        if ( (data->suppress_stutter == FALSE) &&
+             (ui_line_has_mwi_active(lcb->line)) && /* has msgs waiting */
+                     stutterMsgWaiting ) {
+            lsm_util_start_tone(VCM_STUTTER_TONE, FALSE, lsm_get_ms_ui_call_handle(lcb->line, CC_NO_CALL_ID, lcb->ui_id),
+                    dcb->group_id,
+                           ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                           VCM_PLAY_TONE_TO_EAR);
+        } else {
+            lsm_util_start_tone(VCM_INSIDE_DIAL_TONE, FALSE, lsm_get_ms_ui_call_handle(lcb->line, CC_NO_CALL_ID, lcb->ui_id),
+                    dcb->group_id,
+                           ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
+                           VCM_PLAY_TONE_TO_EAR);
+        }
+    }
+
+    /*
+     * For round table phone, post WAITINGFORDIGITS event,
+     * so that UI can pop up dialing screen.
+     * For TNP, this event gets ignored.
+     */
+    ui_call_state(evWaitingForDigits, lcb->line, lcb->ui_id, CC_CAUSE_NORMAL);
+
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_dialing_completed (lsm_lcb_t *lcb, cc_state_data_dialing_completed_t *data)
+{
+    line_t          line = lcb->line;
+    fsmdef_dcb_t   *dcb = lcb->dcb;
+
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    lsm_change_state(lcb, __LINE__, LSM_S_PROCEED);
+
+    /* If KPML is enabled then do not change UI state to
+     * proceed, more digit to collect
+     */
+    if (dp_get_kpml_state()) {
+        return (CC_RC_SUCCESS);
+    }
+
+    ui_call_info(data->caller_id.calling_name,
+                 data->caller_id.calling_number,
+                 data->caller_id.alt_calling_number,
+                 data->caller_id.display_calling_number,
+                 data->caller_id.called_name,
+                 data->caller_id.called_number,
+                 data->caller_id.display_called_number,
+                 data->caller_id.orig_called_name,
+                 data->caller_id.orig_called_number,
+                 data->caller_id.last_redirect_name,
+                 data->caller_id.last_redirect_number,
+                 (calltype_t)dcb->call_type,
+                 line, lcb->ui_id,
+                 dcb->caller_id.call_instance_id,
+                 FSM_GET_SECURITY_STATUS(dcb),
+                 FSM_GET_POLICY(dcb));
+
+    lsm_ui_call_state(evProceed, line, lcb, CC_CAUSE_NORMAL);
+
+    (void) lsm_stop_tone(lcb, NULL);
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_call_sent (lsm_lcb_t *lcb, cc_state_data_call_sent_t *data)
+{
+    line_t          line = lcb->line;
+    fsmdef_dcb_t   *dcb;
+    char            tmp_str[STATUS_LINE_MAX_LEN];
+    fsmdef_media_t *media;
+    static const char fname[] = "lsm_call_sent";
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    lsm_change_state(lcb, __LINE__, LSM_S_PROCEED);
+
+    (void) lsm_stop_tone(lcb, NULL);
+
+    /*
+     * We go ahead and start a rx port if our local SDP indicates
+     * the need in an attempt to be 3264 compliant. Since we have
+     * not yet locked down the codec, we will use preferred codec if
+     * configured. If not, we use the first codec in our local
+     * list of supported codecs. The codec list was initialized
+     * in fsmdef_init_local_sdp.
+     */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb) {
+         if (!GSMSDP_MEDIA_ENABLED(media)) {
+             continue;
+         }
+         LSM_DEBUG(DEB_F_PREFIX"%d %d %d\n", DEB_F_PREFIX_ARGS(LSM, fname), media->direction_set,
+                   media->direction, media->is_multicast);
+         if ((media->direction_set) &&
+             ((media->direction == SDP_DIRECTION_SENDRECV) ||
+              (media->direction == SDP_DIRECTION_RECVONLY))) {
+
+             lsm_rx_start(lcb, cc_state_name(CC_STATE_FAR_END_ALERTING),
+                          media);
+         }
+    }
+
+    if (!dp_get_kpml_state()) {
+        if ((platGetPhraseText(STR_INDEX_CALLING,
+                                     (char *) tmp_str,
+                                     STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) {
+            ui_set_call_status(tmp_str, line, lcb->ui_id);
+        }
+    }
+
+    /*
+     * cancel offhook to first digit timer.
+     */
+    dp_int_cancel_offhook_timer(line, lcb->call_id);
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_far_end_proceeding (lsm_lcb_t *lcb,
+                       cc_state_data_far_end_proceeding_t * data)
+{
+    line_t        line = lcb->line;
+    fsmdef_dcb_t *dcb;
+
+    lsm_change_state(lcb, __LINE__, LSM_S_PROCEED);
+
+    if (!dp_get_kpml_state()) {
+        ui_set_call_status(platform_get_phrase_index_str(CALL_PROCEEDING_IN),
+                           line, lcb->ui_id);
+        /*
+         * update placed call info in call history with dialed digits
+         */
+        dcb = lcb->dcb;
+        if (dcb != NULL && dcb->placed_call_update_required) {
+            lsm_update_placed_callinfo(dcb);
+            dcb->placed_call_update_required = FALSE;
+        }
+    }
+
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_far_end_alerting (lsm_lcb_t *lcb, cc_state_data_far_end_alerting_t *data)
+{
+    static const char fname[] = "lsm_far_end_alerting";
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+    fsmdef_dcb_t   *dcb;
+    const char     *status = NULL;
+    fsmcnf_ccb_t   *ccb;
+    boolean         rcv_port_started = FALSE;
+    char            tmp_str[STATUS_LINE_MAX_LEN];
+    boolean         spoof_ringout;
+    fsmdef_media_t  *media;
+    call_events     call_state;
+    fsmdef_media_t *audio_media;
+    boolean         is_session_progress = FALSE;
+
+
+    /*
+     * Need to check if rcv_chan is already open and if we will be
+     * receiving inband ringing. The recv_chan should always be
+     * open since we always open a receive channel when initiating a
+     * call. If inband ringing will be sent by the far end, we
+     * will close the receive port and reopen it using the codec
+     * negotiated when we received the SDP in the far ends call
+     * proceeding message. We want to close the receive port well
+     * ahead of reopening it due to some issue in the dsp where
+     * a close followed immediately by an open causes a reset of
+     * the DSP.
+     */
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+    audio_media =  gsmsdp_find_audio_media(dcb);
+
+    if (dcb->inband) {
+        /* close (with refresh) all media entries */
+        lsm_close_rx(lcb, TRUE, NULL);
+        lsm_close_tx(lcb, TRUE, NULL);
+    }
+
+    /*
+     * Check to see if we need to spoof ring out in connected or holding
+     * state.
+     *
+     * The LSM can be in holding state when the user is resuming
+     * currently held call that was early transferred to another party
+     * and the other party has not answered the call yet.
+     */
+    if (dcb->spoof_ringout_requested &&
+        ((lcb->state == LSM_S_CONNECTED) || (lcb->state == LSM_S_HOLDING))) {
+        /* Spoof ring out is requested in the connected/holding state */
+        spoof_ringout = TRUE;
+    } else {
+        spoof_ringout = FALSE;
+    }
+    lsm_change_state(lcb, __LINE__, LSM_S_RINGOUT);
+
+    /* Don't send the dialplan update msg if CFWD_ALL. Otherwise the invalid
+     * redial numer is saved. (CSCsv08816)
+     */
+    if (dcb->active_feature != CC_FEATURE_CFWD_ALL) {
+        dp_int_update(line, call_id, data->caller_id.called_number);
+    }
+
+
+    /*
+     * Check for inband alerting or spoof ringout.
+     * If no inband alerting and this is not a spoof ringout case,
+     * just update the status line to show we are alerting. The local
+     * ringback tone will not be started until the ringback delay timer
+     * expires.
+     */
+    if (dcb->inband != TRUE || spoof_ringout) {
+        status = platform_get_phrase_index_str(CALL_ALERTING_LOCAL);
+
+        if (spoof_ringout) {
+
+            if (audio_media) {
+
+            /*
+             * Ringback delay timer is not used for spoof ringout case
+             * so start local ringback tone now.
+             */
+                lsm_util_start_tone(VCM_ALERTING_TONE, FALSE, lsm_get_ms_ui_call_handle(line, call_id, CC_NO_CALL_ID),
+                           dcb->group_id,audio_media->refid,
+                           VCM_PLAY_TONE_TO_EAR);
+            }
+
+        }
+    } else {
+       is_session_progress = TRUE;
+        (void) lsm_stop_tone(lcb, NULL);
+
+        if ((platGetPhraseText(STR_INDEX_SESSION_PROGRESS,
+                                     (char *) tmp_str,
+                                     STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) {
+            status = tmp_str;
+        }
+
+        /* start receive and transmit for all media entries that are active */
+        GSMSDP_FOR_ALL_MEDIA(media, dcb) {
+            if (!GSMSDP_MEDIA_ENABLED(media)) {
+                /* this entry is not active */
+                continue;
+            }
+            LSM_DEBUG(DEB_L_C_F_PREFIX"direction_set:%d direction:%d"
+                      " dest_addr:0x%x is_multicast:%d\n",
+                      DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname),
+                      media->direction_set,
+                      media->direction, media->dest_addr,
+                      media->is_multicast);
+
+            if (media->direction_set) {
+                if (media->direction == SDP_DIRECTION_SENDRECV ||
+                    media->direction == SDP_DIRECTION_RECVONLY) {
+                    lsm_rx_start(lcb,
+                                 cc_state_name(CC_STATE_FAR_END_ALERTING),
+                                 media);
+                    rcv_port_started = TRUE;
+                }
+
+                if (media->direction == SDP_DIRECTION_SENDRECV ||
+                    media->direction == SDP_DIRECTION_SENDONLY) {
+                    lsm_tx_start(lcb,
+                             cc_state_name(CC_STATE_FAR_END_ALERTING),
+                             media);
+                }
+            }
+        }
+
+        if (!rcv_port_started) {
+            /*
+             * Since we had SDP we thought inband ringback was in order but
+             * media attributes indicate the receive port is to remain
+             * closed. In this case, go ahead and apply local ringback tone
+             * or user will hear silence. We do not depend on ringback delay
+             * timer to start the local ringback tone so we have to start it
+             * here.
+             */
+            status = platform_get_phrase_index_str(CALL_ALERTING_LOCAL);
+            lsm_util_start_tone(VCM_ALERTING_TONE, FALSE, lsm_get_ms_ui_call_handle(line, call_id, CC_NO_CALL_ID), dcb->group_id,
+                           ((audio_media != NULL) ? audio_media->refid :
+                                                   CC_NO_MEDIA_REF_ID),
+                           VCM_PLAY_TONE_TO_EAR);
+        } else {
+            lsm_set_ringer(lcb, call_id, line, YES);
+        }
+    }
+
+    ccb = fsmcnf_get_ccb_by_call_id(call_id);
+
+    /* Update call information */
+    lsm_internal_update_call_info(lcb, dcb);
+
+    /* This is the case where remote end of the call has been early trasnfered
+     * to another endpoint.
+     */
+
+    ccb = fsmcnf_get_ccb_by_call_id(lcb->call_id);
+
+    if ((ccb != NULL) && (ccb->active == TRUE) &&
+        (ccb->flags & LCL_CNF)) {
+        call_state = evConference;
+    } else {
+        call_state = evRingOut;
+    }
+
+    /* If an invalid DN is dialed during CFA then CCM sends 183/Session Progress
+     * (a.k.a. far end alerting) so it can play the invalid DN announcement. In
+     * this case CCM sends 404 Not Found after playing the announcement. If we
+     * are here due to that situation then don't propagate call info or status
+     * to UI side as it will display "Session Progress" on the status line and
+     * will log the DN in Placed calls; and we don't want either. Just skip the
+     * update and following 404 Not Found will take care of playing/displaying
+     * Reorder. Note that this condition may occur only in TNP/CCM mode.
+     */
+    if (dcb->active_feature != CC_FEATURE_CFWD_ALL) {
+       if(!is_session_progress) {//CSCtc18750
+               /*
+                * update placed call info in call history with dialed digits
+                */
+               if (dcb->placed_call_update_required) {
+                   lsm_update_placed_callinfo(dcb);
+                   dcb->placed_call_update_required = FALSE;
+               }
+
+               if (status) {
+                   ui_set_call_status(status, line, lcb->ui_id);
+               }
+       }
+
+        lsm_ui_call_state(call_state, line, lcb, CC_CAUSE_NORMAL);
+
+    }
+    /* For roundtable phones, UI will be in dial state, which is different from TNP UI,
+     * TNP UI does not have different dialing layer. In this case offhook dialing screen
+     * does not vanish untill GSM provides procced call status, hence all the softkeys are
+     * available during CFWD, which is not correct
+     */
+    if (dcb->active_feature == CC_FEATURE_CFWD_ALL) {
+        lsm_ui_call_state(evReorder, line, lcb, CC_CAUSE_NORMAL);
+    }
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_call_received (lsm_lcb_t *lcb, cc_state_data_call_received_t *data)
+{
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_alerting (lsm_lcb_t *lcb, cc_state_data_alerting_t *data)
+{
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+    fsmdef_dcb_t   *dcb;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    lsm_change_state(lcb, __LINE__, LSM_S_RINGIN);
+
+    dcb->ui_update_required = TRUE;
+    lsm_internal_update_call_info(lcb, dcb);
+
+    ui_new_call(evRingIn, line, lcb->ui_id, NORMAL_CALL,
+                dcb->caller_id.call_instance_id, FALSE);
+
+    fsmutil_set_shown_calls_ci_element(dcb->caller_id.call_instance_id, line);
+    lsm_ui_call_state(evRingIn, line, lcb, CC_CAUSE_NORMAL);
+    lsm_update_inalert_status(line, lcb->ui_id, data, TRUE);
+
+
+    lsm_set_ringer(lcb, call_id, line, YES);
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_answered (lsm_lcb_t *lcb, cc_state_data_answered_t *data)
+{
+    line_t          line = lcb->line;
+    fsmdef_dcb_t   *dcb;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    lsm_change_state(lcb, __LINE__, LSM_S_OFFHOOK);
+
+
+    lsm_internal_update_call_info(lcb, dcb);
+
+    vcmControlRinger(VCM_RING_OFF, NO, NO, line, dcb->call_id);
+
+    lsm_ui_call_state(evOffHook, line, lcb, CC_CAUSE_NORMAL);
+
+    //vcmActivateWlan(TRUE);
+
+    (void) lsm_stop_tone(lcb, NULL);
+
+    return (CC_RC_SUCCESS);
+}
+
+/**
+ *
+ * Function updates media paths based on the negotated parameters.
+ *
+ * @param lcb          line control block
+ * @param caller_fname caller function name
+ *
+ * @return  none
+ *
+ * @pre     (dcb not_eq NULL)
+ * @pre     (fname not_eq NULL)
+ */
+static void
+lsm_update_media (lsm_lcb_t *lcb, const char *caller_fname)
+{
+    static const char fname[] = "lsm_update_media";
+    fsmdef_dcb_t   *dcb;
+    fsmdef_media_t *media;
+    boolean        rx_refresh;
+    boolean        tx_refresh;
+    char           addr_str[MAX_IPADDR_STR_LEN];
+    int            i;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL),
+                    fname);
+        return;
+    }
+
+    addr_str[0] = '\0';
+
+    /*
+     * Close rx and tx port for media change. Check media direction
+     * to see if port should be closed or remain open. If the port
+     * needs to be kept open, lsm_close_* functions will check to
+     * see if any media attributes have changed. If anything has
+     * changed, the port is closed, otherwise the port remains
+     * open. If media direction is not set, treat as if set to inactive.
+     * Also, if multicast leave rx_refresh and tx_refresh to FALSE to
+     * force a socket close.
+     */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb) {
+        if (!GSMSDP_MEDIA_ENABLED(media) ||
+            FSM_CHK_FLAGS(media->hold, FSM_HOLD_LCL)) {
+            /* this entry is not active or locally held */
+            continue;
+        }
+
+        rx_refresh = FALSE;
+        tx_refresh = FALSE;
+
+        if ((media->direction_set) && (media->is_multicast == FALSE)) {
+            if (media->direction == SDP_DIRECTION_SENDRECV ||
+                media->direction == SDP_DIRECTION_RECVONLY) {
+                rx_refresh = TRUE;
+            }
+            if (media->direction == SDP_DIRECTION_SENDRECV ||
+                media->direction == SDP_DIRECTION_SENDONLY) {
+                tx_refresh = TRUE;
+            }
+        }
+
+        lsm_close_rx(lcb, rx_refresh, media);
+        lsm_close_tx(lcb, tx_refresh, media);
+
+        if (LSMDebug) {
+            /* debug is enabled, format the dest addr into string */
+            ipaddr2dotted(addr_str, &media->dest_addr);
+            for (i = 0; i < media->num_payloads; i++)
+            {
+                LSM_DEBUG(DEB_L_C_F_PREFIX"%d rx, tx refresh's are %d %d"
+                          ", dir=%d, payload=%d addr=%s, multicast=%d\n",
+                          DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line,
+                          dcb->call_id, fname), media->refid, rx_refresh,
+                          tx_refresh, media->direction,
+                          media->payloads[i], addr_str, media->is_multicast );
+            }
+        }
+        if (rx_refresh ||
+            (media->is_multicast &&
+             media->direction_set &&
+             media->direction == SDP_DIRECTION_RECVONLY)) {
+            lsm_rx_start(lcb, caller_fname, media);
+        }
+        if (tx_refresh) {
+            lsm_tx_start(lcb, caller_fname, media);
+        }
+        if ( rx_refresh &&
+              (media->cap_index == CC_VIDEO_1)) {
+             // force an additional update so UI can refresh the remote view
+             ui_update_video_avail(dcb->line, lcb->ui_id, dcb->cur_video_avail);
+             LSM_DEBUG(DEB_L_C_F_PREFIX"Video Avail Called %d",
+                  DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, lcb->ui_id, fname), dcb->cur_video_avail);
+         }
+    }
+}
+
+/**
+ *
+ * Function to set media attributes and set the ui state.
+ *
+ * @param lcb        line control block
+ * @param line       line
+ * @param fname      caller function name
+ *
+ * @return  none
+ *
+ * @pre     (dcb not_eq NULL)
+ * @pre     (fname not_eq NULL)
+ */
+static void
+lsm_call_state_media (lsm_lcb_t *lcb, line_t line, const char *fname)
+{
+    fsmcnf_ccb_t   *ccb;
+    fsmdef_dcb_t   *dcb;
+    call_events     call_state;
+    callid_t        call_id = lcb->call_id;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL),
+                    "lsm_call_state_media");
+        return;
+    }
+
+    ccb = fsmcnf_get_ccb_by_call_id(call_id);
+
+    /* Update media parametes to the platform */
+    lsm_update_media(lcb, fname);
+
+    if ((ccb != NULL) && (ccb->active == TRUE)) {
+        /* For joined call leg, do not change UI state to conf. */
+        if ((ccb->flags & JOINED) ||
+            (fname == cc_state_name(CC_STATE_RESUME))) {
+            call_state = evConnected;
+        } else {
+            call_state = evConference;
+        }
+    } else {
+        call_state = evConnected;
+    }
+
+    /*
+     * Possible media changes, update call information and followed
+     * by the state update. This is important sequence for 7940/60
+     * SIP to force the BTXML update.
+     */
+    // Commenting out original code for CSCsv72370. Leaving here for reference.
+    // lsm_internal_update_call_info(lcb, dcb);
+
+    lsm_ui_call_state(call_state, line, lcb, CC_CAUSE_NORMAL);
+    // CSCsv72370 - Important sequence for TNP this follows state change
+    lsm_internal_update_call_info(lcb, dcb);
+}
+
+
+static cc_rcs_t
+lsm_connected (lsm_lcb_t *lcb, cc_state_data_connected_t *data)
+{
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+    fsmdef_dcb_t   *dcb;
+    int             alerting = YES;
+    call_events     original_call_event;
+    int ringSettingBusyStationPolicy;
+    boolean tone_stop_bool = TRUE;
+    int             sdpmode = 0;
+    boolean         start_ice = FALSE;
+
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    original_call_event = lcb->previous_call_event;
+    /*
+     * If a held call is being resumed, check the
+     * policy to see if the phone should resume alerting.
+     */
+    if (lcb->state == LSM_S_HOLDING) {
+        config_get_value(CFGID_RING_SETTING_BUSY_POLICY,
+                     &ringSettingBusyStationPolicy,
+                     sizeof(ringSettingBusyStationPolicy));
+        if (0 == ringSettingBusyStationPolicy) {
+            alerting = NO;
+        }
+
+               /*
+                * CSCtd31671: When agent phone resumes from a held call with
+            * monitor warning tone, the tone should not be stopped.
+            */
+               if(lcb->dcb->active_tone == VCM_MONITORWARNING_TONE || lcb->dcb->active_tone == VCM_RECORDERWARNING_TONE)
+                       tone_stop_bool = FALSE;
+    }
+
+    /* Don't try to start ICE unless this is the first time connecting.
+     *  TODO(ekr@rtfm.com): Is this the right ICE start logic? What about restarts
+    */
+    if (strlen(dcb->peerconnection) && lcb->state != LSM_S_CONNECTED)
+      start_ice = TRUE;
+
+    lsm_change_state(lcb, __LINE__, LSM_S_CONNECTED);
+
+    if (!sdpmode) {
+        if (tone_stop_bool == TRUE)
+            (void) lsm_stop_tone(lcb, NULL);
+    }
+
+    /* Start ICE */
+    if (start_ice) {
+      short res = vcmStartIceChecks(dcb->peerconnection);
+      /* TODO(emannion): Set state to dead here. */
+      if (res)
+        return CC_RC_SUCCESS;
+    }
+
+    /*
+     * Open the RTP receive channel.
+     */
+    lsm_call_state_media(lcb, line, cc_state_name(CC_STATE_CONNECTED));
+
+
+    if (!sdpmode) {
+        vcmEnableSidetone(YES);
+
+        lsm_set_ringer(lcb, call_id, line, alerting);
+    }
+
+    FSM_RESET_FLAGS(lcb->flags, LSM_FLAGS_ANSWER_PENDING);
+    FSM_RESET_FLAGS(lcb->flags, LSM_FLAGS_DUSTING);
+
+    /*
+     * update placed call info in call history with dialed digits
+     */
+    if (dcb->placed_call_update_required) {
+        lsm_update_placed_callinfo(dcb);
+        dcb->placed_call_update_required = FALSE;
+    }
+
+    /*
+     * If UI state was changed, update status line.
+     */
+    if (lcb->previous_call_event != original_call_event) {
+        if (lcb->previous_call_event == evConference) {
+        } else {
+
+             ui_set_call_status(platform_get_phrase_index_str(CALL_CONNECTED),
+                               line, lcb->ui_id);
+        }
+    }
+    ui_update_video_avail(line, lcb->ui_id, dcb->cur_video_avail);
+    return (CC_RC_SUCCESS);
+}
+
+/**
+ * Function: lsm_hold_reversion
+ * Perform Hold Reversion on the given call
+ * any other call pending then it should play call waiting tone.
+ *
+ * @param lsm_lcb_t     lcb for this call
+ *
+ * @return  cc_rcs_t   SUCCESS or FAILURE of the operation
+ *
+ * @pre     (lcb not_eq NULL)
+ */
+
+static cc_rcs_t
+lsm_hold_reversion (lsm_lcb_t *lcb)
+{
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+
+    // Update call state on the JAVA side
+    lsm_ui_call_state(evHoldRevert, line, lcb, CC_CAUSE_NORMAL);
+
+    if (lsm_find_state(LSM_S_RINGIN) > CC_NO_CALL_ID) {
+        // No Reversion ringing if we have calls in ringing state
+        return CC_RC_SUCCESS;
+    }
+    ui_set_notification(line, call_id,
+                        (char *)INDEX_STR_HOLD_REVERSION, CALL_ALERT_TIMEOUT,
+                        FALSE, HR_NOTIFY_PRI);
+    lsm_reversion_ringer(lcb, call_id, line);
+
+    return (CC_RC_SUCCESS);
+}
+
+/*
+ * lsm_hold_local
+ *
+ * Move the phone into the Hold state.
+ *
+ * Function is used when the local side initiated the hold.
+ */
+static cc_rcs_t
+lsm_hold_local (lsm_lcb_t *lcb, cc_state_data_hold_t *data)
+{
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+    fsmdef_dcb_t   *dcb;
+    cc_causes_t    cause;
+    int ringSettingBusyStationPolicy;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    /*
+     * Stop ringer if spoofing ringout for CCM
+     */
+    if (dcb->spoof_ringout_applied) {
+        (void) lsm_stop_tone(lcb, NULL);
+    }
+
+    /* hard close receive and transmit channels for all media entries */
+    lsm_close_rx(lcb, FALSE, NULL);
+    lsm_close_tx(lcb, FALSE, NULL);
+    /*
+     * Note that local hold does not have any newer UI information from the
+     * network. Note need to update the call information and the UI will
+     * be collapsed with "blocked" icon to indicate hold.
+     */
+
+    lsm_change_state(lcb, __LINE__, LSM_S_HOLDING);
+    /* Round table phones need cause for the transfer or conference
+     Do not set the cause if the conference or transfer is created by
+     remote-cc
+     */
+    cause = CC_CAUSE_NORMAL;
+    if (data->reason == CC_REASON_XFER) {
+            cause = CC_CAUSE_XFER_LOCAL;
+    } else if (data->reason == CC_REASON_CONF) {
+        cause = CC_CAUSE_CONF;
+    }
+
+    lsm_ui_call_state(evHold, line, lcb, cause);
+
+    ui_set_call_status(platform_get_phrase_index_str(CALL_INITIATE_HOLD),
+                       line, lcb->ui_id);
+
+    config_get_value(CFGID_RING_SETTING_BUSY_POLICY,
+                     &ringSettingBusyStationPolicy,
+                     sizeof(ringSettingBusyStationPolicy));
+    if (ringSettingBusyStationPolicy) {
+        lsm_set_ringer(lcb, call_id, line, YES);
+    } else {
+        /*
+         * If the hold reason is internal this means the phone logic is placing
+         * a call on hold, not the user. Thus don't update the alerting for the
+         * hold state as the user should not hear the alerting pattern change
+         * as they did not place the call on hold. The phone places calls on hold
+         * in cases such as the phone has an active call, another call comes in
+         * for that line and the new call is answered. Therefore the phone places the
+         * active call on hold before answering the incoming call.
+         *
+         */
+        if (data->reason == CC_REASON_INTERNAL) {
+            lsm_set_ringer(lcb, call_id, line, NO);
+        } else {
+            lsm_set_ringer(lcb, call_id, line, YES);
+        }
+    }
+
+    vcmActivateWlan(FALSE);
+
+    return (CC_RC_SUCCESS);
+}
+
+
+/*
+ * lsm_hold_remote
+ *
+ * Move the phone into the Hold state.
+ *
+ * Function is used when the remote side initiated the hold.
+ */
+static cc_rcs_t
+lsm_hold_remote (lsm_lcb_t *lcb, cc_state_data_hold_t *data)
+{
+    static const char fname[] = "lsm_hold_remote";
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+    const char     *prompt_status;
+    fsmdef_dcb_t   *dcb;
+    fsmdef_media_t *media;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    /* close and re-open receive channel for all media entries */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb) {
+        if (!GSMSDP_MEDIA_ENABLED(media)) {
+            /* this entry is not active */
+            continue;
+        }
+        if (media->direction_set &&
+            media->direction == SDP_DIRECTION_INACTIVE) {
+            lsm_close_rx(lcb, FALSE, media);
+        } else {
+            lsm_close_rx(lcb, TRUE, media);
+        }
+
+        /* reopen the receive channel if the direction is RECVONLY */
+        if (media->direction_set &&
+            media->direction == SDP_DIRECTION_RECVONLY) {
+            lsm_rx_start(lcb, fname, media);
+        }
+        /* close tx if media is not inactive or receive only */
+        if ((media->direction == SDP_DIRECTION_INACTIVE) ||
+            (media->direction == SDP_DIRECTION_RECVONLY)) {
+            lsm_close_tx(lcb, FALSE, media);
+        }
+    }
+
+    lsm_internal_update_call_info(lcb, dcb);
+
+    lsm_ui_call_state(evRemHold, line, lcb, CC_CAUSE_NORMAL);
+
+    prompt_status = ((lcb->state == LSM_S_CONNECTED) ?
+                     platform_get_phrase_index_str(CALL_CONNECTED) :
+                     platform_get_phrase_index_str(CALL_INITIATE_HOLD));
+    ui_set_call_status(prompt_status, line, lcb->ui_id);
+
+    lsm_set_ringer(lcb, call_id, line, YES);
+
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_hold (lsm_lcb_t *lcb, cc_state_data_hold_t *data)
+{
+    cc_rcs_t cc_rc;
+
+    if (data == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    LSM_DEBUG(get_debug_string(LSM_DBG_INT1), lcb->call_id, lcb->line,
+              "lsm_hold", "local", data->local);
+
+    switch (data->local) {
+    case (TRUE):
+        cc_rc = lsm_hold_local(lcb, data);
+        break;
+
+    case (FALSE):
+        cc_rc = lsm_hold_remote(lcb, data);
+        break;
+
+    default:
+        cc_rc = CC_RC_ERROR;
+        break;
+    }
+    vcmEnableSidetone(NO);
+    return (cc_rc);
+}
+
+
+static cc_rcs_t
+lsm_resume_local (lsm_lcb_t *lcb, cc_state_data_resume_t *data)
+{
+    line_t        line = lcb->line;
+    fsmdef_dcb_t *dcb;
+
+    lsm_change_state(lcb, __LINE__, LSM_S_HOLDING);
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    ui_set_call_status(platform_get_phrase_index_str(CALL_CONNECTED),
+                       line, lcb->ui_id);
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_resume_remote (lsm_lcb_t *lcb, cc_state_data_resume_t *data)
+{
+    callid_t      call_id = lcb->call_id;
+    line_t        line = lcb->line;
+    const char   *prompt_status;
+
+    if (lcb->dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    lsm_update_media(lcb, cc_state_name(CC_STATE_RESUME));
+
+    prompt_status = ((lcb->state == LSM_S_CONNECTED) ?
+                     platform_get_phrase_index_str(CALL_CONNECTED) :
+                     platform_get_phrase_index_str(CALL_INITIATE_HOLD));
+    ui_set_call_status(prompt_status, line, lcb->ui_id);
+
+    lsm_set_ringer(lcb, call_id, line, YES);
+
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_resume (lsm_lcb_t *lcb, cc_state_data_resume_t *data)
+{
+    cc_rcs_t cc_rc;
+
+    if (data == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    LSM_DEBUG(get_debug_string(LSM_DBG_INT1), lcb->call_id, lcb->line,
+              "lsm_resume", "local", data->local);
+
+    switch (data->local) {
+    case (TRUE):
+        cc_rc = lsm_resume_local(lcb, data);
+        break;
+
+    case (FALSE):
+        cc_rc = lsm_resume_remote(lcb, data);
+        break;
+
+    default:
+        cc_rc = CC_RC_ERROR;
+        break;
+    }
+
+    vcmActivateWlan(TRUE);
+
+    vcmEnableSidetone(YES);
+    return (cc_rc);
+}
+
+
+static cc_rcs_t
+lsm_onhook (lsm_lcb_t *lcb, cc_state_data_onhook_t *data)
+{
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+    fsmdef_dcb_t   *dcb;
+    cc_causes_t     cause;
+    int             sdpmode = 0;
+
+       config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    dp_int_onhook(line, call_id);
+
+    /* hard close receive and transmit channels for all media entries */
+    lsm_close_rx(lcb, FALSE, NULL);
+    lsm_close_tx(lcb, FALSE, NULL);
+
+    lsm_change_state(lcb, __LINE__, LSM_S_IDLE);
+
+    if (lsm_is_phone_inactive()) {
+        vcmEnableSidetone(NO);
+    }
+
+    ui_set_call_status(ui_get_idle_prompt_string(), line, lcb->ui_id);
+
+
+    (void) lsm_stop_tone(lcb, NULL);
+
+    if (!sdpmode) {
+        vcmControlRinger(VCM_RING_OFF, NO, NO, line, dcb->call_id);
+    }
+
+    lsm_set_ringer(lcb, call_id, line, YES);
+
+    cause = data->cause;
+    if (FSM_CHK_FLAGS(dcb->flags, FSMDEF_F_XFER_COMPLETE)) {
+        DEF_DEBUG(DEB_F_PREFIX"Transfer complete.\n", DEB_F_PREFIX_ARGS(LSM, "lsm_onhook"));
+        cause = CC_CAUSE_XFER_COMPLETE;
+    }
+    lsm_ui_call_state(evOnHook, line, lcb, cause);
+
+
+    lsm_free_lcb(lcb);
+
+    vcmActivateWlan(FALSE);
+
+    vcmRemoveBandwidth(lsm_get_ms_ui_call_handle(line, call_id, CC_NO_CALL_ID));
+
+    return (CC_RC_SUCCESS);
+}
+
+static cc_rcs_t
+lsm_call_failed (lsm_lcb_t *lcb, cc_state_data_call_failed_t *data)
+{
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+    vcm_tones_t     tone;
+    lsm_states_t    line_state;
+    const char     *status = NULL;
+    call_events     state;
+    boolean         send_call_info = TRUE;
+       fsmdef_dcb_t   *dcb;
+    boolean         must_log = FALSE;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    /* For busy generated by UI-STATE in 183, do not manipulate the
+     * media port
+     */
+   if (data->cause != CC_CAUSE_UI_STATE_BUSY) {
+       /* hard close receive and transmit channels for all media entries */
+       lsm_close_rx(lcb, FALSE, NULL);
+       lsm_close_tx(lcb, FALSE, NULL);
+   }
+
+    switch (data->cause) {
+    case (CC_CAUSE_BUSY):
+        line_state = LSM_S_BUSY;
+        state = evBusy;
+        tone = VCM_LINE_BUSY_TONE;
+        status = platform_get_phrase_index_str(LINE_BUSY);
+        dp_int_update(line, call_id, data->caller_id.called_number);
+        send_call_info = FALSE;
+        break;
+
+    case (CC_CAUSE_UI_STATE_BUSY):
+        line_state = LSM_S_BUSY;
+        state = evBusy;
+        tone = VCM_LINE_BUSY_TONE;
+        dp_int_update(line, call_id, data->caller_id.called_number);
+        break;
+
+    case (CC_CAUSE_INVALID_NUMBER):
+        line_state = LSM_S_INVALID_NUMBER;
+        state = evReorder;
+        tone = VCM_REORDER_TONE;
+        send_call_info = FALSE;
+        break;
+
+    case (CC_CAUSE_CONGESTION):
+    case (CC_CAUSE_PAYLOAD_MISMATCH):
+        dp_int_update(line, call_id, data->caller_id.called_number);
+
+        /* FALLTHROUGH */
+        /*sa_ignore FALL_THROUGH*/
+    default:
+        send_call_info = FALSE;
+        line_state = LSM_S_CONGESTION;
+        state = evReorder;
+        tone = VCM_REORDER_TONE;
+        if ( (data->cause == CC_CAUSE_NO_USER_ANS)||
+             (data->cause == CC_TEMP_NOT_AVAILABLE) ) {
+           must_log = TRUE;
+        }
+        break;
+    }
+
+    lsm_change_state(lcb, __LINE__, line_state);
+
+    if (status) {
+        ui_set_call_status(status, line, lcb->ui_id);
+    }
+
+    if (state == evReorder && !must_log) {
+        ui_log_disposition(dcb->call_id, CC_CALL_LOG_DISP_IGNORE);
+    }
+
+    /* Send call info only if not error */
+    if (send_call_info == TRUE) {
+        ui_call_info(data->caller_id.calling_name,
+                 data->caller_id.calling_number,
+                 data->caller_id.alt_calling_number,
+                 data->caller_id.display_calling_number,
+                 data->caller_id.called_name,
+                 data->caller_id.called_number,
+                 data->caller_id.display_called_number,
+                 data->caller_id.orig_called_name,
+                 data->caller_id.orig_called_number,
+                 data->caller_id.last_redirect_name,
+                 data->caller_id.last_redirect_number,
+                 (calltype_t)dcb->call_type,
+                 line, lcb->ui_id,
+                 dcb->caller_id.call_instance_id,
+                 FSM_GET_SECURITY_STATUS(dcb),
+                 FSM_GET_POLICY(dcb));
+    }
+
+    lsm_ui_call_state(state, line, lcb, CC_CAUSE_NORMAL);
+
+    /* Tone played in remote-cc play tone request, so don't start tone again
+     */
+    if ((data->cause != CC_CAUSE_UI_STATE_BUSY) && (data->cause != CC_CAUSE_REMOTE_DISCONN_REQ_PLAYTONE)) {
+        fsmdef_media_t *audio_media = gsmsdp_find_audio_media(dcb);
+
+        lsm_util_start_tone(tone, FALSE, lsm_get_ms_ui_call_handle(line, call_id, CC_NO_CALL_ID), dcb->group_id,
+                       ((audio_media != NULL) ? audio_media->refid :
+                                                CC_NO_MEDIA_REF_ID),
+                       VCM_PLAY_TONE_TO_EAR);
+    }
+
+    return (CC_RC_SUCCESS);
+}
+
+static void
+lsm_ringer (lsm_lcb_t *lcb, cc_action_data_ringer_t *data)
+{
+    vcm_ring_mode_t ringer;
+    line_t line = lcb->line;
+
+    ringer = (data->on == FALSE) ? (VCM_RING_OFF) : (VCM_FEATURE_RING);
+
+    LSM_DEBUG(DEB_F_PREFIX"CTI RING SETTING: line = %d, ringer Mode = %s,"
+              "Ring once = NO, alertInfo = NO\n", DEB_F_PREFIX_ARGS(LSM, "lsm_ringer"),
+              line, vm_alert_names[ringer]);
+
+    vcmControlRinger(ringer, NO, NO, line, lcb->call_id);
+}
+
+static cc_rcs_t
+lsm_dial_mode (lsm_lcb_t *lcb, cc_action_data_dial_mode_t *data)
+{
+    return (CC_RC_SUCCESS);
+}
+
+
+static cc_rcs_t
+lsm_mwi (lsm_lcb_t *lcb, callid_t call_id, line_t line,
+         cc_action_data_mwi_t *data)
+{
+    ui_set_mwi(line, data->on, data->type, data->newCount, data->oldCount, data->hpNewCount, data->hpOldCount);
+
+    return (CC_RC_SUCCESS);
+}
+
+
+/*
+ *  Function: lsm_update_ui
+ *
+ *  Parameters:
+ *      call_id:
+ *      line:
+ *      data:
+ *
+ *  Description: This function is used to hide the UI platform details from
+ *               the FSMs. This function is provided to allow the FSMs
+ *               to update the UI in certain cases.
+ *
+ *  Returns: rc
+ *
+ */
+cc_rcs_t
+lsm_update_ui (lsm_lcb_t *lcb, cc_action_data_update_ui_t *data)
+{
+    callid_t        call_id = lcb->call_id;
+    line_t          line = lcb->line;
+    lsm_states_t    instance_state;
+    call_events     call_state = evMaxEvent;
+    fsmcnf_ccb_t   *ccb;
+    fsmdef_dcb_t   *dcb;
+    boolean         update = FALSE;
+    boolean         inbound;
+    cc_feature_data_call_info_t *call_info;
+    call_events     original_call_event;
+    lsm_lcb_t       *lcb_tmp;
+    const char      *conf_str;//[] = {(char)0x80, (char)0x34, (char)0x00};
+
+    instance_state = lcb->state;
+
+    switch (data->action) {
+    case CC_UPDATE_CONF_ACTIVE:
+
+        switch (instance_state) {
+        case LSM_S_RINGOUT:
+            call_state = evRingOut;
+            break;
+
+        case LSM_S_CONNECTED:
+        default:
+            ccb = fsmcnf_get_ccb_by_call_id(call_id);
+            if ((ccb != NULL) && (ccb->active == TRUE)) {
+
+                conf_str = platform_get_phrase_index_str(UI_CONFERENCE);
+                lcb_tmp = lsm_get_lcb_by_call_id(ccb->cnf_call_id);
+                dcb = lcb_tmp->dcb;
+                ui_call_info(CALL_INFO_NONE,
+                              CALL_INFO_NONE,
+                              CALL_INFO_NONE,
+                              0,
+                              conf_str,
+                              CALL_INFO_NONE,
+                              0,
+                              CALL_INFO_NONE,
+                              CALL_INFO_NONE,
+                              CALL_INFO_NONE,
+                              CALL_INFO_NONE,
+                              FSMDEF_CALL_TYPE_OUTGOING,
+                              dcb->line, lcb_tmp->ui_id,
+                              dcb->caller_id.call_instance_id,
+                              FSM_GET_SECURITY_STATUS(dcb),
+                              FSM_GET_POLICY(dcb));
+
+                call_state = evConference;
+
+            } else if (instance_state == LSM_S_CONNECTED) {
+
+                call_state = evConnected;
+
+            } else {
+
+                call_state = evRingOut;
+            }
+            break;
+        }                       /* switch (instance_state) { */
+
+        break;
+
+    case CC_UPDATE_CALLER_INFO:
+
+        /* For local conference, do not update the primary
+         * call bubbles call-info. Primary call is already
+         * displaying To conference in this case
+         * But dcb-> caller_id should be updated to
+         * refresh the UI when the call is dropped
+         */
+        ccb = fsmcnf_get_ccb_by_call_id(call_id);
+        if (ccb && (ccb->flags & LCL_CNF) &&
+            (ccb->cnf_call_id == call_id)) {
+            break;
+        }
+
+        call_info = &data->data.caller_info;
+        dcb = lcb->dcb;
+        if (dcb == NULL || call_info == NULL) {
+            return (CC_RC_ERROR);
+        }
+
+        inbound = dcb->inbound;
+        if (call_info->feature_flag & CC_ORIENTATION) {
+            inbound =
+                (call_info->orientation == CC_ORIENTATION_FROM) ? TRUE : FALSE;
+            update = TRUE;
+        }
+
+        if (call_info->feature_flag & CC_CALLER_ID) {
+            update = TRUE;
+            /*
+             * This "if" block, without the "&& inbound" condition, was put in by Serhad
+             * to fix CSCsm58054 and it results in CSCso98110. The "inbound" condition
+             * is added to narrow the scope of CSCsm58054's fix. Note that "inbound" here
+             * refers to the perceived orientation set in call info. So for example, in case
+             * of a 3-way conf, and phone is the last party to receive the call, is ringing
+             * and then be joined into a conference, direction would be outbound. The display
+             * would say "To Conference".
+             */
+            if ( (instance_state == LSM_S_RINGIN) && inbound ) {
+                cc_state_data_alerting_t alerting_data;
+
+                alerting_data.caller_id = dcb->caller_id;
+                lsm_update_inalert_status(line, lcb->ui_id, &alerting_data, TRUE);
+            }
+        }
+
+        if (call_info->feature_flag & CC_CALL_INSTANCE) {
+            update = TRUE;
+        }
+
+        if (call_info->feature_flag & CC_SECURITY) {
+            update = TRUE;
+        }
+
+       if (call_info->feature_flag & CC_POLICY) {
+           update = TRUE;
+       }
+
+        /*
+         * If we are going to spoof ring out, skip the explicit UI update.
+         * the far end alerting handling will update the UI. Do not
+         * update UI twice.
+         */
+        if (dcb->spoof_ringout_requested &&
+            !dcb->spoof_ringout_applied &&
+            lcb->state == LSM_S_CONNECTED) {
+            cc_state_data_far_end_alerting_t alerting_data;
+
+            alerting_data.caller_id = dcb->caller_id;
+            (void) lsm_far_end_alerting(lcb, &alerting_data);
+            dcb->spoof_ringout_applied = TRUE;
+        } else if (update && dcb->ui_update_required) {
+
+            calltype_t call_type;
+
+            if (dcb->call_type == FSMDEF_CALL_TYPE_FORWARD) {
+                call_type = (inbound) ? (calltype_t)dcb->call_type:FSMDEF_CALL_TYPE_OUTGOING;
+            } else {
+                if (inbound) {
+                    call_type = FSMDEF_CALL_TYPE_INCOMING;
+                } else {
+                    call_type = FSMDEF_CALL_TYPE_OUTGOING;
+                }
+            }
+
+             ui_call_info(dcb->caller_id.calling_name,
+                          dcb->caller_id.calling_number,
+                          dcb->caller_id.alt_calling_number,
+                          dcb->caller_id.display_calling_number,
+                          dcb->caller_id.called_name,
+                          dcb->caller_id.called_number,
+                          dcb->caller_id.display_called_number,
+                          dcb->caller_id.orig_called_name,
+                          dcb->caller_id.orig_called_number,
+                          dcb->caller_id.last_redirect_name,
+                          dcb->caller_id.last_redirect_number,
+                          call_type,
+                          line,
+                          lcb->ui_id,
+                          dcb->caller_id.call_instance_id,
+                          FSM_GET_SECURITY_STATUS(dcb),
+                          FSM_GET_POLICY(dcb));
+
+            dcb->ui_update_required = FALSE;
+
+            conf_str = platform_get_phrase_index_str(UI_CONFERENCE);
+           if(cpr_strncasecmp(dcb->caller_id.called_name, conf_str, strlen(conf_str)) == 0){
+                   dcb->is_conf_call = TRUE;
+           } else {
+                   dcb->is_conf_call = FALSE;
+           }
+        }
+
+        break;
+
+    case CC_UPDATE_SET_CALL_STATUS:
+        {
+            /* set call status line */
+            cc_set_call_status_data_t *call_status_p =
+                &data->data.set_call_status_parms;
+            ui_set_call_status(call_status_p->phrase_str_p, call_status_p->line,
+                               lcb->ui_id);
+            break;
+        }
+    case CC_UPDATE_SET_NOTIFICATION:
+        {
+            /* set status line notification */
+            cc_set_notification_data_t *call_notification_p =
+                &data->data.set_notification_parms;
+            ui_set_notification(line, lcb->ui_id,
+                                call_notification_p->phrase_str_p,
+                                call_notification_p->timeout, FALSE,
+                                (char)call_notification_p->priority);
+            break;
+        }
+    case CC_UPDATE_CLEAR_NOTIFICATION:
+        /* clear status line notification */
+        ui_clear_notification();
+        break;
+
+    case CC_UPDATE_SECURITY_STATUS:
+        /* update security status */
+        break;
+
+    case CC_UPDATE_XFER_PRIMARY:
+        call_state = evConnected;
+        break;
+
+    case CC_UPDATE_CALL_PRESERVATION:
+
+        /* Call is in preservation mode. Update UI so that only endcall softkey is available */
+        ui_call_in_preservation(line, lcb->ui_id);
+        break;
+
+    case CC_UPDATE_CALL_CONNECTED:
+        if (instance_state == LSM_S_CONNECTED) {
+            call_state = evConnected;
+        }
+        break;
+
+    case CC_UPDATE_CONF_RELEASE:
+        dcb = lcb->dcb;
+
+        if (instance_state == LSM_S_CONNECTED) {
+            call_state = evConnected;
+
+        } else if (instance_state == LSM_S_RINGOUT) {
+            call_state = evRingOut;
+        }
+
+        /*
+         * If we are going to spoof ring out, skip the explicit UI update.
+         * the far end alerting handling will update the UI. Do not
+         * update UI twice.
+         */
+        if (dcb->spoof_ringout_requested &&
+            !dcb->spoof_ringout_applied &&
+            lcb->state == LSM_S_CONNECTED) {
+            cc_state_data_far_end_alerting_t alerting_data;
+
+            alerting_data.caller_id = dcb->caller_id;
+            (void) lsm_far_end_alerting(lcb, &alerting_data);
+            dcb->spoof_ringout_applied = TRUE;
+
+            call_state = evRingOut;
+
+        } else {
+            calltype_t call_type;
+            if (dcb->orientation == CC_ORIENTATION_FROM) {
+                call_type = FSMDEF_CALL_TYPE_INCOMING;
+            } else if (dcb->orientation == CC_ORIENTATION_TO) {
+                call_type = FSMDEF_CALL_TYPE_OUTGOING;
+            } else {
+                call_type = (calltype_t)(dcb->call_type);
+            }
+            ui_call_info(dcb->caller_id.calling_name,
+                          dcb->caller_id.calling_number,
+                          dcb->caller_id.alt_calling_number,
+                          dcb->caller_id.display_calling_number,
+                          dcb->caller_id.called_name,
+                          dcb->caller_id.called_number,
+                          dcb->caller_id.display_called_number,
+                          dcb->caller_id.orig_called_name,
+                          dcb->caller_id.orig_called_number,
+                          dcb->caller_id.last_redirect_name,
+                          dcb->caller_id.last_redirect_number,
+                          call_type,
+                          line,
+                          lcb->ui_id,
+                          dcb->caller_id.call_instance_id,
+                          FSM_GET_SECURITY_STATUS(dcb),
+                          FSM_GET_POLICY(dcb));
+        }
+
+
+        break;
+
+    default:
+        break;
+    }
+
+    if (call_state != evMaxEvent) {
+        original_call_event = lcb->previous_call_event;
+
+        lsm_ui_call_state(call_state, line, lcb, CC_CAUSE_NORMAL);
+        if (original_call_event != call_state) {
+            /* Call state changed, take care of special event */
+            switch (call_state) {
+            case evConference:
+                break;
+
+            case evConnected:
+            case evWhisper:
+                ui_set_call_status(
+                           platform_get_phrase_index_str(CALL_CONNECTED),
+                           line, lcb->ui_id);
+                break;
+
+            default:
+                break;
+            }
+        }
+    }
+
+    return (CC_RC_SUCCESS);
+}
+
+
+/*
+ *  Function: lsm_update_placed_callinfo
+ *
+ *  Description: this helps log dialed digits (as opposed to RPID provided
+ *               value) into placed calls.  This also decides whether to
+ *               log called party name received in RPID.
+ *
+ *  Parameters: dcb - pointer to default SM control block
+ *
+ *  Returns: none
+ *
+ */
+#define CISCO_PLAR_STRING  "x-cisco-serviceuri-offhook"
+void
+lsm_update_placed_callinfo (void *data)
+{
+    const char     *tmp_called_number = NULL;
+    const char     *called_name = NULL;
+    fsmdef_dcb_t   *dcb = NULL;
+    lsm_lcb_t      *lcb;
+    static const char fname[] = "lsm_update_placed_callinfo";
+    boolean has_called_number = FALSE;
+
+    LSM_DEBUG(DEB_F_PREFIX"Entering ...\n", DEB_F_PREFIX_ARGS(LSM, fname));
+    dcb = (fsmdef_dcb_t *) data;
+    lcb = lsm_get_lcb_by_call_id(dcb->call_id);
+    if (lcb == NULL) {
+        LSM_DEBUG(DEB_F_PREFIX"Exiting: lcb not found\n", DEB_F_PREFIX_ARGS(LSM, fname));
+        return;
+    }
+
+    if (dcb->caller_id.called_number != NULL &&
+        dcb->caller_id.called_number[0] != NUL) {
+        has_called_number = TRUE;
+    }
+
+
+    tmp_called_number = lsm_get_gdialed_digits();
+
+
+    /* if tmp_called_number is NULL or empty, return */
+    if (tmp_called_number == NULL || (*tmp_called_number) == NUL) {
+        LSM_DEBUG(DEB_L_C_F_PREFIX"Exiting : dialed digits is empty\n",
+                  DEB_L_C_F_PREFIX_ARGS(LSM, lcb->line, lcb->call_id, fname));
+        return;
+    }
+
+    /*
+     * if tmp_called_number is same as what we receive in RPID,
+     * then get the called name from RPID if provided.
+     */
+    if (has_called_number) {
+        if (strcmp(tmp_called_number, CISCO_PLAR_STRING) == 0) {
+            tmp_called_number = dcb->caller_id.called_number;
+        }
+        /* if RPID number matches, dialed digits, use RPID name */
+        if (strcmp(dcb->caller_id.called_number, tmp_called_number) == 0) {
+            called_name = dcb->caller_id.called_name;
+        } else {
+               char tmp_str[STATUS_LINE_MAX_LEN];
+               platGetPhraseText(STR_INDEX_ANONYMOUS_SPACE, (char *)tmp_str, STATUS_LINE_MAX_LEN - 1);
+            if(strcmp(dcb->caller_id.called_number,tmp_str) == 0
+               && strcmp(dcb->caller_id.orig_rpid_number, tmp_called_number) == 0
+               && strcmp(dcb->caller_id.called_name, platform_get_phrase_index_str(UI_UNKNOWN)) != 0) {
+                 called_name = dcb->caller_id.called_name;
+            }
+        }
+    }
+    ui_update_placed_call_info(lcb->line, lcb->call_id, called_name,
+                               tmp_called_number);
+    LSM_DEBUG(DEB_L_C_F_PREFIX"Exiting: invoked ui_update_placed_call_info()\n",
+              DEB_L_C_F_PREFIX_ARGS(LSM, lcb->line, lcb->call_id, fname));
+}
+
+cc_int32_t
+lsm_show_cmd (cc_int32_t argc, const char *arv[])
+{
+    int             i = 0;
+    lsm_lcb_t      *lcb;
+
+    debugif_printf("\n------------------ LSM lcbs -------------------");
+    debugif_printf("\ni   call_id  line  state             lcb");
+    debugif_printf("\n-----------------------------------------------\n");
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        debugif_printf("%-2d  %-7d  %-4d  %-16s  0x%8p\n",
+                       i++, lcb->call_id, lcb->line,
+                       lsm_state_name(lcb->state), lcb);
+
+    }
+
+    return (0);
+}
+
+void
+lsm_init_config (void)
+{
+    /*
+     * The silent period between call waiting bursts is now configurable
+     * for TNP phones. Store away the value for the callwaiting code to use.
+     * The config is in seconds, but CPR expects the duration in milliseconds
+     * thus multiply the config value by 1000. Non-TNP phones default to
+     * 10 seconds.
+     */
+    config_get_value(CFGID_CALL_WAITING_SILENT_PERIOD, &callWaitingDelay,
+                     sizeof(callWaitingDelay));
+    callWaitingDelay = callWaitingDelay * 1000;
+}
+
+void
+lsm_init (void)
+{
+    static const char fname[] = "lsm_init";
+    lsm_lcb_t *lcb;
+    int i;
+
+    /*
+     * Init the lcbs.
+     */
+    lsm_lcbs = (lsm_lcb_t *) cpr_calloc(LSM_MAX_LCBS, sizeof(lsm_lcb_t));
+    if (lsm_lcbs == NULL) {
+        LSM_ERR_MSG(LSM_F_PREFIX"lsm_lcbs cpr_calloc returned NULL\n", fname);
+        return;
+    }
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        lsm_init_lcb(lcb);
+    }
+
+    /*
+     * Create tones and continous tone timer. The same call back function
+     * is utilized for each of these timers.
+     */
+    lsm_tmr_tones = cprCreateTimer("lsm_tmr_tones",
+                                   GSM_MULTIPART_TONES_TIMER,
+                                   TIMER_EXPIRATION, gsm_msg_queue);
+    lsm_continuous_tmr_tones = cprCreateTimer("lsm_continuous_tmr_tones",
+                                              GSM_CONTINUOUS_TONES_TIMER,
+                                              TIMER_EXPIRATION,
+                                              gsm_msg_queue);
+    lsm_tone_duration_tmr = cprCreateTimer("lsm_tone_duration_tmr",
+                                                  GSM_TONE_DURATION_TIMER,
+                                                  TIMER_EXPIRATION, gsm_msg_queue);
+    lsm_init_config();
+
+    for (i=0 ; i<MAX_REG_LINES; i++) {
+        lsm_call_perline[i] = 0;
+        lsm_mnc_reached[i] = FALSE;
+        lsm_bt_reached[i] = FALSE;
+    }
+
+    memset(cfwdall_state_in_ccm_mode, 0, sizeof(cfwdall_state_in_ccm_mode));
+}
+
+void
+lsm_shutdown (void)
+{
+    (void) cprDestroyTimer(lsm_tmr_tones);
+
+    (void) cprDestroyTimer(lsm_continuous_tmr_tones);
+
+    cpr_free(lsm_lcbs);
+}
+
+/**
+ *
+ * Peform reset for lsm, include all the variables that has to be reset
+ *
+ * @param none
+ *
+ * @return  none
+ *
+ */
+void
+lsm_reset (void)
+{
+    line_t line;
+    int    i;
+    lsm_lcb_t *lcb;
+
+    lsm_init_config();
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        lsm_init_lcb(lcb);
+    }
+
+    for (i=0 ; i<MAX_REG_LINES; i++) {
+        lsm_call_perline[i] = 0;
+        lsm_mnc_reached[i] = FALSE;
+        lsm_bt_reached[i] = FALSE;
+    }
+
+    for (line=0; line < MAX_REG_LINES+1; line++) {
+
+        cc_line_ringer_mode[line] = CC_RING_DEFAULT;
+    }
+}
+
+/*
+ * cc_call_attribute
+ * This sets call attribute. During the conf or xfer consultation phase,
+ * far end of the 1st call may disconnect the call. In this case phone has to
+ * display the regular call softkey set instead of consultation softkey set.
+ * For this reason GSM will call cc_call_attribute to the remaining call,
+ * when original call is disconnected.
+ */
+void
+cc_call_attribute (callid_t call_id, line_t line, call_attr_t attribute)
+{
+    static const char fname[] = "cc_call_attribute";
+
+
+    LSM_DEBUG(DEB_L_C_F_PREFIX"attribute=%d",
+                         DEB_L_C_F_PREFIX_ARGS(LSM, line, call_id, fname), attribute);
+
+    ui_set_call_attr(line, call_id, attribute);
+}
+
+
+/*
+ * lsm_call_state
+ * This routine is responsible for responding to requests from the CSM and
+ * doing whatever is required via platform specific routines.
+ */
+void
+cc_call_state (callid_t call_id, line_t line, cc_states_t state,
+               cc_state_data_t *data)
+{
+    static const char fname[] = "cc_call_state";
+    cc_rcs_t   result = CC_RC_SUCCESS;
+    lsm_lcb_t *lcb;
+
+    LSM_DEBUG(get_debug_string(LSM_DBG_ENTRY), call_id, line,
+              cc_state_name(state));
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+
+    if (lcb == NULL) {
+        LSM_DEBUG(get_debug_string(DEBUG_INPUT_NULL), fname);
+        return;
+    }
+
+    switch (state) {
+    case CC_STATE_OFFHOOK:
+        result = lsm_offhook(lcb, &(data->offhook));
+#ifdef TEST
+        test_dial_calls(line, call_id, 500, "10011234");
+#endif
+        break;
+
+    case CC_STATE_DIALING:
+        result = lsm_dialing(lcb, &(data->dialing));
+        break;
+
+    case CC_STATE_DIALING_COMPLETED:
+        result = lsm_dialing_completed(lcb, &(data->dialing_completed));
+        break;
+
+    case CC_STATE_CALL_SENT:
+        result = lsm_call_sent(lcb, &(data->call_sent));
+        break;
+
+    case CC_STATE_FAR_END_PROCEEDING:
+        result = lsm_far_end_proceeding(lcb, &(data->far_end_proceeding));
+        break;
+
+    case CC_STATE_FAR_END_ALERTING:
+        result = lsm_far_end_alerting(lcb, &(data->far_end_alerting));
+        break;
+
+    case CC_STATE_CALL_RECEIVED:
+        result = lsm_call_received(lcb, &(data->call_received));
+        break;
+
+    case CC_STATE_ALERTING:
+        result = lsm_alerting(lcb, &(data->alerting));
+        break;
+
+    case CC_STATE_ANSWERED:
+        result = lsm_answered(lcb, &(data->answered));
+        break;
+
+    case CC_STATE_CONNECTED:
+        result = lsm_connected(lcb, &(data->connected));
+#ifdef TEST
+        test_disc_call(line, call_id);
+        test_line_offhook(line, cc_get_new_call_id());
+#endif
+        break;
+
+    case CC_STATE_HOLD:
+        result = lsm_hold(lcb, &(data->hold));
+        break;
+
+    case CC_STATE_HOLD_REVERT:
+        result = lsm_hold_reversion(lcb);
+        break;
+
+    case CC_STATE_RESUME:
+        result = lsm_resume(lcb, &(data->resume));
+        break;
+
+    case CC_STATE_ONHOOK:
+        result = lsm_onhook(lcb, &(data->onhook));
+        break;
+
+    case CC_STATE_CALL_FAILED:
+        result = lsm_call_failed(lcb, &(data->call_failed));
+        break;
+
+    default:
+        break;
+    }
+
+    if (result == CC_RC_ERROR) {
+        LSM_DEBUG(get_debug_string(LSM_DBG_CC_ERROR), call_id, line, fname,
+                  state, data);
+    }
+
+    return;
+}
+
+static cc_rcs_t
+lsm_media (lsm_lcb_t *lcb, callid_t call_id, line_t line)
+{
+    fsmdef_dcb_t *dcb;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        return (CC_RC_ERROR);
+    }
+
+    if (!dcb->spoof_ringout_requested) {
+        lsm_update_media(lcb, "MEDIA");
+        vcmEnableSidetone(YES);
+    } else if (!dcb->spoof_ringout_applied &&
+               (lcb->state == LSM_S_CONNECTED)) {
+        cc_state_data_far_end_alerting_t alerting_data;
+
+        alerting_data.caller_id = dcb->caller_id;
+        (void) lsm_far_end_alerting(lcb, &alerting_data);
+        dcb->spoof_ringout_applied = TRUE;
+    }
+
+    return (CC_RC_SUCCESS);
+}
+
+/*
+ *  Function: lsm_stop_media
+ *
+ *  Parameters:
+ *     lcb     - pointer to lsm_lcb_t,
+ *     call_id - gsm call id for the call in used.
+ *     line    - line_t for the line number (dn line).
+ *     data    - action data.
+ *
+ *  Description:
+ *     The function simply stops media (close Rx and Tx) and set the
+ *  proper ringer.
+ *
+ *  Returns:   None.
+ */
+static void
+lsm_stop_media (lsm_lcb_t *lcb, callid_t call_id, line_t line,
+                cc_action_data_t *data)
+{
+    static const char fname[] = "lsm_stop_media";
+    fsmdef_dcb_t *dcb;
+    fsmdef_media_t *media;
+
+    dcb = lcb->dcb;
+    if (dcb == NULL) {
+        LSM_DEBUG(get_debug_string(DEBUG_INPUT_NULL), fname);
+        return;
+    }
+
+    /* hard close receive and transmit channels */
+    if ((data == NULL) ||
+        (data->stop_media.media_refid == CC_NO_MEDIA_REF_ID)) {
+        /* no data provided or no specific ref ID, defaul to all entries  */
+        lsm_close_rx(lcb, FALSE, NULL);
+        lsm_close_tx(lcb, FALSE, NULL);
+    } else {
+        /* look up the media entry for the given reference ID */
+        media = gsmsdp_find_media_by_refid(dcb,
+                                           data->stop_media.media_refid);
+        if (media != NULL) {
+            lsm_close_rx(lcb, FALSE, media);
+            lsm_close_tx(lcb, FALSE, media);
+        } else {
+            /* no entry found */
+            LSM_DEBUG(DEB_L_C_F_PREFIX"no media with reference ID %d found\n",
+                      DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname),
+                                         data->stop_media.media_refid);
+            return;
+        }
+    }
+    lsm_set_ringer(lcb, call_id, line, YES);
+}
+
+
+
+/*
+ * lsm_add_remote_stream
+ *
+ * Description:
+ *    The function adds a remote stream to the media subsystem
+ *
+ * Parameters:
+ *   [in]  line - line
+ *   [in]  call_id - GSM call ID
+ *   [in]  media - media line to add as remote stream
+ *   [out] pc_stream_id
+ * Returns: None
+ */
+void lsm_add_remote_stream (line_t line, callid_t call_id, fsmdef_media_t *media, int *pc_stream_id)
+{
+    static const char fname[] = "lsm_add_remote_stream";
+    fsmdef_dcb_t   *dcb;
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        dcb = lcb->dcb;
+        if (dcb == NULL) {
+            LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname);
+            return;
+        }
+
+        vcmCreateRemoteStream(media->cap_index, dcb->peerconnection,
+                pc_stream_id);
+
+    }
+}
+
+/*
+ * lsm_data_channel_negotiated
+ *
+ * Description:
+ *    The function informs the API of a negotiated data channel m= line
+ *
+ * Parameters:
+ *   [in]  line - line
+ *   [in]  call_id - GSM call ID
+ *   [in]  media - media line to add as remote stream
+ *   [out] pc_stream_id
+ * Returns: None
+ */
+void lsm_data_channel_negotiated (line_t line, callid_t call_id, fsmdef_media_t *media, int *pc_stream_id)
+{
+    static const char fname[] = "lsm_data_channel_negotiated";
+    fsmdef_dcb_t   *dcb;
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb) {
+        dcb = lcb->dcb;
+        if (dcb == NULL) {
+            LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname);
+            return;
+        }
+
+        /*
+         * have access to media->streams, media->protocol, media->sctp_port
+         * vcmSetDataChannelParameters may need renaming TODO: jesup
+         */
+
+        vcmSetDataChannelParameters(dcb->peerconnection, media->streams, media->sctp_port, media->protocol);
+
+    }
+}
+
+/**
+ *
+ * Peform non call related action
+ *
+ * @param line_t     line
+ * @param callid_t      gsm_id
+ * @param action        type of action
+ * @param cc_action_data_t        line
+ *
+ * @return  true if the action has been peformed, else false
+ *
+ * @pre     (action == CC_ACTION_MWI_LAMP_ONLY || CC_ACTION_SET_LINE_RINGER ||
+                       CC_ACTION_PLAY_BLF_ALERTING_TONE)
+ */
+static boolean
+cc_call_non_call_action (callid_t call_id, line_t line,
+                         cc_actions_t action, cc_action_data_t *data)
+{
+    /* Certain requests are device based and does not contain any
+     * line number and call_id associated with it. So handle thoese
+     * requests here
+     */
+    switch (action) {
+
+    case CC_ACTION_MWI_LAMP_ONLY:
+        if (data != NULL) {
+            ui_change_mwi_lamp(data->mwi.on);
+            return(TRUE);
+        }
+        break;
+
+    case CC_ACTION_SET_LINE_RINGER:
+
+        if (data != NULL) {
+            return(TRUE);
+        }
+        break;
+
+    case CC_ACTION_PLAY_BLF_ALERTING_TONE:
+        lsm_play_tone(CC_FEATURE_BLF_ALERT_TONE);
+        return TRUE;
+
+    default:
+        break;
+    }
+
+    return(FALSE);
+}
+
+/*
+ * LSM API supports various actions such as play tone, stop tone,
+ * direct media operation etc.
+ *
+ * @param[in] call_id   GSM call ID of an active call.
+ * @param[in] line      line number of the line_t type.
+ * @param[in] action    cc_actions_t for the desired action.
+ * @param[in] data      cc_action_data_t data or parameters that may be
+ *                      required for certain action.
+ *
+ * @return              cc_rcs_t status.
+ *
+ * @pre                 line not_eqs CC_NO_LINE
+ * @pre                 ((action equals CC_ACTION_PLAY_TONE) or
+ *                       (action equals CC_ACTION_STOP_TONE) or
+ *                       (action equals CC_ACTION_DIAL_MODE) or
+ *                       (action equals CC_ACTION_MWI) or
+ *                       (action equals CC_ACTION_OPEN_RCV) or
+ *                       (action equals CC_ACTION_UPDATE_UI) or
+ *                       (action equals CC_ACTION_RINGER))
+ */
+cc_rcs_t
+cc_call_action (callid_t call_id, line_t line, cc_actions_t action,
+               cc_action_data_t *data)
+{
+    static const char fname[] = "cc_call_action";
+    cc_rcs_t   result = CC_RC_SUCCESS;
+    lsm_lcb_t *lcb;
+    fsmdef_dcb_t *dcb;
+    fsmdef_media_t *media;
+
+    LSM_DEBUG(get_debug_string(LSM_DBG_ENTRY), call_id, line,
+              cc_action_name(action));
+
+    /* perform non call related actions. lcb is not required
+     * for these actions
+     */
+    if (cc_call_non_call_action(call_id, line, action, data)) {
+        return (result);
+    }
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+
+    if ((lcb == NULL) && (action != CC_ACTION_MWI)) {
+        LSM_DEBUG(get_debug_string(DEBUG_INPUT_NULL), fname);
+        return (CC_RC_ERROR);
+    }
+
+    switch (action) {
+    case CC_ACTION_PLAY_TONE:
+        if (data != NULL) {
+            result = lsm_start_tone(lcb, &(data->tone));
+        } else {
+            result = CC_RC_ERROR;
+        }
+        break;
+
+    case CC_ACTION_STOP_TONE:
+        if (data != NULL) {
+            result = lsm_stop_tone(lcb, &(data->tone));
+        } else {
+            result = CC_RC_ERROR;
+        }
+        break;
+
+    case CC_ACTION_SPEAKER:
+        break;
+
+    case CC_ACTION_DIAL_MODE:
+        if (data != NULL) {
+            result = lsm_dial_mode(lcb, &(data->dial_mode));
+        } else {
+            result = CC_RC_ERROR;
+        }
+        break;
+
+    case CC_ACTION_MWI:
+        if (data != NULL) {
+            result = lsm_mwi(NULL, call_id, line, &(data->mwi));
+        } else {
+            result = CC_RC_ERROR;
+        }
+        break;
+
+    case CC_ACTION_OPEN_RCV:
+        if (data != NULL) {
+            result = lsm_open_rx(lcb, &(data->open_rcv), NULL);
+        } else {
+            result = CC_RC_ERROR;
+        }
+        break;
+
+    case CC_ACTION_UPDATE_UI:
+        if (data != NULL) {
+            result = lsm_update_ui(lcb, &(data->update_ui));
+        } else {
+            result = CC_RC_ERROR;
+        }
+        break;
+
+    case CC_ACTION_MEDIA:
+        result = lsm_media(lcb, call_id, line);
+        break;
+
+    case CC_ACTION_RINGER:
+        if (data != NULL) {
+            lsm_ringer(lcb, &(data->ringer));
+        }
+        break;
+
+    case CC_ACTION_STOP_MEDIA:
+        lsm_stop_media(lcb, call_id, line, data);
+        break;
+
+    case CC_ACTION_START_RCV:
+        /* start receiving */
+        dcb = lcb->dcb;
+        if (dcb == NULL) {
+            /* No call ID */
+            result = CC_RC_ERROR;
+            break;
+        }
+
+        GSMSDP_FOR_ALL_MEDIA(media, dcb) {
+            if (!GSMSDP_MEDIA_ENABLED(media)) {
+                /* this entry is not active */
+                continue;
+            }
+
+            /* only support starting all receive channels for now */
+            lsm_rx_start(lcb, fname, media);
+        }
+        break;
+
+    case CC_ACTION_ANSWER_PENDING:
+        FSM_SET_FLAGS(lcb->flags, LSM_FLAGS_ANSWER_PENDING);
+        break;
+
+    default:
+        break;
+    }
+
+
+    if (result == CC_RC_ERROR) {
+        LSM_DEBUG(get_debug_string(LSM_DBG_CC_ERROR), call_id, line, fname,
+                  action, data);
+    }
+
+    return (result);
+}
+
+
+void
+lsm_ui_display_notify (const char *notify_str, unsigned long timeout)
+{
+    /*
+     * add 0 as (default) priority; it is don't care in legacy mode
+     */
+    ui_set_notification(CC_NO_LINE, CC_NO_CALL_ID,
+                        (char *)notify_str, (int)timeout, FALSE,
+                        DEF_NOTIFY_PRI);
+}
+
+void
+lsm_ui_display_status (const char *status_str, line_t line, callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+
+    if (call_id == CC_NO_CALL_ID) {
+        /* Invalid call id */
+        return;
+    }
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb == NULL) {
+        return;
+    }
+
+    ui_set_call_status((char *) status_str, line, lcb->ui_id);
+}
+
+/**
+ * This function will display notification status line.
+ *
+ * @param[in] str_index - index into phrase dictionary
+ *
+ * @return none
+ */
+void lsm_ui_display_notify_str_index (int str_index)
+{
+    char tmp_str[STATUS_LINE_MAX_LEN];
+
+    if ((platGetPhraseText(str_index,
+                                 (char *)tmp_str,
+                                 (STATUS_LINE_MAX_LEN - 1))) == CPR_SUCCESS) {
+        lsm_ui_display_notify(tmp_str, NO_FREE_LINES_TIMEOUT);
+    }
+}
+
+/*
+ *  Function: lsm_parse_displaystr
+ *
+ *  Parameters:string to be parsed
+ *
+ *  Description:Wrapper function for parsing string to be displayed
+ *
+ *  Returns: Pointer to parsed number
+ *
+ */
+string_t
+lsm_parse_displaystr (string_t displaystr)
+{
+    return (sippmh_parse_displaystr(displaystr));
+}
+
+void
+lsm_speaker_mode (short mode)
+{
+    ui_set_speaker_mode((boolean)mode);
+}
+
+/*
+ *  Function:lsm_update_active_tone
+ *
+ *  Parameters:
+ *          tone       - tone type
+ *          call_id    - call identifier
+ *
+ *  Description: Update dcb->active_tone if starting infinite duration tone.
+ *
+ *  Returns:none
+ *
+ */
+void
+lsm_update_active_tone (vcm_tones_t tone, callid_t call_id)
+{
+    static const char fname[] = "lsm_update_active_tone";
+    fsmdef_dcb_t *dcb;
+
+    /* if tone is any of following then set active_tone in dcb b/c these
+     * tones have infinite duration and need to be stopped. Other tones
+     * only play for a finite/short duration so no need to stop them as
+     * they will stop automatically.
+     */
+    switch (tone) {
+    /* for all tones with infinite playing duration */
+    case VCM_INSIDE_DIAL_TONE:
+    case VCM_LINE_BUSY_TONE:
+    case VCM_ALERTING_TONE:
+    case VCM_STUTTER_TONE:
+    case VCM_REORDER_TONE:
+    case VCM_OUTSIDE_DIAL_TONE:
+    case VCM_PERMANENT_SIGNAL_TONE:
+    case VCM_RECORDERWARNING_TONE:
+    case VCM_MONITORWARNING_TONE:
+        dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+        if (dcb == NULL) {
+            /* Possibibly the ui_id was passed in and the dcb is no longer existed.
+             * Try to retrieve the corresponding dcb.
+             */
+            dcb = fsmdef_get_dcb_by_call_id(lsm_get_callid_from_ui_id(call_id));
+        }
+
+        if (dcb != NULL) {
+            /* Ideally a call should not make a infinite tone start request
+             * (without making a stop request) while there is already one playing.
+             * However, DSP will start playing the new request tone by overriding
+             * the current one. Technically its okay. So, just printing a log msg.
+             */
+            if (dcb->active_tone != VCM_NO_TONE) {
+                LSM_DEBUG(DEB_L_C_F_PREFIX"Active Tone current = %d  new = %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, call_id, fname),
+                                                 dcb->active_tone, tone);
+            }
+            dcb->active_tone = tone;
+        }
+        break;
+
+    default:
+        /* do nothing */
+        break;
+    }
+}
+
+/*
+ *  Function: lsm_is_tx_channel_opened
+ *
+ *  Parameters: call_id
+ *
+ *  Description: check to see tx channel is openned
+ *
+ *  Returns:    TRUE or FALSE
+ *
+ */
+boolean
+lsm_is_tx_channel_opened(callid_t call_id)
+{
+    fsmdef_dcb_t *dcb_p = fsmdef_get_dcb_by_call_id(call_id);
+    fsmdef_media_t *media = NULL;
+
+    if (dcb_p == NULL) {
+        return (FALSE);
+    }
+
+    /*
+     * search the all entries that has a valid media and matches
+     * SDP_MEDIA_AUDIO type.
+     */
+    GSMSDP_FOR_ALL_MEDIA(media, dcb_p) {
+        if (media->type == SDP_MEDIA_AUDIO) {
+            /* found a match */
+            if (media->xmit_chan)
+               return (TRUE);
+        }
+    }
+    return (FALSE);
+}
+
+/*
+ *  Function:lsm_update_monrec_tone_action
+ *
+ *  Parameters:
+ *          tone       - tone type
+ *          call_id    - call identifier
+ *
+ *  Description: Update dcb->monrec_tone_action.
+ *
+ *  Returns:none
+ *
+ */
+void
+lsm_update_monrec_tone_action (vcm_tones_t tone, callid_t call_id, uint16_t direction)
+{
+    static const char fname[] = "lsm_update_monrec_tone_action";
+    fsmdef_dcb_t *dcb;
+    boolean tx_opened = lsm_is_tx_channel_opened(call_id);
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+    if (dcb != NULL) {
+            switch(tone) {
+                case VCM_MONITORWARNING_TONE:
+                    switch (dcb->monrec_tone_action) {
+                        case FSMDEF_MRTONE_NO_ACTION:
+                            if (!tx_opened) {
+                                dcb->monrec_tone_action = FSMDEF_MRTONE_RESUME_MONITOR_TONE;
+                            } else {
+                                dcb->monrec_tone_action = FSMDEF_MRTONE_PLAYED_MONITOR_TONE;
+                            }
+                            break;
+
+                        case FSMDEF_MRTONE_PLAYED_RECORDER_TONE:
+                            dcb->monrec_tone_action = FSMDEF_MRTONE_PLAYED_BOTH_TONES;
+                            break;
+
+                         case FSMDEF_MRTONE_RESUME_RECORDER_TONE:
+                            dcb->monrec_tone_action = FSMDEF_MRTONE_RESUME_BOTH_TONES;
+                            break;
+
+                        case FSMDEF_MRTONE_PLAYED_MONITOR_TONE:
+                        case FSMDEF_MRTONE_PLAYED_BOTH_TONES:
+                        case FSMDEF_MRTONE_RESUME_MONITOR_TONE:
+                        case FSMDEF_MRTONE_RESUME_BOTH_TONES:
+                        default:
+                            DEF_DEBUG(DEB_F_PREFIX"Invalid action request... tone:%d monrec_tone_action:%d \n",
+                                      DEB_F_PREFIX_ARGS("RCC", fname), tone, dcb->monrec_tone_action);
+                            break;
+                    }
+                    dcb->monitor_tone_direction = direction;
+                    break;
+
+                case VCM_RECORDERWARNING_TONE:
+                    switch (dcb->monrec_tone_action) {
+                        case FSMDEF_MRTONE_NO_ACTION:
+                            if (!tx_opened) {
+                                dcb->monrec_tone_action = FSMDEF_MRTONE_RESUME_RECORDER_TONE;
+                            } else {
+                                dcb->monrec_tone_action = FSMDEF_MRTONE_PLAYED_RECORDER_TONE;
+                            }
+                            break;
+
+                        case FSMDEF_MRTONE_PLAYED_MONITOR_TONE:
+                            dcb->monrec_tone_action = FSMDEF_MRTONE_PLAYED_BOTH_TONES;
+                            break;
+
+                        case FSMDEF_MRTONE_RESUME_MONITOR_TONE:
+                            dcb->monrec_tone_action = FSMDEF_MRTONE_RESUME_BOTH_TONES;
+                            break;
+
+                        case FSMDEF_MRTONE_PLAYED_RECORDER_TONE:
+                        case FSMDEF_MRTONE_PLAYED_BOTH_TONES:
+                        case FSMDEF_MRTONE_RESUME_RECORDER_TONE:
+                        case FSMDEF_MRTONE_RESUME_BOTH_TONES:
+                        default:
+                            DEF_DEBUG(DEB_F_PREFIX"Invalid action request... tone:%d monrec_tone_action:%d \n",
+                                      DEB_F_PREFIX_ARGS("RCC", fname), tone, dcb->monrec_tone_action);
+                            break;
+                    }
+                    dcb->recorder_tone_direction = direction;
+                    break;
+
+                default:
+                    break;
+        } /* end of switch */
+
+        LSM_DEBUG(DEB_L_C_F_PREFIX"Start request for tone: %d. Set monrec_tone_action: %d\n",
+                  DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, call_id, fname),
+                             tone, dcb->monrec_tone_action);
+
+    } /* end of if */
+}
+
+/*
+ *  Function:lsm_downgrade_monrec_tone_action
+ *
+ *  Parameters:
+ *          tone       - tone type
+ *          call_id    - call identifier
+ *
+ *  Description: Update dcb->monrec_tone_action.
+ *
+ *  Returns:none
+ *
+ */
+void
+lsm_downgrade_monrec_tone_action (vcm_tones_t tone, callid_t call_id)
+{
+    static const char fname[] = "lsm_downgrade_monrec_tone_action";
+    fsmdef_dcb_t *dcb;
+
+    dcb = fsmdef_get_dcb_by_call_id(call_id);
+
+    /* Need to downgrade the monrec_tone_action */
+
+    if (dcb != NULL) {
+        switch (tone){
+            case VCM_MONITORWARNING_TONE:
+                switch (dcb->monrec_tone_action) {
+                    case FSMDEF_MRTONE_PLAYED_MONITOR_TONE:
+                    case FSMDEF_MRTONE_RESUME_MONITOR_TONE:
+                        dcb->monrec_tone_action = FSMDEF_MRTONE_NO_ACTION;
+                        break;
+
+                    case FSMDEF_MRTONE_RESUME_BOTH_TONES:
+                        dcb->monrec_tone_action = FSMDEF_MRTONE_RESUME_RECORDER_TONE;
+                        break;
+
+                    case FSMDEF_MRTONE_PLAYED_BOTH_TONES:
+                        dcb->monrec_tone_action = FSMDEF_MRTONE_PLAYED_RECORDER_TONE;
+                        break;
+
+                    case FSMDEF_MRTONE_NO_ACTION:
+                    case FSMDEF_MRTONE_PLAYED_RECORDER_TONE:
+                    case FSMDEF_MRTONE_RESUME_RECORDER_TONE:
+                    default:
+                        DEF_DEBUG(DEB_F_PREFIX"Invalid action request... tone:%d monrec_tone_action:%d \n",
+                                  DEB_F_PREFIX_ARGS("RCC", fname), tone, dcb->monrec_tone_action);
+                        break;
+                }
+                dcb->monitor_tone_direction = VCM_PLAY_TONE_TO_EAR;
+                break;
+
+            case VCM_RECORDERWARNING_TONE:
+                switch (dcb->monrec_tone_action) {
+                    case FSMDEF_MRTONE_PLAYED_RECORDER_TONE:
+                    case FSMDEF_MRTONE_RESUME_RECORDER_TONE:
+                        dcb->monrec_tone_action = FSMDEF_MRTONE_NO_ACTION;
+                        break;
+
+                    case FSMDEF_MRTONE_RESUME_BOTH_TONES:
+                        dcb->monrec_tone_action = FSMDEF_MRTONE_RESUME_MONITOR_TONE;
+                        break;
+
+                    case FSMDEF_MRTONE_PLAYED_BOTH_TONES:
+                        dcb->monrec_tone_action = FSMDEF_MRTONE_PLAYED_MONITOR_TONE;
+                        break;
+
+                    case FSMDEF_MRTONE_NO_ACTION:
+                    case FSMDEF_MRTONE_PLAYED_MONITOR_TONE:
+                    case FSMDEF_MRTONE_RESUME_MONITOR_TONE:
+                    default:
+                        DEF_DEBUG(DEB_F_PREFIX"Invalid action request... tone:%d monrec_tone_action:%d \n",
+                                  DEB_F_PREFIX_ARGS("RCC", fname), tone, dcb->monrec_tone_action);
+                        break;
+                }
+                dcb->recorder_tone_direction = VCM_PLAY_TONE_TO_EAR;
+                break;
+
+            default:
+                break;
+        } /* end of switch */
+
+        LSM_DEBUG(DEB_L_C_F_PREFIX"Stop request for tone: %d Downgrade monrec_tone_action: %d \n",
+                  DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, call_id, fname),
+                             tone, dcb->monrec_tone_action);
+    } /* end of if */
+}
+
+/*
+ *  Function: lsm_set_hold_ringback_status
+ *
+ *  Parameters:
+ *         callid_t - callid of the lcb
+ *         ringback_status - status of call hold ringback
+ *
+ *  Description: Function used to set the ringback status
+ *
+ *  Returns:None
+ *
+ */
+void
+lsm_set_hold_ringback_status(callid_t call_id, boolean ringback_status)
+{
+    lsm_lcb_t      *lcb;
+
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if (lcb->call_id == call_id) {
+            LSM_DEBUG(DEB_F_PREFIX"Setting ringback to %d for lcb %d\n",
+                      DEB_F_PREFIX_ARGS(LSM, "lsm_set_hold_ringback_status"),  ringback_status, call_id);
+            lcb->enable_ringback = ringback_status;
+            break;
+        }
+    }
+}
+
+void lsm_play_tone (cc_features_t feature_id)
+{
+    int play_tone;
+
+    switch (feature_id) {
+    case CC_FEATURE_BLF_ALERT_TONE:
+        if (lsm_find_state(LSM_S_RINGIN) > CC_NO_CALL_ID) {
+            // No tone if we have calls in ringing state
+            return;
+        }
+
+        if (!lsm_callwaiting()) {
+            config_get_value(CFGID_BLF_ALERT_TONE_IDLE, &play_tone, sizeof(play_tone));
+            if (play_tone == 0) {
+                return;
+            }
+            lsm_util_tone_start_with_speaker_as_backup(VCM_CALL_WAITING_TONE, VCM_ALERT_INFO_OFF,
+                                                  CC_NO_CALL_ID, CC_NO_GROUP_ID,
+                                                  CC_NO_MEDIA_REF_ID, VCM_PLAY_TONE_TO_EAR);
+        } else {
+            config_get_value(CFGID_BLF_ALERT_TONE_BUSY, &play_tone, sizeof(play_tone));
+            if (play_tone == 0) {
+                return;
+            }
+            lsm_util_tone_start_with_speaker_as_backup(VCM_CALL_WAITING_TONE, VCM_ALERT_INFO_OFF,
+                                                  CC_NO_CALL_ID, CC_NO_GROUP_ID,
+                                                  CC_NO_MEDIA_REF_ID, VCM_PLAY_TONE_TO_EAR);
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+/*
+ * lsm_update_inalert_status
+ *
+ * Description:
+ *
+ * TNP specific implementation of status line update for inalert state.
+ *
+ * Parameters:
+ *
+ * line_t line - Line facility of the call
+ * callid_t call_id - Call id of call whose state is being reported
+ * cc_state_data_alerting_t * data - alerting callinfo.
+ * boolean notify - whether the msg be displayed at notify level.
+ *
+ * Returns: None
+ */
+static void
+lsm_update_inalert_status (line_t line, callid_t call_id,
+                           cc_state_data_alerting_t * data,
+                           boolean notify)
+{
+    static const char fname[] = "lsm_update_inalert_status";
+    char disp_str[LSM_DISPLAY_STR_LEN];
+
+    // get localized tag index for From and append one space character
+    sstrncpy(disp_str, platform_get_phrase_index_str(UI_FROM),
+             sizeof(disp_str));
+
+    LSM_DEBUG(DEB_L_C_F_PREFIX"+++ calling number = %s\n",
+                         DEB_L_C_F_PREFIX_ARGS(LSM, line, call_id, fname),
+              data->caller_id.calling_number);
+
+    // append calling number if present or localized tag for Unknown Number
+    // otherwise
+    if ((data->caller_id.calling_number) &&
+        (data->caller_id.calling_number[0] != '\0') &&
+        data->caller_id.display_calling_number) {
+
+        sstrncat(disp_str, data->caller_id.calling_number,
+                sizeof(disp_str) - strlen(disp_str));
+    } else {
+        sstrncat(disp_str, platform_get_phrase_index_str(UI_UNKNOWN),
+                sizeof(disp_str) - strlen(disp_str));
+    }
+
+    // we display (via notification) the "From ..." info for 10 seconds.
+    // Note that this will remain displayed for 10 sec even if the user
+    // answers the call or switches to another call. This happens because
+    // notification has higher priority than call status (e.g. connected).
+    // This is done to have parity with SCCP phone behavior.
+    if (notify == TRUE) {
+        ui_set_notification(line, call_id,
+                            (char *)disp_str, (unsigned long)CALL_ALERT_TIMEOUT,
+                            FALSE, FROM_NOTIFY_PRI);
+    }
+    // After the notification we wish to set the call status to From XXXX. Same as SCCP phone behavior
+    lsm_ui_display_status((char *)disp_str, line, call_id);
+
+    return;
+}
+
+
+
+/*
+ * lsm_set_cfwd_all_nonccm
+ * This function calls JNI API to set the CFA state and DN in non-ccm mode.
+ *
+ * @param[in] line - line on which to set the CFA
+ * @param[in] callfwd_dialstring: CFA DN (will be stored in flash)
+ *
+ * @return: None
+ */
+void
+lsm_set_cfwd_all_nonccm (line_t line, char *callfwd_dialstring)
+{
+    // call Java API
+    ui_cfwd_status(line, TRUE, callfwd_dialstring, TRUE);
+}
+
+/*
+ * lsm_set_cfwd_all_ccm
+ *
+ * Description:
+ * This function calls JNI API to set the CFA state and DN in ccm mode.
+ *
+ * Parameters:
+ * char * callfwd_dialstring: CFA DN (will NOT be stored in flash)
+ *
+ * Returns: None
+ */
+void
+lsm_set_cfwd_all_ccm (line_t line, char *callfwd_dialstring)
+{
+    // set locally maintained variable
+    cfwdall_state_in_ccm_mode[line] = TRUE;
+
+    // call Java API
+    ui_cfwd_status((line_t)line, TRUE, callfwd_dialstring, FALSE);
+}
+
+/*
+ * lsm_clear_cfwd_all_nonccm
+ * This function calls JNI API to clear the CFA state and DN in non-ccm mode.
+ *
+ * @param[in] line - line on which to clear the CFA
+ *
+ * @return: None
+ */
+void
+lsm_clear_cfwd_all_nonccm (line_t line)
+{
+    // call Java API
+    ui_cfwd_status(line, FALSE, "", TRUE);
+}
+
+
+/*
+ * lsm_clear_cfwd_all_ccm
+ *
+ * Description:
+ * This function calls JNI API to clear the CFA state and DN in ccm mode.
+ *
+ * Parameters: None
+ *
+ * Returns: None
+ */
+void
+lsm_clear_cfwd_all_ccm (line_t line)
+{
+    // clear locally maintained variable
+    cfwdall_state_in_ccm_mode[line] = FALSE;
+
+    // call Java API
+    ui_cfwd_status((line_t)line, FALSE, "", FALSE);
+}
+
+/*
+ * lsm_check_cfwd_all_nonccm
+ *
+ * Description:
+ * This function returns the CFA state in non-ccm mode.
+ *
+ * @param[in] line - line on which to check the CFA
+ *
+ * @return: TRUE (if CFA set) or FALSE (if CFA clear)
+ */
+int
+lsm_check_cfwd_all_nonccm (line_t line)
+{
+    char cfg_cfwd_url[MAX_URL_LENGTH];
+
+    cfg_cfwd_url[0] = '\0';
+
+    // get the callfwdall url value from the config/flash table
+    config_get_string(CFGID_LINE_CFWDALL+line-1, cfg_cfwd_url, MAX_URL_LENGTH);
+
+    // return appropriate value: TRUE if non-NULL and FALSE otherwise
+    if (cfg_cfwd_url[0]) {
+        return ((int) TRUE);
+    } else {
+        return ((int) FALSE);
+    }
+}
+
+/*
+ * lsm_check_cfwd_all_ccm
+ *
+ * Description:
+ * This function returns the CFA state in ccm mode.
+ *
+ * Parameters: None
+ *
+ * Returns: TRUE or FALSE
+ */
+int
+lsm_check_cfwd_all_ccm (line_t line)
+{
+    return ((int) cfwdall_state_in_ccm_mode[line]);
+}
+
+/*
+ * lsm_is_phone_forwarded
+ *
+ * Description:
+ * This function is called from SIP stack to check if received INVITE
+ * should be responded with 302 or not. In the CCM mode this function
+ * will always return NULL... that is process the INVITE as normal and
+ * DO NOT 302 it. In the non-CCM mode, if the cfwdall_url is non-NULL
+ * then it will form a proper string to use in 302 response; otherwise
+ * a NULL will be returned and the INVITE will be processed as normal.
+ * NOTE: most all code is reused from the legacy phone code.
+ *
+ * Parameters: line - line for which to check the CFA status
+ *
+ * Returns: NULL if forwarding is not set;
+ *          string to use in 302 response if forwarding is set (non-CCM only)
+ */
+char *
+lsm_is_phone_forwarded (line_t line)
+{
+    static const char fname[] = "lsm_is_phone_forwarded";
+    char     proxy_ipaddr_str[MAX_IPADDR_STR_LEN];
+    int      port_number = 5060; // use this value only if none found
+    char    *domain = NULL;
+    char    *port = NULL;
+    cpr_ip_addr_t proxy_ipaddr;
+
+
+    LSM_DEBUG(DEB_F_PREFIX"called\n", DEB_F_PREFIX_ARGS(LSM, fname));
+
+    // check if running in CCM mode. if so, return NULL that is cfwdall
+    // not applicable
+    if (sip_regmgr_get_cc_mode(TEL_CCB_START) == REG_MODE_CCM) {
+        return (NULL);
+    }
+    // get stored callfwdall url value from the config/flash table
+
+    config_get_string(CFGID_LINE_CFWDALL+line-1, cfwdall_url, sizeof(cfwdall_url));
+
+    if (cfwdall_url[0]) {
+        // find domain and port
+        domain = strchr(cfwdall_url, '@');
+        if (!domain) {
+            (void) sipTransportGetServerAddress(&proxy_ipaddr,
+                                        1, TEL_CCB_START);
+            if (proxy_ipaddr.type != CPR_IP_ADDR_INVALID) {
+                ipaddr2dotted(proxy_ipaddr_str, &proxy_ipaddr);
+                port_number = sipTransportGetServerPort(1, TEL_CCB_START);
+            }
+        } else {
+            port = strchr(domain + 1, ':');
+        }
+
+        // handle 3 cases
+        if (domain == NULL) {
+            /* case (1): no domain or port present
+             * We have proxy's dotted ip address format. So, not FQDN check.
+             * Append domain/ip-addr and port.
+             */
+            snprintf(cfwdall_url + strlen(cfwdall_url),
+                     MAX_URL_LENGTH - strlen(cfwdall_url),
+                     "@%s:%d", proxy_ipaddr_str, port_number);
+        } else if (port == NULL) {
+            /* case (2): domain present but no port
+             * Check if the domain is dotted IP address and add port
+             * only if dotted IP address is used
+             */
+            if (!str2ip((const char *) domain + 1, &proxy_ipaddr)) {
+                port_number = sipTransportGetServerPort(1, TEL_CCB_START);
+                snprintf(cfwdall_url + strlen(cfwdall_url),
+                         MAX_URL_LENGTH - strlen(cfwdall_url),
+                         ":%d", port_number);
+            }
+        } else {
+            /* case (3): both domain and port present
+             * Both domain and port exists, but strip the  port if the
+             * domain is FQDN
+             */
+            memcpy(proxy_ipaddr_str, domain + 1, (port - domain - 1));
+            *(proxy_ipaddr_str + (port - domain - 1)) = '\0';
+            if (str2ip((const char *) proxy_ipaddr_str, &proxy_ipaddr) != 0) {
+                *port = '\0';
+            }
+        }
+        return ((char *)cfwdall_url);
+    } else {
+        return ((char *)NULL);
+    }
+}
+
+/*
+ * lsm_get_callid_from_ui_id()
+ *
+ * Description:
+ *    The function gets the UI id from LSM's LCB for a given GSM call ID.
+ *
+ * Parameters:
+ *    ui_id   - UI ID.
+ *
+ * Returns: callid_t
+ */
+callid_t
+lsm_get_callid_from_ui_id (callid_t uid)
+{
+    lsm_lcb_t *lcb;
+    FSM_FOR_ALL_CBS(lcb, lsm_lcbs, LSM_MAX_LCBS) {
+        if (lcb->ui_id == uid) {
+            return lcb->call_id;
+        }
+    }
+    return (CC_NO_CALL_ID);
+}
+
+/*
+ * lsm_get_ui_id
+ *
+ * Description:
+ *    The function gets the UI id from LSM's LCB for a given GSM call ID.
+ *
+ * Parameters:
+ *    call_id - GSM call ID
+ *    ui_id   - UI ID.
+ *
+ * Returns: None
+ */
+callid_t
+lsm_get_ui_id (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        return (lcb->ui_id);
+    }
+    return (CC_NO_CALL_ID);
+}
+
+/*
+ * lsm_get_ms_ui_id
+ *
+ * Description:
+ *    The function gets the UI id from LSM's LCB for a given GSM call ID. During
+ * certain features like barge ui_id is set to CC_NO_CALL_ID.
+ *
+ * Parameters:
+ *    call_id - GSM call ID
+ *    ui_id   - UI ID.
+ *
+ * Returns: None
+ */
+cc_call_handle_t
+lsm_get_ms_ui_call_handle (line_t line, callid_t call_id, callid_t ui_id)
+{
+    callid_t lsm_ui_id;
+
+    if (ui_id != CC_NO_CALL_ID) {
+        return CREATE_CALL_HANDLE(line, ui_id);
+    }
+
+    /* If ui_id present use that */
+    lsm_ui_id = lsm_get_ui_id(call_id);
+
+    if (lsm_ui_id != CC_NO_CALL_ID) {
+        return CREATE_CALL_HANDLE(line, lsm_ui_id);
+    }
+
+    return CREATE_CALL_HANDLE(line, call_id);
+}
+/*
+ * lsm_set_ui_id
+ *
+ * Description:
+ *    The function sets the UI id to LSM's LCB for a given GSM call ID.
+ *
+ * Parameters:
+ *    call_id - GSM call ID
+ *    ui_id   - UI ID.
+ *
+ * Returns: None
+ */
+void
+lsm_set_ui_id (callid_t call_id, callid_t ui_id)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        lcb->ui_id = ui_id;
+    }
+}
+
+char *
+lsm_get_gdialed_digits (void)
+{
+    return (dp_get_gdialed_digits());
+}
+
+/*
+ * lsm_update_video_avail
+ *
+ * Description:
+ *    The function updates session about the video availability
+ *
+ * Parameters:
+ *    line - line
+ *    call_id - GSM call ID
+ *    dir - video avail dir
+ *
+ * Returns: None
+ */
+void lsm_update_video_avail (line_t line, callid_t call_id, int dir)
+{
+    static const char fname[] = "lsm_update_video_avail";
+    fsmdef_dcb_t   *dcb;
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        dcb = lcb->dcb;
+        if (dcb == NULL) {
+            LSM_ERR_MSG(get_debug_string(DEBUG_INPUT_NULL), fname);
+            return;
+        }
+
+        dir &= ~CC_ATTRIB_CAST;
+
+
+        ui_update_video_avail (line, lcb->ui_id, dir);
+
+        lsm_update_dscp_value(dcb);
+    }
+}
+
+/*
+ * lsm_update_video_offered
+ *
+ * Description:
+ *    The function updates session about the video availability
+ *
+ * Parameters:
+ *    line - line
+ *    call_id - GSM call ID
+ *    dir - video avail dir
+ *
+ * Returns: None
+ */
+void lsm_update_video_offered (line_t line, callid_t call_id, int dir)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+       ui_update_video_offered (line, lcb->ui_id, dir);
+    }
+}
+
+/*
+ * lsm_set_video_mute
+ *
+ * Description:
+ *    The function sets the video mute state for the call
+ *
+ * Parameters:
+ *    line - line
+ *    call_id - This is the UI_ID coming from UI
+ *    mute - mute state
+ *
+ * Returns: None
+ */
+void lsm_set_video_mute (callid_t call_id, int mute)
+{
+    lsm_lcb_t *lcb;
+    callid_t cid = lsm_get_callid_from_ui_id(call_id); // get GSM_ID from UI_ID
+
+    lcb = lsm_get_lcb_by_call_id(cid);
+    if (lcb != NULL) {
+       lcb->vid_mute = mute;
+    }
+}
+
+/*
+ * lsm_get_video_mute
+ *
+ * Description:
+ *    The function gets the video mute state for the call
+ *
+ * Parameters:
+ *    line - line
+ *    call_id - GSM call ID
+ *
+ * Returns: t_video_mute
+ */
+int lsm_get_video_mute (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+       return lcb->vid_mute;
+    }
+    return (-1);
+}
+
+
+/*
+ * lsm_set_video_window
+ *
+ * Description:
+ *    The function sets the video window state for the call
+ *
+ * Parameters:
+ *    call_id - This is the UI_ID coming from UI
+ *    flags - video window flags
+ *    x - video window x coordinate
+ *    y - video window y coordinate
+ *    h - video window height
+ *    w - video window width
+ *
+ * Returns: None
+ */
+void lsm_set_video_window (callid_t call_id, int flags, int x, int y, int h, int w)
+{
+    lsm_lcb_t *lcb;
+    callid_t cid = lsm_get_callid_from_ui_id(call_id); // get GSM_ID from UI_ID
+
+    lcb = lsm_get_lcb_by_call_id(cid);
+    if (lcb != NULL) {
+       lcb->vid_flags = flags;
+       lcb->vid_x = x;
+       lcb->vid_y = y;
+       lcb->vid_h = h;
+       lcb->vid_w = w;
+    }
+}
+
+/*
+ * lsm_get_video_window
+ *
+ * Description:
+ *    The function gets the video window for the call
+ *
+ * Parameters:
+ *    call_id - GSM call ID
+ *    *flags - video window flag
+ *    *x - video window x coordinate
+ *    *y - video window y coordinate
+ *    *h - video window height
+ *    *w - video window width
+ *
+ * Returns: void
+ */
+void lsm_get_video_window (callid_t call_id, int *flags, int *x, int *y, int *h, int *w)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb != NULL) {
+        *flags = lcb->vid_flags;
+        *x = lcb->vid_x;
+        *y = lcb->vid_y;
+        *h = lcb->vid_h;
+        *w = lcb->vid_w;
+    }
+}
+
+/*
+ * lsm_is_kpml_subscribed
+ *
+ * Description:
+ *    check if kpml is subscribed for this call
+ *
+ * Parameters:
+ *    call_id - GSM call ID
+ *
+ * Returns: true/false
+ */
+boolean lsm_is_kpml_subscribed (callid_t call_id)
+{
+    lsm_lcb_t *lcb;
+
+    lcb = lsm_get_lcb_by_call_id(call_id);
+    if (lcb == NULL) {
+        return FALSE;
+    }
+    return kpml_is_subscribed(call_id, lcb->line);
+}
+
+/**
+ * A helper method to start the tone.
+ */
+static void lsm_util_start_tone(vcm_tones_t tone, short alert_info,
+        cc_call_handle_t call_handle, groupid_t group_id,
+        streamid_t stream_id, uint16_t direction) {
+
+       int               sdpmode = 0;
+    static const char fname[] = "lsm_util_start_tone";
+    line_t line = GET_LINE_ID(call_handle);
+    callid_t call_id = GET_CALL_ID(call_handle);
+    DEF_DEBUG(DEB_F_PREFIX"Enter, line=%d, call_id=%d.\n",
+              DEB_F_PREFIX_ARGS(MED_API, fname), line, call_id);
+
+    sdpmode = 0;
+    config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+    if (!sdpmode) {
+        vcmToneStart(tone, alert_info, call_handle, group_id, stream_id, direction);
+       }
+    /*
+     * Set delay value for multi-part tones and repeated tones.
+     * Currently the only multi-part tones are stutter and message
+     * waiting tones. The only repeated tones are call waiting and
+     * tone on hold tones.  If the DSP ever supports stutter and
+     * message waiting tones, these tones can be removed from this
+     * switch statement.
+     */
+    switch (tone) {
+    case VCM_MSG_WAITING_TONE:
+        lsm_start_multipart_tone_timer(tone, MSG_WAITING_DELAY, call_id);
+        break;
+
+    case VCM_HOLD_TONE:
+        lsm_start_continuous_tone_timer(tone, TOH_DELAY, call_id);
+        break;
+
+    default:
+        break;
+    }
+
+    /*
+     * Update dcb->active_tone if start request
+     * is for an infinite duration tone.
+     */
+    lsm_update_active_tone(tone, call_id);
+}
+
+/*
+ * Plays a short tone. uses the open audio path.
+ * If no audio path is open, plays on speaker.
+ *
+ * @param[in] tone       - tone type
+ * @param[in] alert_info - alertinfo header
+ * @param[in] call_id    - call identifier
+ * @param[in] direction  - network, speaker, both
+ *
+ * @return none
+ */
+void
+lsm_util_tone_start_with_speaker_as_backup (vcm_tones_t tone, short alert_info,
+                                    cc_call_handle_t call_handle, groupid_t group_id,
+                                    streamid_t stream_id, uint16_t direction) {
+    static const char *fname = "lsm_util_tone_start_with_speaker_as_backup";
+    line_t line = GET_LINE_ID(call_handle);
+    callid_t call_id = GET_CALL_ID(call_handle);
+    DEF_DEBUG(DEB_L_C_F_PREFIX"tone=%-2d: direction=%-2d\n",
+              DEB_L_C_F_PREFIX_ARGS(MED_API, line, call_id, fname),
+              tone, direction);
+
+    //vcmToneStart
+    vcmToneStart(tone, alert_info, call_handle, group_id, stream_id, direction);
+
+    /*
+     * Set delay value for multi-part tones and repeated tones.
+     * Currently the only multi-part tones are stutter and message
+     * waiting tones. The only repeated tones are call waiting and
+     * tone on hold tones.  If the DSP ever supports stutter and
+     * message waiting tones, these tones can be removed from this
+     * switch statement.
+     */
+    switch (tone) {
+    case VCM_MSG_WAITING_TONE:
+        lsm_start_multipart_tone_timer(tone, MSG_WAITING_DELAY, call_id);
+        break;
+
+    case VCM_HOLD_TONE:
+        lsm_start_continuous_tone_timer(tone, TOH_DELAY, call_id);
+        break;
+
+    default:
+        break;
+    }
+
+    /*
+     * Update dcb->active_tone if start request
+     * is for an infinite duration tone.
+     */
+    lsm_update_active_tone(tone, call_id);
+
+}
diff --git a/libs/sipcc/core/gsm/media_cap_tbl.c b/libs/sipcc/core/gsm/media_cap_tbl.c
new file mode 100644 (file)
index 0000000..46cce5e
--- /dev/null
@@ -0,0 +1,158 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <cpr_types.h>
+#include "ccapi.h"
+#include "phone_debug.h"
+#include "cc_debug.h"
+#include "CCProvider.h"
+#include "ccapi_snapshot.h"
+
+/**
+ * global media cap table for interaction between what platform
+ * can do and the GSM-SDP module
+ * AUDIO_1 is used for audio cannot be turned off
+ * VIDEO_1 is used for video
+ */
+cc_media_cap_table_t g_media_table = {
+      1,
+      {
+        {CC_AUDIO_1,SDP_MEDIA_AUDIO,TRUE,TRUE,SDP_DIRECTION_RECVONLY},
+        {CC_VIDEO_1,SDP_MEDIA_VIDEO,TRUE,TRUE,SDP_DIRECTION_RECVONLY},
+        {CC_DATACHANNEL_1,SDP_MEDIA_APPLICATION,FALSE,TRUE,SDP_DIRECTION_SENDRECV},
+      }
+};
+
+static boolean g_nativeVidSupported = FALSE;
+static boolean g_vidCapEnabled = FALSE;
+static boolean g_natve_txCap_enabled = FALSE;
+
+/**
+ * Simple method to trigger escalation and deescalation
+ * based on media capability changes
+ */
+void escalateDeescalate() {
+    g_media_table.id++;
+    if ( ccapp_get_state() != CC_INSERVICE ) {
+        VCM_DEBUG(MED_F_PREFIX"Ignoring video cap update\n", "escalateDeescalate");
+        return;
+    }
+
+    //post the event
+    cc_int_feature(CC_SRC_UI, CC_SRC_GSM, CC_NO_CALL_ID,
+         CC_NO_LINE, CC_FEATURE_UPD_MEDIA_CAP, NULL);
+}
+
+cc_boolean cc_media_isTxCapEnabled() {
+   return g_natve_txCap_enabled;
+}
+
+cc_boolean cc_media_isVideoCapEnabled() {
+    if ( g_nativeVidSupported ) {
+       return g_vidCapEnabled;
+    }
+    return FALSE;
+}
+
+/**
+ * API to update the local video cap in the table
+ * called when native video support or vidCap cfg changes
+ *
+ * This method looks at video cap in cfg & native vid support on platform
+ */
+static void updateVidCapTbl(){
+
+    if ( g_vidCapEnabled  ) {
+        if ( g_media_table.cap[CC_VIDEO_1].enabled == FALSE ) {
+            // cfg is enabled but cap tbl is not
+            if ( g_nativeVidSupported ) {
+                // we can do native now enable cap
+                g_media_table.cap[CC_VIDEO_1].enabled = TRUE;
+                g_media_table.cap[CC_VIDEO_1].support_direction =
+                   g_natve_txCap_enabled?SDP_DIRECTION_SENDRECV:SDP_DIRECTION_RECVONLY;
+                if ( g_natve_txCap_enabled == FALSE ) {
+
+                }
+                escalateDeescalate();
+            } else {
+
+            }
+        }
+    }  else {
+        // disable vid cap
+        DEF_DEBUG(MED_F_PREFIX"video capability disabled \n", "updateVidCapTbl");
+
+        if ( g_media_table.cap[CC_VIDEO_1].enabled ) {
+            g_media_table.cap[CC_VIDEO_1].enabled = FALSE;
+            escalateDeescalate();
+        }
+    }
+}
+
+
+/**
+ * API to update video capability on the device
+ * expected to be called once in the beginning only
+ */
+void cc_media_update_native_video_support(boolean val) {
+    DEF_DEBUG(MED_F_PREFIX"Setting native video support val=%d\n", "cc_media_update_native_video_support", val);
+    g_nativeVidSupported = val;
+    updateVidCapTbl();
+}
+
+/**
+ *
+ * API to update video capability on the device based on config
+ */
+void cc_media_update_video_cap(boolean val) {
+    DEF_DEBUG(MED_F_PREFIX"Setting video cap val=%d\n", "cc_media_update_video_cap", val);
+    g_vidCapEnabled = val;
+    updateVidCapTbl();
+    if ( g_nativeVidSupported ) {
+        ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_VIDEO_CAP_ADMIN_CONFIG_CHANGED, CC_DEVICE_ID);
+    }
+}
+
+/**
+ *
+ * API to update video tx capability based on camera plugin events
+ */
+
+void cc_media_update_native_video_txcap(boolean enable) {
+
+    VCM_DEBUG(MED_F_PREFIX"Setting txcap val=%d\n", "cc_media_update_video_txcap", enable);
+
+    if ( g_natve_txCap_enabled == enable ) {
+        // nothing to do
+        return;
+    }
+
+    g_natve_txCap_enabled = enable;
+    ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_CAMERA_ADMIN_CONFIG_CHANGED, CC_DEVICE_ID);
+
+    if ( g_nativeVidSupported && g_vidCapEnabled ) {
+    // act on camera events only iof native video is enabled
+        if ( g_natve_txCap_enabled ) {
+
+        } else if (g_media_table.cap[CC_VIDEO_1].enabled) {
+
+        }
+
+        g_media_table.cap[CC_VIDEO_1].support_direction  =
+            g_natve_txCap_enabled?SDP_DIRECTION_SENDRECV:SDP_DIRECTION_RECVONLY;
+
+        escalateDeescalate();
+    }
+}
+
+static cc_boolean vidAutoTxPref=FALSE;
+void cc_media_setVideoAutoTxPref(cc_boolean txPref){
+       vidAutoTxPref = txPref;
+}
+
+cc_boolean cc_media_getVideoAutoTxPref(){
+       return vidAutoTxPref;
+}
+
+
diff --git a/libs/sipcc/core/gsm/sm.c b/libs/sipcc/core/gsm/sm.c
new file mode 100755 (executable)
index 0000000..80ba7a2
--- /dev/null
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdio.h"
+#include "sm.h"
+#include "gsm.h"
+#include "fsm.h"
+#include "text_strings.h"
+#include "ccapi.h"
+
+sm_rcs_t
+sm_process_event (sm_table_t *tbl, sm_event_t *event)
+{
+    static const char fname[] = "sm_process_event";
+    int        state_id = event->state;
+    int        event_id = event->event;
+    sm_rcs_t   rc       = SM_RC_ERROR;
+    fsm_fcb_t *fcb      = (fsm_fcb_t *) event->data;
+    cc_feature_t  *feat_msg = NULL;
+    line_t        line_id;
+    fsm_types_t   fsm_type;
+    callid_t      call_id;
+    sm_function_t hdlr; /* cached handler in order to compute its addr once */
+
+    /*
+     * validate the state and event
+     * and that there is a valid function for this state-event pair.
+     */
+    if ((state_id > tbl->min_state) &&
+        (state_id < tbl->max_state) &&
+        (event_id > tbl->min_event) &&
+        (event_id < tbl->max_event)) {
+        rc = SM_RC_DEF_CONT;
+        /*
+         * Save some paramters for debuging, the event handler may
+         * free the fcb once returned.
+         */
+        fsm_type = fcb->fsm_type;
+        call_id  = fcb->call_id;
+        if ((hdlr = tbl->table[tbl->max_event * state_id + event_id]) != NULL) {
+            FSM_DEBUG_SM(DEB_F_PREFIX"%s %-4d: 0x%08lx: sm entry: (%s:%s)\n",
+                     DEB_F_PREFIX_ARGS(FSM, fname), fsm_type_name(fsm_type), call_id,
+                     tbl->table[tbl->max_event * state_id + event_id],
+                     fsm_state_name(fsm_type, state_id),
+                     cc_msg_name((cc_msgs_t)(event_id)));
+
+            rc = hdlr(event);
+        }
+
+        if (rc != SM_RC_DEF_CONT) {
+            /* For event_id == CC_MSG_FEATURE then display the
+             * feature associated with it.
+             */
+            if (event_id == CC_MSG_FEATURE) {
+                feat_msg = (cc_feature_t *) event->msg;
+            }
+            line_id = ((cc_feature_t *) event->msg)->line;
+
+            DEF_DEBUG(DEB_L_C_F_PREFIX"%-5s :(%s:%s%s)\n",
+                        DEB_L_C_F_PREFIX_ARGS(GSM, line_id, call_id, fname),
+                        fsm_type_name(fsm_type),
+                        fsm_state_name(fsm_type, state_id),
+                        cc_msg_name((cc_msgs_t)(event_id)),
+                        feat_msg ? cc_feature_name(feat_msg->feature_id):" ");
+        }
+    }
+    /*
+     * Invalid state-event pair.
+     */
+    else {
+        GSM_ERR_MSG(GSM_F_PREFIX"illegal state-event pair: (%d <-- %d)\n",
+                    fname, state_id, event_id);
+        rc = SM_RC_ERROR;
+    }
+
+    return rc;
+}
diff --git a/libs/sipcc/core/gsm/subapi.c b/libs/sipcc/core/gsm/subapi.c
new file mode 100755 (executable)
index 0000000..1dee58f
--- /dev/null
@@ -0,0 +1,269 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "cpr_memory.h"
+#include "cpr_stdlib.h"
+#include "ccapi.h"
+#include "ccsip_task.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "phntask.h"
+#include "phone.h"
+#include "text_strings.h"
+#include "string_lib.h"
+#include "gsm.h"
+#include "vcm.h"
+#include "subapi.h"
+#include "misc_apps_task.h"
+
+
+static void
+sub_print_msg (char *pData, int len)
+{
+    int ix;
+    int msg_id = *((int *)pData);
+
+    buginf("\nCCAPI: cc_msg= %s, 0x=", cc_msg_name((cc_msgs_t)msg_id));
+    for (ix = 0; ix < len; ix++) {
+        if ((ix % 8 == 0) && ix) {
+            buginf("  ");
+        }
+        if (ix % 24 == 0) {
+            buginf("\n");
+        }
+        buginf("%02x ", *pData++);
+    }
+    buginf("\n");
+}
+
+cc_rcs_t
+sub_send_msg (cprBuffer_t buf, uint32_t cmd, uint16_t len, cc_srcs_t dst_id)
+{
+    cpr_status_e rc;
+
+    CC_DEBUG_MSG sub_print_msg((char *)buf, len);
+
+    switch (dst_id) {
+    case CC_SRC_GSM:
+        rc = gsm_send_msg(cmd, buf, len);
+        if (rc == CPR_FAILURE) {
+            cpr_free(buf);
+        }
+        break;
+    case CC_SRC_SIP:
+        rc = SIPTaskSendMsg(cmd, buf, len, NULL);
+        if (rc == CPR_FAILURE) {
+            cpr_free(buf);
+        }
+        break;
+    case CC_SRC_MISC_APP:
+        rc = MiscAppTaskSendMsg(cmd, buf, len);
+        if (rc == CPR_FAILURE) {
+            cpr_free(buf);
+        }
+        break;
+    default:
+        rc = CPR_FAILURE;
+        break;
+    }
+
+    return (rc == CPR_SUCCESS) ? CC_RC_SUCCESS : CC_RC_ERROR;
+}
+
+cc_rcs_t
+sub_int_subnot_register (cc_srcs_t src_id, cc_srcs_t dst_id,
+                         cc_subscriptions_t evt_pkg, void *callback_fun,
+                         cc_srcs_t dest_task, int msg_id, void *term_callback,
+                         int term_msg_id, long min_duration, long max_duration)
+{
+    sipspi_msg_t *pmsg;
+
+    pmsg = (sipspi_msg_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        return CC_RC_ERROR;
+    }
+
+    pmsg->msg.subs_reg.eventPackage = evt_pkg;
+    pmsg->msg.subs_reg.max_duration = max_duration;
+    pmsg->msg.subs_reg.min_duration = min_duration;
+    pmsg->msg.subs_reg.subsIndCallback = (ccsipSubsIndCallbackFn_t)
+        callback_fun;
+    pmsg->msg.subs_reg.subsIndCallbackMsgID = msg_id;
+    pmsg->msg.subs_reg.subsIndCallbackTask = dest_task;
+    pmsg->msg.subs_reg.subsTermCallback = (ccsipSubsTerminateCallbackFn_t)
+        term_callback;
+    pmsg->msg.subs_reg.subsTermCallbackMsgID = term_msg_id;
+
+    return sub_send_msg((cprBuffer_t)pmsg, SIPSPI_EV_CC_SUBSCRIBE_REGISTER,
+                        sizeof(*pmsg), dst_id);
+}
+
+/*
+ * Function: sub_int_subscribe()
+ *
+ * Parameters: msg_p - pointer to sipspi_msg_t (input parameter)
+ *
+ * Description: posts SIPSPI_EV_CC_SUBSCRIBE to SIP task message queue.
+ *
+ * Returns: CC_RC_ERROR - failed to post msg.
+ *          CC_RC_SUCCESS - successful posted msg.
+ */
+cc_rcs_t
+sub_int_subscribe (sipspi_msg_t *msg_p)
+{
+    sipspi_msg_t *pmsg;
+
+    pmsg = (sipspi_msg_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        return CC_RC_ERROR;
+    }
+
+    memcpy(pmsg, msg_p, sizeof(sipspi_msg_t));
+    return sub_send_msg((cprBuffer_t)pmsg, SIPSPI_EV_CC_SUBSCRIBE,
+                        sizeof(*pmsg), CC_SRC_SIP);
+}
+
+
+cc_rcs_t
+sub_int_subscribe_ack (cc_srcs_t src_id, cc_srcs_t dst_id, sub_id_t sub_id,
+                       uint16_t response_code, int duration)
+{
+    sipspi_msg_t *pmsg;
+
+    pmsg = (sipspi_msg_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        return CC_RC_ERROR;
+    }
+
+    pmsg->msg.subscribe_resp.response_code = response_code;
+    pmsg->msg.subscribe_resp.sub_id = sub_id;
+    pmsg->msg.subscribe_resp.duration = duration;
+
+    return sub_send_msg((cprBuffer_t)pmsg, SIPSPI_EV_CC_SUBSCRIBE_RESPONSE,
+                        sizeof(*pmsg), dst_id);
+}
+
+
+cc_rcs_t
+sub_int_notify (cc_srcs_t src_id, cc_srcs_t dst_id, sub_id_t sub_id,
+                ccsipNotifyResultCallbackFn_t notifyResultCallback,
+                int subsNotResCallbackMsgID, ccsip_event_data_t * eventData,
+                subscriptionState subState)
+{
+    sipspi_msg_t *pmsg;
+
+    pmsg = (sipspi_msg_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        return CC_RC_ERROR;
+    }
+
+    pmsg->msg.notify.eventData = eventData;
+    pmsg->msg.notify.notifyResultCallback = notifyResultCallback;
+    pmsg->msg.notify.sub_id = sub_id;
+    pmsg->msg.notify.subsNotResCallbackMsgID = subsNotResCallbackMsgID;
+    pmsg->msg.notify.subState = subState;
+
+    return sub_send_msg((cprBuffer_t) pmsg, SIPSPI_EV_CC_NOTIFY,
+                        sizeof(*pmsg), dst_id);
+}
+
+/*
+ * Function: sub_int_notify_ack()
+ *
+ * Parameters:  sub_id - subcription id for sip stack to track the subscription.
+ *              response_code - response code to be sent in response to NOTIFY.
+ *              cseq  : CSeq for which response is being sent.
+ *
+ * Description: posts SIPSPI_EV_CC_NOTIFY_RESPONSE to SIP task message queue.
+ *
+ * Returns: CC_RC_ERROR - failed to post msg.
+ *          CC_RC_SUCCESS - successful posted msg.
+ */
+cc_rcs_t
+sub_int_notify_ack (sub_id_t sub_id, uint16_t response_code, uint32_t cseq)
+{
+    sipspi_msg_t *pmsg;
+
+    pmsg = (sipspi_msg_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        return CC_RC_ERROR;
+    }
+
+    pmsg->msg.notify_resp.sub_id = sub_id;
+    pmsg->msg.notify_resp.response_code = response_code;
+    pmsg->msg.notify_resp.cseq = cseq;
+
+    return sub_send_msg((cprBuffer_t)pmsg, SIPSPI_EV_CC_NOTIFY_RESPONSE,
+                        sizeof(*pmsg), CC_SRC_SIP);
+}
+
+
+/*
+ * Function: sub_int_subscribe_term()
+ *
+ * Parameters:  sub_id - subcription id for sip stack to track the subscription.
+ *              immediate - boolean flag to indicate if the termination be immediate.
+ *              request_id - request id significant for out going subscriptions
+ *              event_package - event package type
+ *
+ * Description: posts SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED to SIP task message queue.
+ *
+ * Returns: CC_RC_ERROR - failed to post msg.
+ *          CC_RC_SUCCESS - successful posted msg.
+ */
+cc_rcs_t
+sub_int_subscribe_term (sub_id_t sub_id, boolean immediate, int request_id,
+                        cc_subscriptions_t event_package)
+{
+    sipspi_msg_t *pmsg;
+
+    pmsg = (sipspi_msg_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        return CC_RC_ERROR;
+    }
+
+    pmsg->msg.subs_term.immediate = immediate;
+    pmsg->msg.subs_term.sub_id = sub_id;
+    pmsg->msg.subs_term.request_id = request_id;
+    pmsg->msg.subs_term.eventPackage = event_package;
+
+    return sub_send_msg((cprBuffer_t)pmsg, SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED,
+                        sizeof(*pmsg), CC_SRC_SIP);
+}
+
+cc_rcs_t
+sip_send_message (ccsip_sub_not_data_t * msg_data, cc_srcs_t dest_id, int msg_id)
+{
+    ccsip_sub_not_data_t *pmsg;
+
+    pmsg = (ccsip_sub_not_data_t *) cc_get_msg_buf(sizeof(*pmsg));
+
+    if (!pmsg) {
+        return CC_RC_ERROR;
+    }
+
+    memcpy(pmsg, msg_data, sizeof(*pmsg));
+
+    return sub_send_msg((cprBuffer_t)pmsg, msg_id, sizeof(*pmsg), dest_id);
+}
+
+
+cc_rcs_t
+app_send_message (void *msg_data, int msg_len, cc_srcs_t dest_id, int msg_id)
+{
+    void *pmsg;
+
+    pmsg = (void *) cc_get_msg_buf(msg_len);
+
+    if (!pmsg) {
+        return CC_RC_ERROR;
+    }
+
+    memcpy(pmsg, msg_data, msg_len);
+
+    return sub_send_msg((cprBuffer_t)pmsg, msg_id, (uint16_t)msg_len, dest_id);
+}
diff --git a/libs/sipcc/core/includes/ccSession.h b/libs/sipcc/core/includes/ccSession.h
new file mode 100755 (executable)
index 0000000..398f497
--- /dev/null
@@ -0,0 +1,207 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSESSION_H_
+#define _CCSESSION_H_
+
+#include "session.h"
+#include "sessuri.h"
+
+#define SID_TYPE_SHIFT 28
+#define SID_LINE_SHIFT 16
+
+#define GET_SESS_TYPE(x) ( (x & 0xF0000000) >> SID_TYPE_SHIFT )
+#define GET_LINEID(x) (line_t)( (x & 0xFFF0000) >> SID_LINE_SHIFT )
+#define GET_CALLID(x) (callid_t)(x & 0xFFFF)
+
+
+
+
+/**
+ *  ccSessionProviderCmd
+ *      CallControl Provider Management Interface
+ *      Called by Application to issue cmds to sipStack
+ *
+ *  @param data -  command and data
+ *         data->cmd - see Session Provider Commands in session.h
+ *         data->cmdData.ccData.reason - reason for SHUTDOWN/UNREGISTER_ALL_LINES CMD
+                 CC_CAUSE_NORMAL/CC_CAUSE_NONE
+ *         data->cmdData.ccData.reason_info - Descriptive stringa "notused"
+ *
+ *  @return  none
+ *
+ */
+void ccSessionProviderCmd(sessionProvider_cmd_t *data);
+
+
+/**
+ *  ccSessionProviderState
+ *      Method to report provider state updates to Application
+ *
+ *  @param state - indicates the Session Provider state CCApp_states_t
+ *  @param data  - ccProvider_state_t indicating
+ *         data->stateData.ccData.mode  - REGMODE CCM
+ *         data->stateData.ccData.cause - FAILOVER/FALLBACK
+ *
+ *  @return  none
+ *
+ */
+void ccSessionProviderState(unsigned int state, ccProvider_state_t *data);
+
+
+/**
+ *  ccSessionCmd
+ *      Method to Handle session lifecycle command such as realize, start etc.
+ *
+ *  @param sCmd - session liefcycle command
+ *         sCmd->cmd - REQUEST CMD
+ *         sCmd->sessID - session ID
+ *
+ *  @return  none
+ *
+ */
+
+void ccSessionCmd (sessionCmd_t *sCmd);
+
+
+/**
+ *  ccCreateSession
+ *
+ *      Called by Application to create a new session
+ *
+ *  @param param - uri_t
+ *         param->param.call_session_param.line_id - line on which to create the session
+ *  @return  ccSession_id_t - id of the session created
+ */
+
+session_id_t ccCreateSession(uri_t *param);
+
+/**
+ *  ccCloseSession
+ *
+ *      Called by Application to close a session
+ *
+ *  @param sess_id - ID of the session to be closed
+ *
+ *  @return  0 success -1 failure
+ *
+ */
+
+int ccCloseSession(session_id_t sess_id);
+
+/* Need to document IDs and data for the following 4 methods */
+
+/**
+ *  ccInvokeFeature
+ *
+ *      Called by Application to invoke feature on session
+ *
+ *  @param featData  - featID and Additional info if needed for the feature
+ *
+ *  @return  none
+ *
+ */
+
+void ccInvokeFeature(session_feature_t *featData);
+
+/**
+ *  ccInvokeProviderFeature
+ *
+ *      Called by Application to invoke device specific features
+ *
+ *  @param featData  - Additional info if needed for the feature
+ *
+ *  @return  none
+ *
+ */
+
+void ccInvokeProviderFeature(session_feature_t *featData);
+
+
+/**
+ *  ccSessionUpdate
+ *
+ *      Called by sipstack to update session state and data
+ *
+ *  @param eventID - ID of the event updating the session data
+ *  @param session_data  - event specific data
+ *
+ *  @return  none
+ *
+ */
+
+void ccSessionUpdate(session_update_t *session);
+
+
+
+/**
+ *  ccFeatureUpdate
+ *
+ *      Called by sipstack to update feature state and data
+ *
+ *  @param featureID - ID of the feature updated
+ *  @param session_data  - feature specific data
+ *
+ *  @return  none
+ *
+ */
+
+void ccFeatureUpdate(feature_update_t *session);
+
+/***** Internal APIs below this line ***************/
+
+/**
+ *  ccCreateSession
+ *
+ *      Called to create a CC session
+ *
+ *  @param param - ccSession_create_param_t
+ *               Contains the type of session and specific data
+ *
+ *  @return  ccSession_id_t - id of the session created
+ */
+session_id_t createSessionId(line_t line, callid_t call);
+
+void platform_sync_cfg_vers (char *cfg_ver, char *dp_ver, char *softkey_ver);
+/********************************************************************************/
+
+/* Misc getter/setter function  to get set the following */
+
+#define PROPERTY_ID_MWI          1  // per line
+#define PROPERTY_ID_TIME         2  // unsigned long
+#define PROPERTY_ID_KPML         3
+#define PROPERTY_ID_REGREASON    4
+#define PROPERTY_ID_SPKR_HDST    5     1
+
+void setIntProperty(unsigned int id, int  val);
+int getIntProperty(unsigned int id);
+void setStrProperty(unsigned int id, char * val);
+char * getStrProperty(unsigned int id);
+
+
+/* Preserved API's from TNP Platform */
+char *ccSetDP(const char *dp_file_name);
+// update_label_n_speed_dial method here
+void setPropertyCacheBoolean(int cfg_id, int bool_value);
+void setPropertyCacheInteger(int cfg_id, int int_value);
+void setPropertyCacheString(int cfg_id, const char *string_value);
+void setPropertyCacheByte(int cfg_id, char byte_value);
+void setPropertyCacheByteArray(int cfg_id, char *byte_value, int length);
+
+/* BLF */
+void ccBLFSubscribe(int request_id, int duration, const char *watcher,
+                    const char *presentity, int app_id, int feature_mask);
+void ccBLFUnsubscribe(int request_id);
+void ccBLFUnsubscribeAll();
+void blf_notification(int request_id, int status, int app_id);
+
+
+/**********************************************************************
+
+MID_JPlatUi_ui_log_status_msg,         Ask RCDN team if log is changing?
+
+**********************************************************************/
+
+#endif
+
diff --git a/libs/sipcc/core/includes/ccapi.h b/libs/sipcc/core/includes/ccapi.h
new file mode 100755 (executable)
index 0000000..6663977
--- /dev/null
@@ -0,0 +1,1578 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_H_
+#define _CCAPI_H_
+
+#include "prtypes.h"
+#include "cpr_types.h"
+#include "cpr_memory.h"
+#include "phone_types.h"
+#include "string_lib.h"
+#include "vcm.h"
+#include "sdp.h"
+#include "cc_constants.h"
+
+typedef unsigned int cc_security_e;
+typedef unsigned int cc_select_state_e;
+typedef unsigned int cc_call_type_e;
+typedef unsigned int cc_policy_e;
+typedef int cc_causes_t;
+#define  CC_CALL_OUTGOMING  CC_CALL_TYPE_OUTGOING
+#define  CC_CALL_FORWARDED  CC_CALL_TYPE_FORWARDED
+#define  CC_CALL_NONE       CC_CALL_TYPE_NONE
+#define  CC_CALL_INCOMING   CC_CALL_TYPE_INCOMING
+#define  SDP_SIZE           4096   /* must increase this */
+#define  CANDIDATE_SIZE     150
+#define  MID_SIZE           150
+
+#include "sessionConstants.h"
+
+typedef int  cc_features_t;
+typedef unsigned int softkey_events;
+typedef unsigned int cc_call_priority_e;
+extern cc_reg_state_t ccapp_get_state();
+
+/* Session FEATURES */
+/* please update cc_feature_names whenever this enum list is changed */
+
+typedef enum {
+    CC_FEATURE_MIN = -1L,
+    CC_FEATURE_NONE,
+    CC_FEATURE_HOLD = 1L,
+    CC_FEATURE_RESUME,
+    CC_FEATURE_OFFHOOK,
+    CC_FEATURE_NEW_CALL,
+    CC_FEATURE_REDIAL,
+    CC_FEATURE_ONHOOK,
+    CC_FEATURE_KEYPRESS,
+    CC_FEATURE_DIAL,
+    CC_FEATURE_XFER,
+    CC_FEATURE_CFWD_ALL,
+    CC_FEATURE_END_CALL,
+    CC_FEATURE_ANSWER,
+    CC_FEATURE_INFO,
+    CC_FEATURE_CONF,
+    CC_FEATURE_JOIN,
+    CC_FEATURE_DIRTRXFR,
+    CC_FEATURE_SELECT,
+    CC_FEATURE_SPEEDDIAL,
+    CC_FEATURE_SWAP,
+    CC_FEATURE_SPEEDDIAL_BLF,
+    CC_FEATURE_BLIND_XFER_WITH_DIALSTRING,
+    CC_FEATURE_BKSPACE,
+    CC_FEATURE_CANCEL,
+    CC_FEATURE_DIALSTR,
+    CC_FEATURE_UPD_SESSION_MEDIA_CAP,
+    /* Not used in the session API */
+    CC_FEATURE_MEDIA,
+    CC_FEATURE_UPDATE,
+    CC_FEATURE_CALLINFO,
+    CC_FEATURE_BLIND_XFER,
+    CC_FEATURE_NOTIFY,
+    CC_FEATURE_SUBSCRIBE,
+    CC_FEATURE_B2BCONF,
+    CC_FEATURE_B2B_JOIN,
+    CC_FEATURE_HOLD_REVERSION,
+    CC_FEATURE_BLF_ALERT_TONE,
+    CC_FEATURE_REQ_PEND_TIMER_EXP,
+    CC_FEATURE_NUMBER,
+    CC_FEATURE_URL,
+    CC_FEATURE_REDIRECT,
+    CC_FEATURE_RINGBACK_DELAY_TIMER_EXP,
+    CC_FEATURE_CALL_PRESERVATION,
+    CC_FEATURE_UPD_MEDIA_CAP,
+    CC_FEATURE_CAC_RESP_PASS,
+    CC_FEATURE_CAC_RESP_FAIL,
+    CC_FEATURE_FAST_PIC_UPD,
+    CC_FEATURE_UNDEFINED,
+    CC_FEATURE_CREATEOFFER,
+    CC_FEATURE_CREATEANSWER,
+    CC_FEATURE_SETLOCALDESC,
+    CC_FEATURE_SETREMOTEDESC,
+    CC_FEATURE_LOCALDESC,
+    CC_FEATURE_REMOTEDESC,
+    CC_FEATURE_SETPEERCONNECTION,
+    CC_FEATURE_ADDSTREAM,
+    CC_FEATURE_REMOVESTREAM,
+    CC_FEATURE_ADDICECANDIDATE,
+    CC_FEATURE_MAX
+} group_cc_feature_t;
+
+#define skNewCall CC_FEATURE_NEW_CALL
+#define skConfrn CC_FEATURE_CONF
+
+/* please update the following cc_feature_names whenever this feature list is changed */
+
+#ifdef __CC_FEATURE_STRINGS__
+static const char *cc_feature_names[] = {
+    "NONE",
+    "HOLD",
+    "RESUME",
+    "OFFHOOK",
+    "NEW_CALL",
+    "REDIAL",
+    "ONHOOK",
+    "KEYPRESS",
+    "DIAL",
+    "XFER",
+    "CFWD_ALL",  //10
+    "END_CALL",
+    "ANSWER",
+    "INFO",
+    "CONF",
+    "JOIN",
+    "DIR_XFER",
+    "SELECT",
+    "SPEEDDIAL",
+    "SWAP",
+    "SDBLF",  //45
+    "BLIND_XFER_WITH_DIALSTRING",
+    "BSPACE",
+    "CANCEL",
+    "DIALSTR",
+    "UPD_SESSION_MEDIA_CAP",
+    "NEW_MEDIA",
+    "UPDATE",
+    "CALLINFO",
+    "BLIND_XFER",
+    "NOTIFY",
+    "SUBSCRIBE",
+    "B2BCONF",
+    "B2BJOIN",
+    "HOLD_REVERSION", //jni_max + 10
+    "BLF_ALERT_TONE",
+    "REQPENDTMREXP",
+    "NUMBER",
+    "URL",
+    "REDIRECT", //jni_max + 20
+    "RINGBACKDELAYTMREXP",
+    "CALL_PRESERVATION",
+    "UPD_MEDIA_CAP",
+    "CAC PASSED",
+    "CAC FAILED",
+    "FAST_PIC_UPD",
+    "UNDEFINED",
+    "CREATEOFFER",
+    "CREATEANSWER",
+    "SETLOCALDESC",
+    "SETREMOTEDESC",
+    "LOCALDESC",
+    "REMOTEDESC",
+    "SETPEERCONNECTION",
+    "ADDSTREAM",
+    "REMOVESTREAM",
+    "ADDICECANDIDATE",
+    "MAX"
+};
+
+/* This checks at compile-time that the cc_feature_names list
+ * is the same size as the cc_group_feature_t enum
+ */
+PR_STATIC_ASSERT(PR_ARRAY_SIZE(cc_feature_names) == CC_FEATURE_MAX + 1);
+
+#endif
+
+/*
+ * Constants
+ */
+#define CC_MAX_DIALSTRING_LEN (512)
+#define CC_MAX_MEDIA_TYPES    (15)
+#define CC_MAX_MEDIA_CAP      (4)
+#define CC_NO_CALL_ID         (0)
+#define CC_NO_LINE            (0)
+#define CC_NO_DATA            (NULL)
+#define CC_NO_MEDIA_REF_ID    (0)
+#define CC_MAX_REDIRECTS      (1)
+#define CC_MAX_BODY_PARTS     3
+#define CC_NO_GROUP_ID        (0)
+#define CC_NO_CALL_INST_ID    (0)
+#define CC_SHORT_DIALSTRING_LEN (64)
+#define CC_CISCO_PLAR_STRING  "x-cisco-serviceuri-offhook"
+#define CISCO_BLFPICKUP_STRING  "x-cisco-serviceuri-blfpickup"
+#define JOIN_ACROSS_LINES_DISABLED 0
+#define CC_MAX_TRACKS          8   // <EM> query this figure
+#define CC_MAX_STREAMS         2   // TODO: expand signaling to handle more than one of each a/v.
+
+
+/*
+ *
+ * Support type definintions
+ *
+ */
+typedef enum cc_rcs_t_ {
+    CC_RC_MIN = -1,
+    CC_RC_SUCCESS,
+    CC_RC_ERROR,
+    CC_RC_MAX
+} cc_rcs_t;
+
+typedef enum cc_msgs_t_ {
+    CC_MSG_MIN = -1,
+    CC_MSG_SETUP,
+    CC_MSG_SETUP_ACK,
+    CC_MSG_PROCEEDING,
+    CC_MSG_ALERTING,
+    CC_MSG_CONNECTED,
+    CC_MSG_CONNECTED_ACK,
+    CC_MSG_RELEASE,
+    CC_MSG_RELEASE_COMPLETE,
+    CC_MSG_FEATURE,
+    CC_MSG_FEATURE_ACK,
+    CC_MSG_OFFHOOK,
+    CC_MSG_ONHOOK,
+    CC_MSG_LINE,
+    CC_MSG_DIGIT_BEGIN,
+    CC_MSG_DIGIT_END,
+    CC_MSG_DIALSTRING,
+    CC_MSG_MWI,
+    CC_MSG_AUDIT,
+    CC_MSG_CREATEOFFER,
+    CC_MSG_CREATEANSWER,
+    CC_MSG_SETLOCALDESC,
+    CC_MSG_SETREMOTEDESC,
+    CC_MSG_REMOTEDESC,
+    CC_MSG_LOCALDESC,
+    CC_MSG_SETPEERCONNECTION,
+    CC_MSG_ADDSTREAM,
+    CC_MSG_REMOVESTREAM,
+    CC_MSG_ADDCANDIDATE,
+    CC_MSG_AUDIT_ACK,
+    CC_MSG_OPTIONS,
+    CC_MSG_OPTIONS_ACK,
+    CC_MSG_SUBSCRIBE,
+    CC_MSG_NOTIFY,
+    CC_MSG_FAILOVER_FALLBACK,
+    CC_MSG_INFO,
+    /* update the following strings table if this is changed */
+    CC_MSG_MAX
+} cc_msgs_t;
+
+#ifdef __CC_MESSAGES_STRINGS__
+static const char *cc_msg_names[] = {
+    "SETUP",
+    "SETUP_ACK",
+    "PROCEEDING",
+    "ALERTING",
+    "CONNECTED",
+    "CONNECTED_ACK",
+    "RELEASE",
+    "RELEASE_COMPLETE",
+    "FEAT:",
+    "FEATURE_ACK",
+    "OFFHOOK",
+    "ONHOOK",
+    "LINE",
+    "DIGIT_BEGIN",
+    "DIGIT_END",
+    "DIALSTRING",
+    "MWI",
+    "AUDIT",
+    "CREATEOFFER",
+    "CREATEANSWER",
+    "SETLOCALDESC",
+    "SETREMOTEDESC",
+    "REMOTEDESC",
+    "LOCALDESC",
+    "SETPEERCONNECTION",
+    "ADDSTREAM",
+    "REMOVESTREAM",
+    "ADDCANDIDATE",
+    "AUDIT_ACK",
+    "OPTIONS",
+    "OPTIONS_ACK",
+    "SUBSCRIBE",
+    "NOTIFY",
+    "FAILOVER_FALLBACK",
+    "INFO",
+    "INVALID",
+};
+
+/* This checks at compile-time that the cc_msg_names list
+ * is the same size as the cc_msgs_t enum
+ */
+PR_STATIC_ASSERT(PR_ARRAY_SIZE(cc_msg_names) == CC_MSG_MAX + 1);
+
+#endif //__CC_MESSAGES_STRINGS__
+
+typedef enum cc_srcs_t_ {
+    CC_SRC_MIN = -1,
+    CC_SRC_GSM,
+    CC_SRC_UI,
+    CC_SRC_SIP,
+    CC_SRC_MISC_APP,
+    CC_SRC_RCC,
+    CC_SRC_CCAPP,
+    CC_SRC_MAX
+} cc_srcs_t;
+
+
+typedef enum cc_transfer_mode_e_ {
+    CC_XFR_MODE_MIN = -1,
+    CC_XFR_MODE_NONE,
+    CC_XFR_MODE_TRANSFEROR,
+    CC_XFR_MODE_TRANSFEREE,
+    CC_XFR_MODE_TARGET,
+    CC_XFR_MODE_MAX
+} cc_transfer_mode_e;
+
+typedef enum cc_regmgr_rsp_type_e_ {
+    CC_FAILOVER_RSP=0,
+    CC_RSP_START =1,
+    CC_RSP_COMPLETE=2
+} cc_regmgr_rsp_type_e;
+
+typedef enum cc_regmgr_rsp_e_ {
+    CC_REG_FAILOVER_RSP,
+    CC_REG_FALLBACK_RSP
+} cc_regmgr_rsp_e;
+
+/*
+ * Identifies what was in the Alert-Info header
+ */
+typedef enum {
+    ALERTING_NONE, /* No alert-info header present */
+    ALERTING_OLD,  /* Unrecognized pattern or an error, mimic old behavior */
+    ALERTING_TONE, /* Play tone */
+    ALERTING_RING  /* Play ringing pattern */
+} cc_alerting_type;
+
+typedef enum {
+    CC_MONITOR_NONE = 0,
+    CC_MONITOR_SILENT = 1,
+    CC_MONITOR_COACHING = 2
+} monitor_mode_t;
+
+typedef enum {
+  CFWDALL_NONE = -1,
+  CFWDALL_CLEAR,
+  CFWDALL_SET
+} cfwdall_mode_t;
+
+/* Do not change enum values unless remote-cc xml has been changed */
+
+typedef enum {
+    CC_RING_DEFAULT = 0,
+    CC_RING_ENABLE = 1,
+    CC_RING_DISABLE = 2
+} cc_rcc_ring_mode_e;
+
+/* Do not change enum values unless remote-cc xml has been changed */
+
+typedef enum {
+    CC_SK_EVT_TYPE_DEF = 0,
+    CC_SK_EVT_TYPE_EXPLI = CC_SK_EVT_TYPE_DEF,
+    CC_SK_EVT_TYPE_IMPLI = 1
+} cc_rcc_skey_evt_type_e;
+
+/* media name with media capability table */
+typedef enum {
+    CC_AUDIO_1,
+    CC_VIDEO_1,
+    CC_DATACHANNEL_1,
+} cc_media_cap_name;
+
+typedef struct cc_sdp_addr_t_ {
+    cpr_ip_addr_t addr;
+    unsigned int port;
+} cc_sdp_addr_t;
+
+typedef struct cc_sdp_data_t_ {
+    cc_sdp_addr_t addr;
+    int           media_types[CC_MAX_MEDIA_TYPES];
+    int32_t       avt_payload_type;
+} cc_sdp_data_t;
+
+typedef struct cc_sdp_t_ {
+    sdp_t         *src_sdp;     /* pointer to source SDP */
+    sdp_t         *dest_sdp;    /* pointer to received SDP */
+} cc_sdp_t;
+
+typedef enum
+{
+    cc_disposition_unknown = 0,
+    cc_disposition_render,
+    cc_disposition_session,
+    cc_dispostion_icon,
+    cc_disposition_alert,
+    cc_disposition_precondition
+} cc_disposition_type_t;
+
+typedef struct
+{
+    cc_disposition_type_t disposition;
+    boolean               required_handling;
+} cc_content_disposition_t;
+
+typedef enum
+{
+    cc_content_type_unknown = 0,
+    cc_content_type_SDP,
+    cc_content_type_sipfrag,
+    cc_content_type_CMXML
+} cc_content_type_t;
+
+typedef struct cc_msgbody_t_ {
+    cc_content_type_t        content_type;
+    cc_content_disposition_t content_disposition;
+    uint32_t                 body_length;
+    char                     *body;
+    char                     *content_id;
+} cc_msgbody_t;
+
+typedef struct cc_msgbody_info_t_ {
+        uint32_t          num_parts;            /* number of body parts   */
+        cc_content_type_t content_type;         /* top level content type */
+        cc_msgbody_t  parts[CC_MAX_BODY_PARTS]; /* parts                  */
+} cc_msgbody_info_t;
+
+/*
+ * Message definitions
+ */
+
+typedef enum cc_redirect_reasons_t {
+    CC_REDIRECT_REASON_MIN = -1,
+    CC_REDIRECT_REASON_NONE,
+    CC_REDIRECT_REASON_BUSY,
+    CC_REDIRECT_REASON_NOANSWER,
+    CC_REDIRECT_REASON_UNCONDITIONAL,
+    CC_REDIRECT_REASON_DEFLECTION,
+    CC_REDIRECT_REASON_UNAVAILABLE
+} cc_redirect_reasons_t;
+
+typedef struct cc_redirect_t_ {
+    int count;
+    struct {
+        char                  number[CC_MAX_DIALSTRING_LEN];
+        cc_redirect_reasons_t redirect_reason;
+    } redirects[CC_MAX_REDIRECTS];
+} cc_redirect_t;
+
+typedef enum {
+   CC_FEAT_NONE,
+   CC_FEAT_HOLD,
+   CC_FEAT_RESUME,
+   CC_FEAT_BARGE,
+   CC_FEAT_CBARGE,
+   CC_FEAT_REPLACE,
+   CC_FEAT_CALLINFO,
+   CC_FEAT_INIT_CALL,
+   CC_FEAT_MONITOR,
+   CC_FEAT_TOGGLE_TO_SILENT_MONITORING,
+   CC_FEAT_TOGGLE_TO_WHISPER_COACHING
+} cc_call_info_e;
+
+typedef enum {
+   CC_REASON_NONE,
+   CC_REASON_XFER,
+   CC_REASON_CONF,
+   CC_REASON_SWAP,
+   CC_REASON_RCC,
+   CC_REASON_INTERNAL,
+   CC_REASON_MONITOR_UPDATE
+} cc_hold_resume_reason_e;
+
+typedef enum {
+   CC_PURPOSE_NONE,
+   CC_PURPOSE_INFO,
+   CC_PURPOSE_ICON,
+   CC_PURPOSE_CARD
+} cc_purpose_e;
+
+typedef enum {
+   CC_ORIENTATION_NONE = CC_CALL_TYPE_NONE,
+   CC_ORIENTATION_FROM = CC_CALL_TYPE_INCOMING,
+   CC_ORIENTATION_TO = CC_CALL_TYPE_OUTGOING
+} cc_orientation_e;
+
+typedef enum {
+   CC_UI_STATE_NONE,
+   CC_UI_STATE_RINGOUT,
+   CC_UI_STATE_CONNECTED,
+   CC_UI_STATE_BUSY
+} cc_ui_state_e;
+
+typedef enum {
+   CC_REASON_NULL,
+   CC_REASON_ACTIVECALL_LIST
+} cc_onhook_reason_e;
+
+typedef enum {
+   CC_CALL_LOG_DISP_MISSED,
+   CC_CALL_LOG_DISP_RCVD,
+   CC_CALL_LOG_DISP_PLACED,
+   CC_CALL_LOG_DISP_UNKNWN,
+   CC_CALL_LOG_DISP_IGNORE
+} cc_call_logdisp_e;
+
+/*
+ * The cc_caller_id_t structure contains caller IDs fields.
+ * Sending these fields accross components needs special care.
+ * There are CCAPI API that provide access or transport these fields
+ * by the source or by the destination. Any new caller IDs added,
+ * please update the APIs in ccapi.c to support the new fields.
+ * See cc_cp_caller(), cc_mv_caller_id() and cc_free_caller_id() functions.
+ */
+// mostly overlap with sessionTypes.h:cc_callinfo_t
+typedef struct cc_caller_id_t_ {
+    string_t calling_name;
+    string_t calling_number;
+    string_t alt_calling_number;
+    boolean  display_calling_number;
+    string_t called_name;
+    string_t called_number;
+    boolean  display_called_number;
+    string_t orig_called_name;
+    string_t orig_called_number;
+    string_t last_redirect_name;
+    string_t last_redirect_number;
+    string_t orig_rpid_number;
+    cc_call_type_e call_type;
+    uint16_t call_instance_id;
+} cc_caller_id_t;
+
+/*
+ * The followings are definitions of bits in the feature_flag of the
+ * cc_feature_data_call_info_t strucure.
+ *
+ * The CC_DELAY_UI_UPDATE flag is not related to call information but
+ * it indicates that the call info event signaled by SIP stack
+ * to GSM that UI update can be delayed due to media manipulation
+ * event will follow. This allows GSM to defer UI update to after the
+ * media manipulation event.
+ */
+#define CC_SECURITY         1
+#define CC_ORIENTATION      (1<<1)
+#define CC_UI_STATE         (1<<2)
+#define CC_CALLER_ID        (1<<3)
+#define CC_CALL_INSTANCE    (1<<4)
+#define CC_DELAY_UI_UPDATE  (1<<5)
+#define CC_POLICY  (1<<6)
+
+typedef struct cc_feature_data_call_info_t_{
+   uint16_t            feature_flag;
+   cc_security_e       security;
+   cc_policy_e         policy;
+   cc_orientation_e    orientation;
+   cc_ui_state_e       ui_state;
+   cc_caller_id_t      caller_id;
+   cc_call_priority_e  priority;
+   boolean             swap; //Indicate if hold/resume is because of swap
+   boolean             protect; //indicate if the call has to be protected
+   boolean             dusting; //indicate if it is a dusting call
+   uint32_t            callref;
+   char                global_call_id[CC_GCID_LEN];
+} cc_feature_data_call_info_t;
+
+typedef struct cc_replace_info_t_ {
+   callid_t           remote_call_id; /* remote call ID to replace */
+} cc_replace_info_t;
+
+typedef struct cc_join_info_t_ {
+   callid_t           join_call_id;
+} cc_join_info_t;
+
+typedef struct cc_initcall_t {
+   char                           gcid[CC_GCID_LEN];    // Global call id used for CTI
+   monitor_mode_t                 monitor_mode;
+} cc_initcall_t;
+
+typedef union {
+   cc_initcall_t                  initcall;
+   cc_hold_resume_reason_e        hold_resume_reason;
+   cc_feature_data_call_info_t    call_info_feat_data;
+   cc_purpose_e                   purpose; // Used for Barge, CBARGE
+   cc_replace_info_t              replace;
+   cc_join_info_t                 join;
+} cc_call_info_data_t;
+
+typedef struct {
+  cc_call_info_data_t    data;
+  cc_call_info_e         type;
+} cc_call_info_t;
+
+typedef enum cc_xfer_methods_t_ {
+    CC_XFER_METHOD_MIN = -1,
+    CC_XFER_METHOD_NONE,
+    CC_XFER_METHOD_BYE,
+    CC_XFER_METHOD_REFER,
+    CC_XFER_METHOD_DIRXFR,
+    CC_RCC_METHOD_REFER,
+    CC_XFER_METHOD_MAX
+} cc_xfer_methods_t;
+
+typedef enum cc_subscriptions_t_ {
+    CC_SUBSCRIPTIONS_MIN = -1,
+    CC_SUBSCRIPTIONS_NONE,
+    CC_SUBSCRIPTIONS_XFER,
+    CC_SUBSCRIPTIONS_DIALOG = CC_SUBSCRIPTIONS_DIALOG_EXT,
+    CC_SUBSCRIPTIONS_CONFIG,
+    CC_SUBSCRIPTIONS_KPML = CC_SUBSCRIPTIONS_KPML_EXT,
+    CC_SUBSCRIPTIONS_PRESENCE = CC_SUBSCRIPTIONS_PRESENCE_EXT,
+    CC_SUBSCRIPTIONS_REMOTECC = CC_SUBSCRIPTIONS_REMOTECC_EXT,
+    CC_SUBSCRIPTIONS_REMOTECC_OPTIONSIND = CC_SUBSCRIPTIONS_REMOTECC_OPTIONSIND_EXT,
+    CC_SUBSCRIPTIONS_CONFIGAPP = CC_SUBSCRIPTIONS_CONFIGAPP_EXT,
+    CC_SUBSCRIPTIONS_MEDIA_INFO = CC_SUBSCRIPTIONS_MEDIA_INFO_EXT,
+    CC_SUBSCRIPTIONS_MAX
+} cc_subscriptions_t;
+
+typedef struct cc_feature_data_newcall_t_ {
+    char          dialstring[CC_MAX_DIALSTRING_LEN];
+    cc_causes_t   cause;
+    cc_redirect_t redirect;
+    cc_replace_info_t replace;
+    cc_join_info_t   join;
+    char          global_call_id[CC_GCID_LEN];
+    callid_t      prim_call_id;                /* For internal new call event
+                                                * refer primary call's call_id
+                                                */
+    cc_hold_resume_reason_e     hold_resume_reason; /* Reason for new consult call */
+} cc_feature_data_newcall_t;
+
+typedef struct cc_feature_data_xfer_t_ {
+    cc_causes_t       cause;
+    char              dialstring[CC_MAX_DIALSTRING_LEN];
+    char              referred_by[CC_MAX_DIALSTRING_LEN];
+    cc_xfer_methods_t method;
+    callid_t          target_call_id;
+    char          global_call_id[CC_GCID_LEN];
+} cc_feature_data_xfer_t;
+
+typedef enum cc_app_type_t_ {
+    CC_APP_MIN = -1,
+    CC_APP_NONE,
+    CC_APP_CMXML,
+    CC_APP_REMOTECC,
+    CC_APP_MAX
+} cc_app_type_t;
+
+typedef struct cc_refer_body_t_ {
+    struct cc_refer_body_t_  *next;
+    char              refer_body[200];
+    int                  refer_body_len;
+    cc_app_type_t      app_type;
+} cc_refer_body_t;
+
+typedef struct cc_feature_data_ind_t__ {
+    cc_causes_t       cause;
+    char              to[CC_MAX_DIALSTRING_LEN];
+    char              referred_by[CC_MAX_DIALSTRING_LEN];
+    char              referred_to[CC_MAX_DIALSTRING_LEN];
+    cc_refer_body_t   refer_body;
+} cc_feature_data_ind_t;
+
+typedef struct cc_feature_data_endcall_t_ {
+    cc_causes_t cause;
+    char        dialstring[CC_MAX_DIALSTRING_LEN];
+} cc_feature_data_endcall_t;
+
+typedef struct cc_kfact_t {
+  char   rxstats[CC_KFACTOR_STAT_LEN];
+  char   txstats[CC_KFACTOR_STAT_LEN];
+} cc_kfact_t;
+
+typedef struct cc_feature_data_hold_t_ {
+    cc_msgbody_info_t msg_body;
+    cc_call_info_t    call_info;
+    cc_kfact_t        kfactor;
+} cc_feature_data_hold_t;
+
+typedef struct cc_feature_data_hold_reversion_t_ {
+    int               alertInterval;
+} cc_feature_data_hold_reversion_t;
+
+typedef struct cc_feature_data_resume_t_ {
+    cc_causes_t       cause;
+    cc_msgbody_info_t msg_body;
+    cc_call_info_t    call_info;
+    cc_kfact_t        kfactor;
+} cc_feature_data_resume_t;
+
+typedef struct cc_feature_data_redirect_t_ {
+    char          redirect_number[CC_MAX_DIALSTRING_LEN];
+    cc_redirect_t redirect;
+} cc_feature_data_redirect_t;
+
+typedef struct cc_feature_data_subscribe_t_ {
+    cc_subscriptions_t event_package;
+    boolean            subscribe;
+    char               subscribe_uri[CC_MAX_DIALSTRING_LEN];
+    cc_srcs_t          component;
+    int                component_id;
+    int                *callBack;
+} cc_feature_data_subscribe_t;
+
+typedef enum cc_dialog_lock_e_ {
+    CC_DIALOG_UNLOCKED,       /* unlocked                              */
+    CC_DIALOG_LOCKED         /* local selected or locked               */
+} cc_dialog_lock_e;
+
+typedef struct cc_notify_data_config_t {
+    boolean config_state;
+} cc_notify_data_config_t;
+
+typedef struct cc_notify_data_kpml_t {
+    boolean kpml_state;
+} cc_notify_data_kpml_t;
+
+
+typedef struct cc_notify_data_rcc_t {
+    cc_features_t     feature;
+    cc_select_state_e select;
+} cc_notify_data_rcc_t;
+
+
+typedef union cc_notify_data_t {
+    cc_notify_data_config_t config;
+    cc_notify_data_kpml_t   kpml;
+    cc_notify_data_rcc_t    rcc;
+} cc_notify_data_t;
+
+typedef struct cc_feature_data_notify_t_ {
+    cc_subscriptions_t subscription;
+    cc_xfer_methods_t method;
+    cc_causes_t       cause;
+    // The refer data above should have been in a cc_notify_data_refer_t
+    int               cause_code;
+    callid_t          blind_xferror_gsm_id;
+    boolean           final;
+    cc_notify_data_t  data;
+} cc_feature_data_notify_t;
+
+typedef struct cc_feature_data_update_t_ {
+    cc_msgbody_info_t msg_body;
+} cc_feature_data_update_t;
+
+typedef struct cc_feature_data_b2bcnf_t_ {
+    cc_causes_t cause;
+    callid_t    call_id;
+    callid_t    target_call_id;
+    char        global_call_id[CC_GCID_LEN];
+} cc_feature_data_b2bcnf_t;
+
+typedef struct cc_feature_data_record_t_ {
+    boolean subref_flag;
+} cc_feature_data_record_t;
+
+typedef struct cc_feature_data_select_t_ {
+    boolean select;   /* TRUE when select, FALSE when unselect */
+} cc_feature_data_select_t;
+
+typedef struct cc_feature_data_b2b_join_t_ {
+    callid_t    b2bjoin_callid;
+    callid_t    b2bjoin_joincallid;
+} cc_feature_data_b2b_join_t;
+
+
+typedef struct cc_media_cap_t_ {
+    cc_media_cap_name name;          /* media channel name designator     */
+    sdp_media_e       type;          /* media type: audio, video etc      */
+    boolean           enabled;       /* this media is enabled or disabled */
+    boolean           support_security; /* security is supported          */
+    sdp_direction_e   support_direction;/* supported direction            */
+    cc_media_stream_id_t pc_stream;       /* The media stream in the PC */
+    cc_media_track_id_t  pc_track;        /* The track ID in the media stream */
+} cc_media_cap_t;
+
+typedef struct cc_media_cap_table_t_ {
+    uint32_t        id;
+    cc_media_cap_t  cap[CC_MAX_MEDIA_CAP];/* capability table.             */
+} cc_media_cap_table_t;
+
+typedef struct cc_media_track_t_ {
+    unsigned int    media_stream_track_id;
+    boolean         video;
+} cc_media_track_t;
+
+typedef struct cc_media_remote_track_table_t_ {
+    uint32_t          num_tracks;
+    uint32_t          media_stream_id;
+    cc_media_track_t  track[CC_MAX_TRACKS];
+} cc_media_remote_track_table_t;
+
+typedef struct cc_media_remote_stream_table_t_ {
+    cc_media_remote_track_table_t  streams[CC_MAX_STREAMS];
+} cc_media_remote_stream_table_t;
+
+typedef struct cc_media_local_track_table_t_ {
+    uint32_t          media_stream_id;
+    cc_media_track_t  track[CC_MAX_TRACKS];
+} cc_media_local_track_table_t;
+
+typedef struct cc_feature_data_generic_t {
+    boolean subref_flag;
+    uint32_t     eventid;
+} cc_feature_data_generic_t;
+
+typedef struct cc_feature_data_cnf_t_ {
+    cc_causes_t cause;
+    callid_t    call_id;
+    callid_t    target_call_id;
+} cc_feature_data_cnf_t;
+
+typedef struct cc_feature_data_cancel_t_ {
+    cc_rcc_skey_evt_type_e cause;
+    callid_t    call_id;
+    callid_t    target_call_id;
+} cc_feature_data_cancel_t;
+
+typedef struct cc_feature_data_pc_t_ {
+  char pc_handle[PC_HANDLE_SIZE];
+} cc_feature_data_pc_t;
+
+typedef struct cc_feature_data_track_t_ {
+  cc_media_stream_id_t stream_id;
+  cc_media_track_id_t  track_id;
+  cc_media_type_t      media_type;
+} cc_feature_data_track_t;
+
+
+typedef struct cc_feature_candidate_t_ {
+  uint16_t    level;
+  char        candidate[CANDIDATE_SIZE];
+  char        mid[MID_SIZE];
+} cc_feature_candidate_t;
+
+typedef struct cc_feature_session_t_ {
+  unsigned int  sessionid;
+  cc_boolean    has_constraints;
+} cc_feature_session_t;
+
+
+typedef union cc_feature_data_t {
+    cc_feature_data_newcall_t   newcall;
+    cc_feature_data_xfer_t      xfer;
+    cc_feature_data_ind_t       indication;
+    cc_feature_data_endcall_t   endcall;
+    cc_feature_data_hold_t      hold;
+    cc_feature_data_hold_reversion_t      hold_reversion;
+    cc_feature_data_resume_t    resume;
+    cc_feature_data_redirect_t  redirect;
+    cc_feature_data_subscribe_t subscribe;
+    cc_feature_data_notify_t    notify;
+    cc_feature_data_update_t    update;
+    cc_feature_data_b2bcnf_t    b2bconf;
+    cc_feature_data_call_info_t call_info;
+    cc_feature_data_record_t    record;
+    cc_feature_data_select_t    select;
+    cc_feature_data_b2b_join_t  b2bjoin;
+    cc_feature_data_generic_t   generic;
+    cc_feature_data_cnf_t       cnf;
+    cc_feature_data_b2bcnf_t    cancel;
+    cc_media_cap_t              caps;
+    cc_feature_data_pc_t        pc;
+    cc_feature_data_track_t     track;
+    cc_feature_candidate_t      candidate;
+    cc_feature_session_t        session;
+} cc_feature_data_t;
+
+typedef struct cc_setup_t_ {
+    cc_msgs_t       msg_id;
+    cc_srcs_t       src_id;
+    callid_t        call_id;
+    line_t          line;
+    cc_alerting_type alert_info;
+    vcm_ring_mode_t alerting_ring;
+    vcm_tones_t     alerting_tone;
+    cc_caller_id_t  caller_id;
+    cc_redirect_t   redirect;
+    cc_call_info_t  call_info;
+    boolean         replaces;
+    string_t        recv_info_list;
+    cc_msgbody_info_t msg_body;
+} cc_setup_t;
+
+typedef struct cc_setup_ack_t_ {
+    cc_msgs_t      msg_id;
+    cc_srcs_t      src_id;
+    callid_t       call_id;
+    line_t         line;
+    cc_caller_id_t caller_id;
+    cc_msgbody_info_t    msg_body;
+} cc_setup_ack_t;
+
+typedef struct cc_proceeding_t_ {
+    cc_msgs_t      msg_id;
+    cc_srcs_t      src_id;
+    callid_t       call_id;
+    line_t         line;
+    cc_caller_id_t caller_id;
+} cc_proceeding_t;
+
+typedef struct cc_alerting_t_ {
+    cc_msgs_t      msg_id;
+    cc_srcs_t      src_id;
+    callid_t       call_id;
+    line_t         line;
+    cc_caller_id_t caller_id;
+    cc_msgbody_info_t msg_body;
+    boolean        inband;
+} cc_alerting_t;
+
+typedef struct cc_connected_t_ {
+    cc_msgs_t      msg_id;
+    cc_srcs_t      src_id;
+    callid_t       call_id;
+    line_t         line;
+    cc_caller_id_t caller_id;
+    string_t       recv_info_list;
+    cc_msgbody_info_t msg_body;
+} cc_connected_t;
+
+typedef struct cc_connected_ack_t_ {
+    cc_msgs_t      msg_id;
+    cc_srcs_t      src_id;
+    callid_t       call_id;
+    line_t         line;
+    cc_caller_id_t caller_id;
+    cc_msgbody_info_t msg_body;
+} cc_connected_ack_t;
+
+typedef struct cc_release_t_ {
+    cc_msgs_t   msg_id;
+    cc_srcs_t   src_id;
+    callid_t    call_id;
+    line_t      line;
+    cc_causes_t cause;
+    char        dialstring[CC_MAX_DIALSTRING_LEN];
+    cc_kfact_t  kfactor;
+} cc_release_t;
+
+typedef struct cc_release_complete_t_ {
+    cc_msgs_t   msg_id;
+    cc_srcs_t   src_id;
+    callid_t    call_id;
+    line_t      line;
+    cc_causes_t cause;
+    cc_kfact_t  kfactor;
+} cc_release_complete_t;
+
+typedef struct cc_feature_t_ {
+    cc_msgs_t            msg_id;
+    cc_srcs_t            src_id;
+    callid_t             call_id;
+    line_t               line;
+    cc_features_t        feature_id;
+    cc_feature_data_t    data;
+    boolean              data_valid;
+    cc_jsep_action_t     action;
+    char                 sdp[SDP_SIZE];
+} cc_feature_t;
+
+typedef struct cc_feature_ack_t_ {
+    cc_msgs_t         msg_id;
+    cc_srcs_t         src_id;
+    callid_t          call_id;
+    line_t            line;
+    cc_features_t     feature_id;
+    cc_feature_data_t data;
+    boolean           data_valid;
+    cc_causes_t       cause;
+} cc_feature_ack_t;
+
+typedef struct cc_offhook_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t call_id;
+    line_t   line;
+    char     global_call_id[CC_GCID_LEN];
+    callid_t prim_call_id;                /* For internal new call event
+                                           * refer primary call's call_id
+                                           */
+    cc_hold_resume_reason_e hold_resume_reason; /* Reason for new consult call */
+    monitor_mode_t monitor_mode;
+    cfwdall_mode_t cfwdall_mode;
+} cc_offhook_t;
+
+typedef struct cc_onhook_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t call_id;
+    line_t   line;
+    boolean  softkey;
+    callid_t prim_call_id;                /* For internal new call event
+                                           * refer primary call's call_id
+                                           */
+    cc_hold_resume_reason_e hold_resume_reason; /* Reason for new consult call */
+    cc_onhook_reason_e  active_list;                 /* onhook is because of active call
+                                           * press
+                                           */
+} cc_onhook_t;
+
+typedef struct cc_line_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t  call_id;
+    line_t    line;
+} cc_line_t;
+
+typedef struct cc_digit_begin_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t  call_id;
+    line_t    line;
+    int       digit;
+} cc_digit_begin_t;
+
+typedef struct cc_digit_end_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t  call_id;
+    line_t    line;
+    int       digit;
+} cc_digit_end_t;
+
+typedef struct cc_dialstring_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t  call_id;
+    line_t    line;
+    char      dialstring[CC_MAX_DIALSTRING_LEN];
+    char      g_call_id[CC_GCID_LEN];
+    monitor_mode_t monitor_mode;
+} cc_dialstring_t;
+
+// mostly overlap with sessionTypes.h:cc_mwi_status_t
+typedef struct cc_action_data_mwi_ {
+    boolean on;
+    int     type;
+    int     newCount;
+    int     oldCount;
+    int     hpNewCount;
+    int     hpOldCount;
+} cc_action_data_mwi_t;
+
+typedef struct cc_mwi_t_ {
+    cc_msgs_t            msg_id;
+    cc_srcs_t            src_id;
+    callid_t             call_id;
+    line_t               line;
+    cc_action_data_mwi_t msgSummary;
+} cc_mwi_t;
+
+typedef struct cc_options_sdp_req_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t  call_id;
+    line_t    line;
+    void *    pMessage;
+} cc_options_sdp_req_t;
+
+typedef struct cc_options_sdp_ack_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t  call_id;
+    line_t    line;
+    void *    pMessage;
+    cc_msgbody_info_t msg_body;
+} cc_options_sdp_ack_t;
+
+typedef struct cc_audit_sdp_req_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t call_id;
+    line_t   line;
+    boolean  apply_ringout;
+} cc_audit_sdp_req_t;
+
+typedef struct cc_audit_sdp_ack_t_ {
+    cc_msgs_t msg_id;
+    cc_srcs_t src_id;
+    callid_t  call_id;
+    line_t    line;
+    cc_msgbody_info_t msg_body;
+} cc_audit_sdp_ack_t;
+
+typedef struct cc_feature_tmr_t_ {
+    callid_t      call_id;
+    line_t        line;
+    cc_features_t feature_id;
+} cc_feature_tmr_t;
+
+typedef struct cc_regmgr_t_ {
+    cc_msgs_t       msg_id;
+    cc_srcs_t       src_id;
+    int             rsp_type;
+    cc_regmgr_rsp_e rsp_id;
+    boolean         wait_flag;
+} cc_regmgr_t;
+
+// mostly overlap with sessionTypes.h:session_send_info_t
+typedef struct cc_info_t {
+    cc_msgs_t msg_id;
+    cc_srcs_t not_used; // why not share a common struct??  why cast everything to cc_setup_t??
+    callid_t  call_id;
+    line_t    line;
+    string_t  info_package;
+    string_t  content_type;
+    string_t  message_body;
+} cc_info_t;
+
+typedef struct cc_msg_t_ {
+    union {
+        cc_setup_t            setup;
+        cc_setup_ack_t        setup_ack;
+        cc_proceeding_t       proceeding;
+        cc_alerting_t         alerting;
+        cc_connected_t        connected;
+        cc_connected_ack_t    connected_ack;
+        cc_release_t          release;
+        cc_release_complete_t release_complete;
+        cc_feature_t          feature;
+        cc_feature_ack_t      feature_ack;
+        cc_offhook_t          offhook;
+        cc_onhook_t           onhook;
+        cc_line_t             line;
+        cc_digit_begin_t      digit_begin;
+        cc_digit_end_t        digit_end;
+        cc_dialstring_t       dialstring;
+        cc_mwi_t              mwi;
+        cc_options_sdp_ack_t  options_ack;
+        cc_audit_sdp_ack_t    audit_ack;
+        cc_info_t             info;
+    } msg;
+} cc_msg_t;
+
+
+callid_t    cc_get_new_call_id(void);
+const char *cc_msg_name(cc_msgs_t id);
+const char *cc_src_name(cc_srcs_t id);
+const char *cc_cause_name(cc_causes_t id);
+const char *cc_feature_name(cc_features_t id);
+
+void cc_int_setup(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                  line_t line, cc_caller_id_t *caller_id,
+                  cc_alerting_type alert_info, vcm_ring_mode_t alerting_ring,
+                  vcm_tones_t alerting_tone, cc_redirect_t *redirect,
+                  cc_call_info_t *call_info_p, boolean replaces,
+                  string_t recv_info_list, cc_msgbody_info_t *msg_body);
+
+void cc_int_setup_ack(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                      line_t line, cc_caller_id_t *caller_id,
+                      cc_msgbody_info_t *msg_body);
+
+void cc_int_proceeding(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                       line_t line, cc_caller_id_t *caller_id);
+
+void cc_int_alerting(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                     line_t line, cc_caller_id_t *caller_id,
+                     cc_msgbody_info_t *msg_body, boolean inband);
+
+void cc_int_connected(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                      line_t line, cc_caller_id_t *caller_id,
+                      string_t recv_info_list, cc_msgbody_info_t *msg_body);
+
+void cc_int_connected_ack(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                          line_t line, cc_caller_id_t *caller_id,
+                          cc_msgbody_info_t *msg_body);
+
+void cc_int_release(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                    line_t line, cc_causes_t cause, const char *dialstring,
+                    cc_kfact_t *kfactor);
+
+void cc_int_release_complete(cc_srcs_t src_id, cc_srcs_t dst_id,
+                             callid_t call_id, line_t line, cc_causes_t cause,
+                             cc_kfact_t *kfactor);
+
+void cc_int_feature2(cc_msgs_t msg_id, cc_srcs_t src_id, cc_srcs_t dst_id,
+                    callid_t call_id, line_t line, cc_features_t feature_id,
+                    cc_feature_data_t *data);
+
+void cc_createoffer(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                    line_t line, cc_features_t feature_id, cc_feature_data_t *data);
+
+void cc_createanswer (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                    line_t line, cc_features_t feature_id, string_t sdp, cc_feature_data_t *data);
+
+void cc_setlocaldesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
+                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp, cc_feature_data_t *data);
+
+void cc_setremotedesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
+                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp, cc_feature_data_t *data);
+
+void cc_localdesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
+                    cc_features_t feature_id, cc_feature_data_t *data);
+
+void cc_remotedesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
+                    cc_features_t feature_id, cc_feature_data_t *data);
+
+void cc_int_feature_ack(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                        line_t line, cc_features_t feature_id,
+                        cc_feature_data_t *data, cc_causes_t cause);
+
+void cc_int_offhook(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t prim_call_id,
+                    cc_hold_resume_reason_e consult_reason, callid_t call_id,
+                    line_t line, char *global_call_id,
+                    monitor_mode_t monitor_mode,
+                    cfwdall_mode_t cfwdall_mode);
+
+void cc_int_onhook(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t prim_call_id,
+                   cc_hold_resume_reason_e consult_reason, callid_t call_id,
+                   line_t line, boolean softkey, cc_onhook_reason_e active_list);
+
+void cc_int_line(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                 line_t line);
+
+void cc_int_digit_begin(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                        line_t line, int digit);
+
+void cc_int_digit_end(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                      line_t line, int digit);
+
+void cc_int_dialstring(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                       line_t line, const char *dialstring,
+                       const char *g_call_id, monitor_mode_t monitor_mode);
+
+void cc_int_mwi(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                line_t line, boolean on, int type, int newCount,
+                int oldCount, int hpNewCount, int hpOldCount);
+
+void cc_int_options_sdp_req(cc_srcs_t src_id, cc_srcs_t dst_id,
+                            callid_t call_id, line_t line, void *pMessage);
+
+void cc_int_options_sdp_ack(cc_srcs_t src_id, cc_srcs_t dst_id,
+                            callid_t call_id, line_t line, void *pMessage,
+                            cc_msgbody_info_t *msg_body);
+
+void cc_int_audit_sdp_req(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                          line_t line, boolean apply_ringout);
+
+void cc_int_audit_sdp_ack(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                          line_t line, cc_msgbody_info_t *msg_body);
+
+void cc_int_info(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
+                      line_t line, string_t info_package, string_t content_type,
+                      string_t message_body);
+
+void cc_int_fail_fallback(cc_srcs_t src_id, cc_srcs_t dst_id, int rsp_type,
+                          cc_regmgr_rsp_e rsp_id, boolean waited);
+#define cc_fail_fallback_sip(a, b, c, d)     cc_int_fail_fallback(a, CC_SRC_SIP, b, c, d)
+#define cc_fail_fallback_gsm(a, b, c)     cc_int_fail_fallback(a, CC_SRC_GSM, b, c, FALSE)
+
+#define cc_setup(a, b, c, d, e, f, g, h, i, j, k, l) cc_int_setup(a, CC_SRC_GSM, b, c, d, e, f, g, h, i, j, k, l)
+#define cc_setup_ack(a, b, c, d, e) \
+        cc_int_setup_ack(a, CC_SRC_GSM, b, c, d, e)
+#define cc_proceeding(a, b, c, d)     cc_int_proceeding(a, CC_SRC_GSM, b, c, d)
+#define cc_alerting(a, b, c, d, e, f) \
+        cc_int_alerting(a, CC_SRC_GSM, b, c, d, e, f)
+#define cc_connected(a, b, c, d, e, f) \
+        cc_int_connected(a, CC_SRC_GSM, b, c, d, e, f)
+#define cc_connected_ack(a, b, c, d, e) \
+        cc_int_connected_ack(a, CC_SRC_GSM, b, c, d, e)
+#define cc_release(a, b, c, d, e, f)     cc_int_release(a, CC_SRC_GSM, b, c, d, e, f)
+#define cc_release_complete(a, b, c, d, e) \
+        cc_int_release_complete(a, CC_SRC_GSM, b, c, d, e)
+#define cc_feature(a, b, c, d, e)     cc_int_feature2(CC_MSG_FEATURE, a, CC_SRC_GSM, b, c, d, e)
+#define cc_int_feature(a, b, c, d, e, f)     cc_int_feature2(CC_MSG_FEATURE, a, b, c, d, e, f)
+#define cc_feature_ack(a, b, c, d, e, f) \
+        cc_int_feature_ack(a, CC_SRC_GSM, b, c, d, e, f)
+#define cc_offhook(a, b, c)           cc_int_offhook(a, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE, b, c, NULL, CC_MONITOR_NONE,CFWDALL_NONE)
+#define cc_offhook_ext(a, b, c, d, e) cc_int_offhook(a, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE, b, c, d, e,CFWDALL_NONE)
+#define cc_onhook(a, b, c, d)         cc_int_onhook(a, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE, b, c, d, CC_REASON_NULL)
+#define cc_onhook_ext(a, b, c, d, e)  cc_int_onhook(a, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE, b, c, d, e)
+#define cc_line(a, b, c)              cc_int_line(a, CC_SRC_GSM, b, c)
+#define cc_digit_begin(a, b, c, d)    cc_int_digit_begin(a, CC_SRC_GSM, b, c, d)
+#define cc_dialstring(a, b, c, d)     cc_int_dialstring(a, CC_SRC_GSM, b, c, d, NULL, CC_MONITOR_NONE)
+#define cc_dialstring_ext(a, b, c, d, e, f)     cc_int_dialstring(a, CC_SRC_GSM, b, c, d, e, f)
+#define cc_mwi(a, b, c, d, e, f, g, h, i)            cc_int_mwi(a, CC_SRC_GSM, b, c, d, e, f, g, h, i )
+#define cc_options_sdp_req(a, b, c, d)   cc_int_options_sdp_req(a, CC_SRC_GSM, b, c, d)
+#define cc_audit_sdp_req(a, b, c, d)     cc_int_audit_sdp_req(a, CC_SRC_GSM, b, c, d)
+
+typedef enum cc_types_t_ {
+    CC_TYPE_INVALID,
+    CC_TYPE_CCM,
+    CC_TYPE_OTHER
+} cc_types_t;
+
+typedef enum cc_states_t_ {
+    CC_STATE_MIN = -1,
+    CC_STATE_OFFHOOK,
+    CC_STATE_DIALING,
+    CC_STATE_DIALING_COMPLETED,
+    CC_STATE_CALL_SENT,
+    CC_STATE_FAR_END_PROCEEDING,
+    CC_STATE_FAR_END_ALERTING,
+    CC_STATE_CALL_RECEIVED,
+    CC_STATE_ALERTING,
+    CC_STATE_ANSWERED,
+    CC_STATE_CONNECTED,
+    CC_STATE_HOLD,
+    CC_STATE_RESUME,
+    CC_STATE_ONHOOK,
+    CC_STATE_CALL_FAILED,
+    CC_STATE_HOLD_REVERT,
+    CC_STATE_UNKNOWN,
+    CC_STATE_MAX
+} cc_states_t;
+
+/* Update cc_action_names structure in lsm.c with the
+ * corresponding change for the following structure.
+ */
+typedef enum cc_actions_t_ {
+    CC_ACTION_MIN = -1,
+    CC_ACTION_SPEAKER,
+    CC_ACTION_DIAL_MODE,
+    CC_ACTION_MWI,
+    CC_ACTION_MWI_LAMP_ONLY,
+    CC_ACTION_OPEN_RCV,
+    CC_ACTION_UPDATE_UI,
+    CC_ACTION_MEDIA,
+    CC_ACTION_RINGER,
+    CC_ACTION_SET_LINE_RINGER,
+    CC_ACTION_PLAY_TONE,
+    CC_ACTION_STOP_TONE,
+    CC_ACTION_STOP_MEDIA,
+    CC_ACTION_START_RCV,
+    CC_ACTION_ANSWER_PENDING,
+    CC_ACTION_PLAY_BLF_ALERTING_TONE,
+    CC_ACTION_MAX
+} cc_actions_t;
+
+typedef enum cc_services_t_ {
+    CC_SERVICE_MIN = -1,
+    CC_SERVICE_MAX
+} cc_services_t;
+
+typedef enum cc_update_ui_actions_t_ {
+    CC_UPDATE_MIN = -1,
+    CC_UPDATE_CONF_ACTIVE,
+    CC_UPDATE_CONF_RELEASE,
+    CC_UPDATE_XFER_PRIMARY,
+    CC_UPDATE_CALLER_INFO,
+    CC_UPDATE_SECURITY_STATUS,
+    CC_UPDATE_SET_CALL_STATUS,
+    CC_UPDATE_CLEAR_CALL_STATUS,
+    CC_UPDATE_SET_NOTIFICATION,
+    CC_UPDATE_CLEAR_NOTIFICATION,
+    CC_UPDATE_CALL_PRESERVATION,
+    CC_UPDATE_CALL_CONNECTED,
+    CC_UPDATE_MAX
+} cc_update_ui_actions_t;
+
+typedef struct cc_state_data_offhook_t_ {
+    cc_caller_id_t caller_id;
+    int dial_mode;
+} cc_state_data_offhook_t;
+
+/*
+ * This structure is passed in CC_STATE_DIALING to lsm to indicate
+ * wether a dialtone needs to be played or not
+ * should the stutter dial tone be suppressed or not
+ */
+typedef struct cc_state_data_dialing_t_ {
+    boolean play_dt;
+    boolean suppress_stutter;
+} cc_state_data_dialing_t;
+
+typedef struct cc_state_data_dialing_completedt_ {
+    cc_caller_id_t caller_id;
+} cc_state_data_dialing_completed_t;
+
+typedef struct cc_state_data_call_sent_t_ {
+    cc_caller_id_t caller_id;
+} cc_state_data_call_sent_t;
+
+typedef struct cc_state_data_far_end_proceeding_t_ {
+    cc_caller_id_t caller_id;
+} cc_state_data_far_end_proceeding_t;
+
+typedef struct cc_state_data_far_end_alerting_t_ {
+    cc_caller_id_t caller_id;
+} cc_state_data_far_end_alerting_t;
+
+typedef struct cc_state_data_call_received_t_ {
+    cc_caller_id_t caller_id;
+} cc_state_data_call_received_t;
+
+typedef struct cc_state_data_alerting_t_ {
+    cc_caller_id_t caller_id;
+} cc_state_data_alerting_t;
+
+typedef struct cc_state_data_answered_t_ {
+    cc_caller_id_t caller_id;
+} cc_state_data_answered_t;
+
+typedef struct cc_state_data_connected_t_ {
+    cc_caller_id_t caller_id;
+} cc_state_data_connected_t;
+
+typedef struct cc_state_data_hold_t_ {
+    cc_caller_id_t          caller_id;
+    boolean                 local;
+    cc_hold_resume_reason_e reason;
+} cc_state_data_hold_t;
+
+typedef struct cc_state_data_resume_t_ {
+    cc_caller_id_t caller_id;
+    boolean        local;
+} cc_state_data_resume_t;
+
+typedef struct cc_state_data_onhook_t_ {
+    cc_caller_id_t caller_id;
+    boolean        local;
+    cc_causes_t    cause;
+} cc_state_data_onhook_t;
+
+typedef struct cc_state_data_call_failed_t_ {
+    cc_caller_id_t caller_id;
+    cc_causes_t    cause;
+} cc_state_data_call_failed_t;
+
+typedef union cc_state_data_t_ {
+    cc_state_data_offhook_t            offhook;
+    cc_state_data_dialing_t            dialing;
+    cc_state_data_dialing_completed_t  dialing_completed;
+    cc_state_data_call_sent_t          call_sent;
+    cc_state_data_far_end_proceeding_t far_end_proceeding;
+    cc_state_data_far_end_alerting_t   far_end_alerting;
+    cc_state_data_call_received_t      call_received;
+    cc_state_data_alerting_t           alerting;
+    cc_state_data_answered_t           answered;
+    cc_state_data_connected_t          connected;
+    cc_state_data_hold_t               hold;
+    cc_state_data_resume_t             resume;
+    cc_state_data_onhook_t             onhook;
+    cc_state_data_call_failed_t        call_failed;
+} cc_state_data_t;
+
+typedef struct cc_action_data_digit_begin_ {
+    int tone;
+} cc_action_data_digit_begin_t;
+
+typedef struct cc_action_data_tone_ {
+    vcm_tones_t tone;
+} cc_action_data_tone_t;
+
+typedef struct cc_action_data_speaker_ {
+    boolean on;
+} cc_action_data_speaker_t;
+
+typedef struct cc_action_data_dial_mode_ {
+    int mode;
+    int digit_cnt;
+} cc_action_data_dial_mode_t;
+
+/*
+typedef struct cc_action_data_mwi_ {
+    boolean on;
+    int32_t type;
+    int32_t newCount;
+    int32_t oldCount;
+    int32_t hpNewCount;
+    int32_t hpOldCount;
+} cc_action_data_mwi_t;
+*/
+
+typedef struct cc_action_data_open_rcv_ {
+    boolean  is_multicast;
+    cpr_ip_addr_t listen_ip;
+    uint16_t port;
+    boolean  rcv_chan;
+    boolean  keep;
+    media_refid_t media_refid; /* the ID of the media to reference to */
+    sdp_media_e media_type;
+} cc_action_data_open_rcv_t;
+
+typedef struct cc_set_call_status_data_ {
+    char *phrase_str_p;
+    int timeout;
+    callid_t call_id;
+    line_t line;
+} cc_set_call_status_data_t;
+
+typedef struct cc_clear_call_status_data_ {
+    callid_t call_id;
+    line_t line;
+} cc_clear_call_status_data_t;
+
+// mostly overlap with sessionTypes.h:cc_notification_data_t
+typedef struct cc_set_notification_data_ {
+    char *phrase_str_p;
+    unsigned long timeout;
+    unsigned long priority;
+} cc_set_notification_data_t;
+
+typedef union cc_update_ui_data_ {
+    cc_feature_data_call_info_t caller_info;
+    cc_set_call_status_data_t   set_call_status_parms;
+    cc_clear_call_status_data_t clear_call_status_parms;
+    cc_set_notification_data_t  set_notification_parms;
+    string_t security_status;
+} cc_update_ui_data_t;
+
+typedef struct cc_action_data_update_ui_ {
+    cc_update_ui_actions_t action;
+    cc_update_ui_data_t    data;
+} cc_action_data_update_ui_t;
+
+typedef struct cc_action_data_ringer_ {
+    boolean on;
+} cc_action_data_ringer_t;
+
+typedef struct cc_action_data_stop_media_ {
+     media_refid_t  media_refid; /* the ID of the media to reference to */
+} cc_action_data_stop_media_t;
+
+typedef union cc_action_data_t_ {
+    cc_action_data_digit_begin_t digit_begin;
+    cc_action_data_tone_t      tone;
+    cc_action_data_speaker_t   speaker;
+    cc_action_data_dial_mode_t dial_mode;
+    cc_action_data_mwi_t       mwi;
+    cc_action_data_open_rcv_t  open_rcv;
+    cc_action_data_update_ui_t update_ui;
+    cc_action_data_ringer_t    ringer;
+    cc_action_data_stop_media_t stop_media;
+} cc_action_data_t;
+
+typedef struct cc_service_data_get_facility_by_line_ {
+    line_t line;
+} cc_service_data_get_facility_by_line_t;
+
+typedef union cc_service_data_t_ {
+    cc_service_data_get_facility_by_line_t get_facility_by_line;
+} cc_service_data_t;
+
+
+typedef enum {
+    MEDIA_INTERFACE_UPDATE_NOT_REQUIRED,
+    MEDIA_INTERFACE_UPDATE_STARTED,
+    MEDIA_INTERFACE_UPDATE_IN_PROCESS
+} dock_undock_event_t;
+
+extern dock_undock_event_t  g_dock_undock_event;
+
+void cc_call_state(callid_t call_id, line_t line, cc_states_t state,
+                   cc_state_data_t *data);
+cc_rcs_t cc_call_action(callid_t call_id, line_t line, cc_actions_t action,
+                    cc_action_data_t *data);
+cc_rcs_t cc_call_service(callid_t call_id, line_t line, cc_services_t service,
+                         cc_service_data_t *data);
+void cc_call_attribute(callid_t call_id, line_t line, call_attr_t attr);
+void cc_init(void);
+void cc_free_msg_body_parts(cc_msgbody_info_t *msg_body);
+void cc_free_msg_data(cc_msg_t *msg);
+void cc_initialize_msg_body_parts_info(cc_msgbody_info_t *msg_body);
+void cc_mv_msg_body_parts(cc_msgbody_info_t *dst_msg,
+                          cc_msgbody_info_t *src_msg);
+cc_rcs_t cc_cp_msg_body_parts(cc_msgbody_info_t *dst_msg,
+                              cc_msgbody_info_t *src_msg);
+void cc_mv_caller_id(cc_caller_id_t *dst_caller, cc_caller_id_t *src_caller);
+  //function that will be invoked by modules external to gsm:
+
+int cc_is_cnf_call(callid_t call_id);
+cc_transfer_mode_e cc_is_xfr_call(callid_t call_id);
+
+extern cprBuffer_t cc_get_msg_buf(int min_size);
+extern const char *cc_feature_name(cc_features_t id);
+extern const char *cc_msg_name(cc_msgs_t id);
+extern const char *cc_cause_name(cc_causes_t id);
+extern void cc_media_update_native_video_support(boolean val);
+extern void cc_media_update_video_cap(boolean val);
+extern void cc_media_update_native_video_txcap(boolean val);
+extern cc_boolean cc_media_isTxCapEnabled();
+extern cc_boolean cc_media_isVideoCapEnabled();
+extern void cc_media_setVideoAutoTxPref(cc_boolean txPref);
+extern cc_boolean cc_media_getVideoAutoTxPref();
+#endif
diff --git a/libs/sipcc/core/includes/check_sync.h b/libs/sipcc/core/includes/check_sync.h
new file mode 100644 (file)
index 0000000..c5dd805
--- /dev/null
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CHECK_SYNC_H
+#define CHECK_SYNC_H
+
+#include "util_parse.h"
+
+typedef enum {
+    SYNCS_ANY,
+    SYNCS_GOT_VERSION,
+    SYNCS_GOT_VERSION_EQ,
+    SYNCS_GOT_SYNC,
+    SYNCS_GOT_SYNC_EQ,
+    SYNCS_GOT_GENERIC,
+    SYNCS_GOT_GENERIC_EQ
+} sync_states;
+
+typedef enum {
+    SYNCF_NONE,
+    SYNCF_MY,
+    SYNCF_WILD
+} SyncVersionFound_t;
+
+typedef struct {
+    char myVersion[MAX_LOAD_ID_STRING];
+    char resetSync[MAX_SYNC_LEN];
+    SyncVersionFound_t versionFound;
+} VersionSync_t;
+
+#define SYNCINFO_XML    "syncinfo.xml"
+#define SYNC_BUF_SIZE   4096
+
+extern int CheckSync;
+extern int SyncInfoInProgress;
+extern char *SyncBuffer;
+
+int check_sync_get(void);
+void add_sync_info(char *version, char *sync, VersionSync_t *versionSync);
+int parse_sync_entry(char **parseptr, VersionSync_t *versionSync);
+int parse_sync_info(char *parseptr, VersionSync_t *versionSync);
+void process_sync_info(char *syncInfo);
+short handle_sync_message(short cmd, void *pData);
+
+#endif
diff --git a/libs/sipcc/core/includes/ci.h b/libs/sipcc/core/includes/ci.h
new file mode 100644 (file)
index 0000000..e5d014c
--- /dev/null
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CI_INCLUDED_H
+#define _CI_INCLUDED_H
+
+#include "plat_api.h"
+#include "plat_debug.h"
+
+
+/* return codes for CI command processing */
+#define CI_OK             (0)
+#define CI_ERROR          (1)
+#define CI_INVALID        (2)
+#define CI_AMBIGUOUS      (3)
+
+/* flags for CI processing */
+#define CI_PROMPT         (0x0001)
+
+/*
+ * Prototypes for public functions
+ */
+void ci_init();
+int ci_process_input(const char *str, char *wkspace, int wklen);
+int32_t ci_show_cmds(int32_t argc, const char *argv[]);
+ci_callback ci_set_interceptor(ci_callback func);
+
+int ci_err_too_few(void);        /* "Too few arguments"   */
+int ci_err_too_many(void);       /* "Too many arguments"  */
+int ci_err_inv_arg(void);        /* "Invalid argument"    */
+uint32_t ci_streval(const char *str);
+
+#endif /* _CI_INCLUDED_H */
diff --git a/libs/sipcc/core/includes/config.h b/libs/sipcc/core/includes/config.h
new file mode 100755 (executable)
index 0000000..9dc31b9
--- /dev/null
@@ -0,0 +1,196 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+#include "cpr_types.h"
+#include "cpr_timers.h"
+#include "cfgfile_utils.h"
+#include "configmgr.h"
+#include "prot_configmgr.h"
+#include "phone.h"
+
+/*
+ * List of timers that the CFG task is responsible for.
+ * CPR will send a msg to the CFG task when these
+ * timers expire. CPR expects a timer id when the timer
+ * is created, this enum serves that purpose.
+ */
+typedef enum {
+    CFG_TFTP_RETRY_TIMER
+} cfgTimerList_t;
+
+#define MAX_REQ_FIELDS          20
+#define MAX_FIELD_NAME_SIZE     30
+#define MAX_FIELD_VALUE_SIZE    30
+#define MAX_FIELDDESC_STR       (MAX_FIELD_VALUE_SIZE+2)
+#define MAX_TFTP_PATH_LEN       64
+
+/*
+ * Definition of bits used in pDHCPInfo->extFields.ext.appStatus (32bits).
+ * Please note that some of these flags correspond to status flags
+ * asserted by Little App.  Please see little_app.c, and make sure these
+ * flags are kept consistent.
+ */
+
+#define APPSTATUS_UPGRADE_FAILED  0x80000000L
+
+/* maximum length of config file including comments */
+#define MAX_CFG_FILE_LENGTH 0x2000
+
+/* minimum length of a registrion in seconds */
+#define MIN_REGISTRATION_PERIOD  20
+
+#define BUF_TEST();
+
+typedef enum {
+    ERASE_PROGRAM,
+    ERASE_ONLY,
+    PROGRAM_ONLY
+} flash_mode_t;
+
+extern var_t prot_cfg_table[];
+extern cfg_rom_t *const prot_startup_config;
+extern cfg_rom_t *const prot_running_config;
+extern cfg_rom_t *const prot_temp_config;
+extern int config_commit(int);
+extern int prot_sanity_check_config_settings(void);
+extern boolean prot_option_allowed_in_cfg_file(const char *);
+extern void prot_shutdown(void);
+extern int prot_config_change_notify(int);
+extern void prot_disconnected(int);
+extern int FlashaProgram(uint16_t *Source, uint16_t *Dest, uint32_t Size,
+                         flash_mode_t mode);
+
+extern uint16_t g_NewVlan;
+extern uint16_t g_NewRelease;
+extern uint16_t g_NewErase;
+extern char local_media_type_string[];
+extern char local_net_dev_type_string[];
+
+void CFGTftpTimeout(int /*cpr_timer_t*/ *tmr);
+int CFGProgramFlash(uint8_t *src, uint8_t *dst, uint32_t len);
+int CFGEraseFlash(uint8_t *src, uint8_t *dst, uint32_t len);
+int CFGEraseProgramFlash(uint8_t *src, uint8_t *dst, uint32_t len);
+int CFGChksum(uint8_t *ptr, uint32_t len, uint32_t *csum, int dspmode,
+              int cmpmode);
+int CFGGetUISettings(uint8_t *pData);
+int CFGSetUISettings(void);
+void CFGSetLockState(boolean);
+boolean CFGIsLocked(void);
+void cfg_set_running_config(void);
+void cfg_get_stored_prot_settings(void);
+void LoadTempConfigData(void);
+void config_handle_cdp(int);
+void cfg_sanity_check_media_range(void);
+void cfg_check_la_appStatus(unsigned long appStatus);
+void cfg_set_inhibitLoading(int yesno);
+int cfg_get_inhibitLoading(void);
+
+
+/////////////////////////////////////////////////////////////
+//  Configuration Variables replacing CUCM config file
+//
+////
+//////
+
+static const int gStartMediaPort = 16384;
+static const int gStopMediaPort = 32766;
+static const boolean gCallerIdBlocking = FALSE;
+static const boolean gAnonblock = FALSE;
+static const char gPreferredCodec[] = "none";
+static const char gDtmfOutOfBand[] = "avt";
+static const int gDtmfAvtPayload = 101;
+static const int gDtmfDbLevel = 3;
+static const int gSipRetx = 10;
+static const int gSipInviteRetx = 6;
+static const int gTimerT1 = 500;
+static const int gTimerT2 = 4000;
+static const int gTimerInviteExpires = 180;
+static const int gTimerRegisterExpires = 3600;
+static const boolean gRegisterWithProxy = TRUE;
+static const char gBackupProxy[] = "USECALLMANAGER";
+static const int gBackupProxyPort = 5060;
+static const char gEmergencyProxy[] = "USECALLMANAGER";
+static const int gEmergencyProxyPort = 5060;
+static const char gOutboundProxy[] = "USECALLMANAGER";
+static const int gOutboundProxyPort = 5060;
+static const boolean gNatRecievedProcessing = FALSE;
+static const char gUserInfo[] = "None";
+static const boolean gRemotePartyID = TRUE;
+static const boolean gSemiAttendedTransfer = TRUE;
+static const int gCallHoldRingback = 2;
+static const boolean gStutterMsgWaiting = FALSE;
+static const char gCallForwardURI[] = "x-cisco-serviceuri-cfwdall";
+static const boolean gCallStats = TRUE;
+static const int gTimerRegisterDelta = 5;
+static const int gMaxRedirects = 70;
+static const boolean gRfc2543Hold = FALSE;
+static const boolean gLocalCfwdEnable = TRUE;
+static const int gConnectionMonitorDuration = 120;
+static const int gCallLogBlfEnabled = 3 & 0x1;
+static const boolean gRetainForwardInformation = FALSE;
+static const int gRemoteCcEnable = 1;
+static const int gTimerKeepAliveExpires = 120;
+static const int gTimerSubscribeExpires = 120;
+static const int gTimerSubscribeDelta = 5;
+static const int gKpml = 3;
+static const boolean gNatEnabled = FALSE;
+static const char gNatAddress[] = "";
+static const boolean gAnableVad = FALSE;
+static const boolean gAutoAnswerAltBehavior = FALSE;
+static const int gAutoAnswerTimer = 1;
+static const boolean gAutoAnswerOverride = TRUE;
+static const int gOffhookToFirstDigitTimer = 15000;
+static const int gSilentPeriodBetweenCallWaitingBursts = 10;
+static const int gRingSettingBusyStationPolicy = 0;
+static const int gBlfAudibleAlertSettingOfIdleStation = 0;
+static const int gBlfAudibleAlertSettingOfBusyStation = 0;
+static const int gJoinAcrossLines = 0;
+static const boolean gCnfJoinEnabled = TRUE;
+static const int gRollover = 0;
+static const boolean gTransferOnhookEnabled = FALSE;
+static const int gDscpForAudio = 184;
+static const int gDscpVideo = 136;
+static const int gT302Timer = 5000;
+static const int gLineIndex = 1;
+static const int gFeatureID = 9;
+static const char gProxy[] = "USECALLMANAGER";
+static const int gPort = 5060;
+static const char gDisplayName[] = "";
+static const char gMessagesNumber[] = "";
+static const boolean gCallerName = TRUE;
+static const boolean gCallerNumber = FALSE;
+static const boolean gRedirectedNumber = FALSE;
+static const boolean gDialedNumber = TRUE;
+static const unsigned char gMessageWaitingLampPolicy = 3;
+static const unsigned char gMessageWaitingAMWI = 1;
+static const unsigned char gRingSettingIdle = 4;
+static const unsigned char gRingSettingActive = 5;
+static const int gMaxNumCalls = 1;
+static const int gBusyTrigger = 1;
+static const unsigned char gAutoAnswerEnabled = 2 & 0x1;
+static const unsigned char gCallWaiting = 3 & 0x1;
+static const int gDeviceSecurityMode = 1;
+static const int gCcm2_sip_port = 5060;
+static const int gCcm3_sip_port = 5060;
+static const boolean gCcm1_isvalid = TRUE;
+static const int gDscpCallControl = 1;
+static const int gSpeakerEnabled = 1;
+static const char gExternalNumberMask[] = "";
+static const char gVersion[] = "0.1";
+static const boolean gRTCPMUX = FALSE;
+static boolean gRTPSAVPF = TRUE;           /* TRUE = RTP/SAVPF , FALSE = RTP/SAVP */
+static const boolean gMAXAVBITRATE = FALSE;      /* Following six are OPUS fmtp options */
+static const boolean gMAXCODEDAUDIOBW = FALSE;
+static const boolean gUSEDTX = FALSE;
+static const boolean gSTEREO = FALSE;
+static const boolean gUSEINBANDFEC = FALSE;
+static const boolean gCBR = FALSE;
+static const boolean gMAXPTIME = FALSE;
+static const int gSCTPPort = 5000;
+static const int gNumDataStreams = 16;
+
+#endif /* _CONFIG_H_ */
diff --git a/libs/sipcc/core/includes/configapp.h b/libs/sipcc/core/includes/configapp.h
new file mode 100644 (file)
index 0000000..fae4092
--- /dev/null
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CONFIGAPP_H
+#define CONFIGAPP_H
+
+extern void configapp_init();
+extern void configapp_shutdown();
+extern void configapp_process_msg(uint32_t cmd, void *msg);
+
+#endif
+
diff --git a/libs/sipcc/core/includes/configmgr.h b/libs/sipcc/core/includes/configmgr.h
new file mode 100755 (executable)
index 0000000..3c0b4be
--- /dev/null
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CONFIGMGR_H_
+#define _CONFIGMGR_H_
+
+/*
+ * #defines for maximum length of the image names
+ * The MAX_LOAD_ID_LENGTH must be larger (or the same as)
+ * MAX_OLD_LOAD_ID_LENGTH or strange things may
+ * happen because they are used as indexes into
+ * arrays.
+ */
+#include "cpr_types.h"
+#define MAX_LOAD_ID_LENGTH     60
+#define MAX_LOAD_ID_STRING     MAX_LOAD_ID_LENGTH + 1
+#define MAX_OLD_LOAD_ID_LENGTH  8
+#define MAX_OLD_LOAD_ID_STRING  MAX_OLD_LOAD_ID_LENGTH + 1
+#define MAX_URL_LENGTH 128
+
+#define MAX_SYNC_LEN       33
+#define MAX_PHONE_LABEL    32
+
+/* Start of section for new "protocol-inspecific" calls */
+
+#define MAX_CONFIG_STRING_NAME  64
+#define DEFAULT_LINE 1
+
+#define YESSTR    "YES"
+#define NOSTR     "NO"
+#define IPOFZEROS "0.0.0.0"
+
+enum ACTIONATTR {
+    AA_IGNORE   = 0,
+    AA_COMMIT   = 1,
+    AA_RELOAD   = 1 << 1,
+    AA_REGISTER = 1 << 2,
+    AA_FORCE    = 1 << 3,
+    AA_RESET    = 1 << 4,
+    AA_SETTINGS = 1 << 5,
+    AA_BU_REG   = 1 << 6
+};
+
+/*********************************************************
+ *
+ *  External Function Prototypes
+ *
+ *********************************************************/
+void config_get_string(int id, char *buffer, int buffer_len);
+void config_set_string(int id, char *buffer);
+void config_get_value(int id, void *buffer, int length);
+void config_set_value(int id, void *buffer, int length);
+
+void config_get_line_string(int id, char *buffer, int line, int buffer_len);
+void config_set_line_string(int id, char *buffer, int line);
+void config_get_line_value(int id, void *buffer, int length, int line);
+void config_set_line_value(int id, void *buffer, int length, int line);
+
+void config_init(void);
+
+#endif /* _CONFIGMGR_H_ */
diff --git a/libs/sipcc/core/includes/debug.h b/libs/sipcc/core/includes/debug.h
new file mode 100644 (file)
index 0000000..b9197c8
--- /dev/null
@@ -0,0 +1,107 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _DEBUG_INCLUDED_H  /* allows multiple inclusion */
+#define _DEBUG_INCLUDED_H
+
+#include "cpr_types.h"
+#include "plat_api.h"
+#include "phone_debug.h"
+#include "CSFLog.h"
+
+typedef cc_int32_t (*debug_callback)(cc_int32_t argc, const char *argv[]);
+typedef cc_int32_t (*show_callback)(cc_int32_t argc, const char *argv[]);
+typedef cc_int32_t (*clear_callback)(cc_int32_t argc, const char *argv[]);
+
+typedef enum {
+    TEST_OPEN,
+    TEST_CLOSE,
+    TEST_KEY,
+    TEST_ONHOOK,
+    TEST_OFFHOOK,
+    TEST_SHOW,
+    TEST_HIDE,
+    TEST_PROFILE,
+    TEST_C3PO
+} test_command_t;
+
+
+typedef int32_t (*test_callback)(int32_t argc, const char *argv[],
+                                 test_command_t command);
+
+extern int32_t TestMode;
+extern int32_t TestShow;
+
+typedef enum {
+    DEBUG_ENTRY_TYPE_FLAG,
+    DEBUG_ENTRY_TYPE_DEBUG_FUNC,
+    DEBUG_ENTRY_TYPE_SHOW_FUNC
+} debug_entry_type_e;
+
+typedef struct {
+    const char *keyw;
+    union {
+        int32_t *flag;
+        debug_callback func;
+        show_callback show_func;
+    } u;
+    debug_entry_type_e type;
+    boolean show_tech;
+} debug_entry_t;
+
+typedef struct {
+    const char *keyw;
+    union {
+        int32_t *flag;
+        clear_callback func;
+    } u;
+} clear_entry_t;
+
+typedef struct {
+    const char *keyw;
+    const char *abrv;
+    const char *help;
+    boolean hidden;
+    union {
+        int32_t *flag;
+        test_callback func;
+    } u;
+    test_command_t command;
+} test_entry_t;
+
+
+typedef struct {
+    unsigned char flag;
+    unsigned char hookevent;
+    unsigned char keyevent;     // keyevent and key will double as a timer value internally
+    unsigned char key;
+} testevent_t;
+
+// The next 4 defines are used as flag values to specify the event types in the queue.
+// Note that we cannot use the HOOKSCAN and KEYSCAN that are defined in phone.h, because
+// HOOKSCAN requires a full integer. In order to minimize space in the test event queue
+// we need short numbers.
+#define TEST_NONE 0
+#define TEST_KEYSCAN 1
+#define TEST_HOOKSCAN 2
+#define TEST_TIMER 0x80
+
+
+#define MAX_DEBUG_NAME 50
+#define MAX_SHOW_NAME 50
+#define MAX_CLEAR_NAME 50
+
+/*
+ * Prototypes for public functions
+ */
+void bind_test_keyword(const char *keyword, const char *abrv, boolean hidden,
+                       test_callback func, test_command_t command,
+                       const char *help);
+testevent_t TESTGetEvent(void);
+
+// Send debug output to CSFLog
+#define debugif_printf(format, ...) CSFLogDebug("debugif", format, ## __VA_ARGS__ )
+
+
+#endif /* _DEBUG_INCLUDED_H */
diff --git a/libs/sipcc/core/includes/dialplan.h b/libs/sipcc/core/includes/dialplan.h
new file mode 100755 (executable)
index 0000000..ec1f93e
--- /dev/null
@@ -0,0 +1,111 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DIALPLAN_H
+#define DIALPLAN_H
+
+#include <vcm.h>
+#include "phone_types.h"
+
+#define DIALPLAN_MAX_SIZE    0x2000
+#define MAX_SUBTITUTIONS 5
+#define MAX_TONES 3
+#define MAX_TEMPLATE_LENGTH 196
+#define DIAL_ESCAPE      '\\'
+#define MAX_DIALSTRING   256
+#define DIAL_TIMEOUT      10
+#define MAX_DP_VERSION_STAMP_LEN   (64+1)
+extern char g_dp_version_stamp[MAX_DP_VERSION_STAMP_LEN];
+
+typedef enum {
+    DIAL_NOMATCH = 0,
+    DIAL_GIVETONE,
+    DIAL_WILDPATTERN,
+    DIAL_FULLPATTERN,
+    DIAL_FULLMATCH,
+    DIAL_IMMEDIATELY
+} DialMatchAction;
+
+/* Set enum values to match DialMatchAction */
+typedef enum {
+    DIALTONE_NOMATCH = 0,
+    DIALTONE_WILD = 2,
+    DIALTONE_FULL,
+    DIALTONE_EXACT
+} DialToneMatch;
+
+typedef enum {
+    UserUnspec,
+    UserPhone,
+    UserIP
+} UserMode;
+
+typedef enum {
+    RouteDefault,       // Route using the default proxy
+    RouteEmergency,     // Route using the emergency proxy
+    RouteFQDN           // Route according to the FQDN in the entry
+} RouteMode;
+
+struct DialTemplate {
+    struct DialTemplate *next;
+    char *pattern;
+    line_t line;
+    char *rewrite;
+    int timeout;
+    UserMode userMode;
+    RouteMode routeMode;
+    int tones_defined;
+    vcm_tones_t tone[MAX_TONES];
+};
+
+struct StoredDialTemplate {
+    short size;                 // Size of header part of structure
+    short nextOffset;           // total Number of bytes used in the entry
+    //   A zero here is used as a last entry
+    int timeout;
+    line_t line;
+    UserMode userMode;
+    short pattern_offset;       // Offset to the pattern string
+    short rewrite_offset;       // Offset to the rewrite string
+    RouteMode routeMode;
+    int tones_defined;
+    vcm_tones_t tone[MAX_TONES];
+
+};
+
+typedef enum {
+    STATE_ANY,
+    STATE_GOT_MATCH,
+    STATE_GOT_MATCH_EQ,
+    STATE_GOT_LINE,
+    STATE_GOT_LINE_EQ,
+    STATE_GOT_TIMEOUT,
+    STATE_GOT_TIMEOUT_EQ,
+    STATE_GOT_USER,
+    STATE_GOT_USER_EQ,
+    STATE_GOT_REWRITE,
+    STATE_GOT_REWRITE_EQ,
+    STATE_GOT_ROUTE,
+    STATE_GOT_ROUTE_EQ,
+    STATE_GOT_TONE,
+    STATE_GOT_TONE_EQ,
+    STATE_START_TAG_COMPLETED, /* start tag parsing is complete when self-terminating (<tag/>) format is not used */
+    STATE_END_TAG_STARTED,     /* end tag started when we see "</" */
+    STATE_END_TAG_FOUND        /* end tag is parsed */
+} ParseDialState;
+
+extern DialMatchAction MatchDialTemplate(const char *pattern,
+                                         const line_t line,
+                                         int *timeout,
+                                         char *rewrite, int rewritelen,
+                                         RouteMode *pRouteMode,
+                                         vcm_tones_t *pTone);
+void SaveDialTemplate(void);
+void RestoreDialPlan(void);
+void InitDialPlan(boolean);
+void FreeDialTemplates(void);
+extern short handle_dialplan(short, void *);
+extern boolean ParseDialTemplate(char *parseptr);
+
+#endif //DIALPLAN_H
diff --git a/libs/sipcc/core/includes/dialplanint.h b/libs/sipcc/core/includes/dialplanint.h
new file mode 100755 (executable)
index 0000000..bb0c404
--- /dev/null
@@ -0,0 +1,93 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DIALPLANINT_H
+#define DIALPLANINT_H
+
+#include "phone_types.h"
+#include "dialplan.h"
+
+#define BKSPACE_KEY 90
+#define DEFAULT_DIALTIMEOUT 30
+#define CISCO_PLAR_STRING  "x-cisco-serviceuri-offhook"
+
+typedef struct timer_index_ {
+    line_t line;
+    callid_t call_id;
+} timer_index_t;
+
+typedef union timer_info_t_ {
+    void *whole;
+    timer_index_t index;
+} timer_info_t;
+
+/* dialplan interface data structure */
+typedef struct dp_int_t_ {
+    line_t   line;
+    callid_t call_id;
+    char     digit;
+    char     digit_str[MAX_DIALSTRING];
+    boolean  collect_more;
+    void    *tmr_ptr;
+    char     global_call_id[CC_GCID_LEN];
+    monitor_mode_t monitor_mode;
+} dp_int_t;
+
+typedef enum {
+    DP_NONE_TIMER,
+    DP_OFFHOOK_TIMER,
+    DP_INTERDIGIT_TIMER
+} dp_timer_type_e;
+
+typedef struct dp_data_t_ {
+    char         gDialed[MAX_DIALSTRING];
+    char         gReDialed[MAX_DIALSTRING];
+    line_t       gRedialLine;
+    char         empty_rewrite[MAX_DIALSTRING];
+
+    uint32_t     offhook_timeout; /* timeout configured to wait after offhook */
+    void        *dial_timer;
+    boolean      gDialplanDone;
+                 /* Indicates that local dialplan match has been completed */
+    boolean      allow_proceed;
+                 /* under certain conditions proceed has to be passed to
+                  * UI even KPML is enabled. For example redial, which not
+                  * collect more digits so freeze the screen by sending
+                  * proceed to UI
+                  */
+
+    line_t       line; /* Line where which dialing is currently active */
+    callid_t     call_id;        /* call id of currently dialing call */
+    boolean      url_dialing;    /* Indicates dialing is url dialing */
+    timer_info_t timer_info;
+    dp_timer_type_e gTimerType;
+} dp_data_t;
+
+void dp_dial_timeout(void *);
+void dp_int_init_dialing_data(line_t line, callid_t call_id);
+void dp_int_update_key_string(line_t line, callid_t call_id, char *digits);
+void dp_int_store_digit_string(line_t line, callid_t call_id, char *digit_str);
+void dp_int_update_keypress(line_t line, callid_t call_id, unsigned char digit);
+void dp_int_dial_immediate(line_t line, callid_t call_id, boolean collect_more,
+                           char *digit_str, char *g_call_id,
+                           monitor_mode_t monitor_mode);
+void dp_int_do_redial(line_t line, callid_t call_id);
+void dp_int_onhook(line_t line, callid_t call_id);
+void dp_int_offhook(line_t line, callid_t call_id);
+void dp_int_update(line_t line, callid_t call_id, string_t called_num);
+int dp_init_template(const char *dp_file_name, int maxSize);
+void dp_int_cancel_offhook_timer(line_t line, callid_t call_id);
+
+
+boolean dp_offhook (line_t line, callid_t call_id);
+boolean dp_get_kpml_state(void);
+void dp_delete_last_digit(line_t line_id, callid_t call_id);
+void dp_store_digits(line_t line, callid_t call_id, unsigned char digit);
+char *dp_get_gdialed_digits(void);
+line_t dp_get_redial_line(void);
+void dp_reset(void);
+boolean dp_check_for_plar_line(line_t line);
+void dp_cancel_offhook_timer(void);
+
+#endif /* DIALPLANINT_H */
diff --git a/libs/sipcc/core/includes/digcalc.h b/libs/sipcc/core/includes/digcalc.h
new file mode 100644 (file)
index 0000000..3665f9f
--- /dev/null
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _DIGCALC_H_
+#define _DIGCALC_H_
+
+#define HASHLEN 16
+typedef char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN + 1];
+
+#define IN
+#define OUT
+
+/* calculate H(A1) as per HTTP Digest spec */
+void DigestCalcHA1(
+    IN char *pszAlg,
+    IN char *pszUserName,
+    IN char *pszRealm,
+    IN char *pszPassword,
+    IN char *pszNonce,
+    IN char *pszCNonce,
+    OUT HASHHEX SessionKey
+    );
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+    IN HASHHEX HA1,         /* H(A1) */
+    IN char *pszNonce,      /* nonce from server */
+    IN char *pszNonceCount, /* 8 hex digits */
+    IN char *pszCNonce,     /* client nonce */
+    IN char *pszQop,        /* qop-value: "", "auth", "auth-int" */
+    IN char *pszMethod,     /* method from the request */
+    IN char *pszDigestUri,  /* requested URL */
+    IN HASHHEX HEntity,     /* H(entity body) if qop="auth-int" */
+    OUT HASHHEX Response    /* request-digest or response-digest */
+    );
+
+void DigestString(char *string, HASHHEX response);
+
+#endif
diff --git a/libs/sipcc/core/includes/dns_utils.h b/libs/sipcc/core/includes/dns_utils.h
new file mode 100644 (file)
index 0000000..51683bd
--- /dev/null
@@ -0,0 +1,77 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _DNS_UTILS_INCLUDED_H
+#define _DNS_UTILS_INCLUDED_H
+
+#include "dns_util.h"
+
+#define MAX_DNS_CON 10
+#define DNS_TO_CNT 400
+#define DNS_RSP_FLG 0x8000
+#define DNS_RSP_CODE 0xf
+
+#define DNS_HOST_TYPE  0x01
+#define DNS_CNAME_TYPE 0x05
+#define DNS_SRV_TYPE   0x21
+#define DNS_PTR_TYPE   0xc0
+
+#define DNS_IN_CLASS 1
+
+#define DNS_INUSE_FREE   0
+#define DNS_INUSE_WAIT   1
+#define DNS_INUSE_CACHED 2
+
+/* per suggestion RFC in about sanity checking */
+#define DNS_MIN_TTL      (5*60)     /* 5 minutes */
+#define DNS_MAX_TTL      (24*60*60) /* 1 day */
+
+#define KAZOO_DNS_DEBUG 1
+
+#define DNS_OK                 0
+#define DNS_ERR_NOBUF          1
+#define DNS_ERR_INUSE          2
+#define DNS_ERR_TIMEOUT        3
+#define DNS_ERR_NOHOST         4
+#define DNS_ERR_HOST_UNAVAIL   5
+#define DNS_ERR_BAD_DATA       6
+#define DNS_ERR_LINK_DOWN      7
+#define DNS_ENTRY_VALID        8
+#define DNS_ENTRY_INVALID      9
+#define DNS_ERR_TTL_EXPIRED    10
+
+#define DNS_MAX_SRV_REQ        20
+
+#define DNS_HNAME_PAD          10
+#define DOMAIN_NAME_LENGTH     256
+
+typedef struct rr_reply_rec_ {
+    uint8_t ordered;
+    uint32_t priority;
+    uint32_t weight;
+    uint32_t rweight;
+    uint32_t ttl;
+    uint32_t port;
+    uint32_t rcvd_order;
+    int8_t *name;
+} rr_reply_rec_t;
+
+typedef struct master_rr_list_ {
+    uint8_t valid;
+    uint16_t ref_count;
+    uint8_t num_recs;
+    int8_t domain_name[DOMAIN_NAME_LENGTH + 1];
+    rr_reply_rec_t **rr_recs_order;
+} master_rr_list_t;
+
+typedef struct call_rr_list_ {
+    uint8_t current_index;
+    uint8_t max_index;
+    uint8_t master_index;
+    int8_t *domain_name;
+    rr_reply_rec_t **rr_recs_order;
+} call_rr_list_t;
+
+
+#endif /* _DNS_UTILS_INCLUDED_H */
diff --git a/libs/sipcc/core/includes/dtmf.h b/libs/sipcc/core/includes/dtmf.h
new file mode 100644 (file)
index 0000000..906bce8
--- /dev/null
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _DTMF_H_
+#define _DTMF_H_
+
+typedef enum _DTMFState {
+    DTMF_IDLE,
+    DTMF_START,
+    DTMF_CONT,
+    DTMF_END
+} DTMFState;
+
+typedef enum {
+    DTMF_OUTOFBAND_NONE = 0,
+    DTMF_OUTOFBAND_AVT,
+    DTMF_OUTOFBAND_AVT_ALWAYS
+} DtmfOutOfBandTransport_t;
+
+boolean RTPDtmfInbandGet(void);
+DtmfOutOfBandTransport_t RTPDtmfOutofbandGet(void);
+void RTPDtmfInbandSet(boolean val);
+void RTPDtmfOutofbandSet(uint16_t channel, DtmfOutOfBandTransport_t transport,
+                         int payload_type);
+
+#endif
diff --git a/libs/sipcc/core/includes/embedded.h b/libs/sipcc/core/includes/embedded.h
new file mode 100644 (file)
index 0000000..68a076b
--- /dev/null
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef embedded_hpp
+#define embedded_hpp
+
+typedef signed char INT8;
+typedef signed short INT16;
+typedef signed int INT32;
+
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+typedef unsigned int UINT32;
+
+typedef signed char int8;
+typedef signed short int16;
+typedef signed int int32;
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+
+#endif
diff --git a/libs/sipcc/core/includes/intelpentiumtypes.h b/libs/sipcc/core/includes/intelpentiumtypes.h
new file mode 100644 (file)
index 0000000..8e9955f
--- /dev/null
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IntelPentium_hpp
+#define IntelPentium_hpp
+
+typedef char INT8;
+typedef short INT16;
+typedef int INT32;
+
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+typedef unsigned int UINT32;
+
+typedef float FLOAT32;
+typedef double FLOAT64;
+
+#endif
\ No newline at end of file
diff --git a/libs/sipcc/core/includes/kpml_common_util.h b/libs/sipcc/core/includes/kpml_common_util.h
new file mode 100755 (executable)
index 0000000..6238112
--- /dev/null
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __KPML_COMMON_UTIL_H__
+#define __KPML_COMMON_UTIL_H__
+
+/* KPML status code for kpml decoder */
+typedef enum {
+    KPML_STATUS_OK,
+    KPML_ERROR_ENCODE,
+    KPML_ERROR_INVALID_ATTR,
+    KPML_ERROR_INVALID_ELEM,
+    KPML_ERROR_INVALID_VALUE,
+    KPML_ERROR_INTERNAL,
+    KPML_ERROR_MISSING_ATTR,
+    KPML_ERROR_MISSING_FIELD,
+    KPML_ERROR_NOMEM,
+    KPML_ERROR_XML_PARSER
+} kpml_status_type_t;
+
+
+/* single_digit_bitmask  */
+#define REGEX_0             1
+#define REGEX_1             (1<<1)
+#define REGEX_2             (1<<2)
+#define REGEX_3             (1<<3)
+#define REGEX_4             (1<<4)
+#define REGEX_5             (1<<5)
+#define REGEX_6             (1<<6)
+#define REGEX_7             (1<<7)
+#define REGEX_8             (1<<8)
+#define REGEX_9             (1<<9)
+#define REGEX_STAR          (1<<10)
+#define REGEX_POUND         (1<<11)
+#define REGEX_A             (1<<12)
+#define REGEX_B             (1<<13)
+#define REGEX_C             (1<<14)
+#define REGEX_D             (1<<15)
+#define REGEX_PLUS          (1<<16)
+
+#define REGEX_0_9             REGEX_0|REGEX_1|REGEX_2|REGEX_3|REGEX_4|REGEX_5|REGEX_6|REGEX_7|REGEX_8|REGEX_9
+
+#define KPML_DEFAULT_DTMF_REGEX  "[x*#ABCD]"
+
+kpml_status_type_t kpml_parse_regex_str(char *regex_str,
+                                        kpml_regex_match_t *regex_match);
+
+#endif /* __KPML_COMMON_UTIL_H__ */
diff --git a/libs/sipcc/core/includes/kpmlmap.h b/libs/sipcc/core/includes/kpmlmap.h
new file mode 100755 (executable)
index 0000000..568895c
--- /dev/null
@@ -0,0 +1,177 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef KPMLMAP_H
+#define KPMLMAP_H
+
+#include "xml_parser_defines.h"
+#include "singly_link_list.h"
+#include "subapi.h"
+
+#define KPML_ESCAPE      '\\'
+#define MAX_DIALSTRING   256
+#define DIAL_TIMEOUT      10
+#define MAX_KPML_TAG_STRING 32
+#define MAX_KPML_TEXT_STRING 16
+#define KPML_BUFFERS_NUM    100
+
+#define KPML_VER_STR "1.0"
+#define KPML_ENTER_STR "#"
+#define NUM_OF_REGX 1
+
+typedef enum {
+    KPML_NOMATCH = 0,
+    KPML_GIVETONE,
+    KPML_WILDPATTERN,
+    KPML_FULLPATTERN,
+    KPML_FULLMATCH,
+    KPML_IMMEDIATELY
+} kpml_match_action_e;
+
+typedef enum {
+    KPML_SUCCESS = 200,
+    KPML_USER_TERM_NOMATCH = 402,
+    KPML_TIMER_EXPIRE = 423,
+    KPML_NO_DIALOG = 481,
+    KPML_SUB_EXPIRE = 487,
+    KPML_BAD_EVENT = 489,
+    KPML_BAD_DOC = 501,
+    KPML_NO_PERSISTENT = 531,
+    KPML_NO_MULTIPLE = 532,
+    KPML_NO_MULITPLE_CALL_LEG = 533
+} kpml_resp_code_e;
+
+typedef enum {
+    KPML_NONE = 0x0,
+    KPML_SIGNAL_ONLY = 0x1,
+    KPML_DTMF_ONLY = 0x2,
+    KPML_BOTH = 0x3
+} kpml_config_e;
+
+#define KPML_SUCCESS_STR "OK"
+#define KPML_USER_TERM_NOMATCH_STR "No Match"
+#define KPML_TIMER_EXPIRE_STR "Timer Expired"
+#define KPML_SUB_EXPIRE_STR "Subscription Expired"
+#define KPML_ATTR_NOT_SUPPORTED_STR "Attribute not supported"
+#define KPML_TRYING_STR "Trying"
+
+
+typedef enum {
+    KPML_ONE_SHOT = 0,
+    KPML_PERSISTENT,
+    KPML_SINGLY_NOTIFY
+} kpml_sub_type_e;
+
+typedef enum {
+    KPML_NO_TIMER,
+    KPML_INTERDIGIT_TIMER,
+    KPML_CRITICAL_TIMER,
+    KPML_EXTRADIGIT_TIMER,
+    KPML_LONGDIGIT_TIMER
+} kpml_timer_e;
+
+typedef enum {
+    NO_SUB_DATA = 0,
+    SUB_DATA_FOUND = 1,
+    NOTIFY_SENT
+} kpml_state_e;
+
+typedef struct kpml_Q_digit_t_ {
+    char digits[MAX_DIALSTRING];
+    int dig_head;
+    int dig_tail;
+} kpml_Q_dig;
+
+typedef struct {
+    line_t line;
+    callid_t call_id;
+    void *timer;
+} kpml_key_t;
+
+typedef struct kpml_regex_match_s {
+    int num_digits;
+    union {
+        unsigned long single_digit_bitmask;
+    } u;
+} kpml_regex_match_t;
+
+typedef struct kpml_data_t_ {
+
+    line_t line;
+    callid_t call_id;
+    sub_id_t sub_id;
+
+    unsigned long sub_duration;
+    void *sub_timer;
+    kpml_key_t subtimer_key;
+
+    /* Hold regx received by SUB */
+    struct Regex regex[NUM_OF_REGX];
+
+    kpml_regex_match_t regex_match[NUM_OF_REGX];
+
+    /* If this is set then send out 402 response after # key */
+    boolean enterkey;
+
+       uint32_t kpml_id;
+
+    /* if this persistent */
+    kpml_sub_type_e persistent;
+    int flush;
+
+    /* timeout duration */
+    xml_unsigned32 inttimeout;
+    xml_unsigned32 crittimeout;
+    xml_unsigned32 extratimeout;
+
+    /* timer pointers for digits */
+    void *inter_digit_timer;
+    void *critical_timer;
+    void *extra_digit_timer;
+
+    kpml_key_t inttime_key;
+    kpml_key_t crittime_key;
+    kpml_key_t extratime_key;
+
+    xml_unsigned16 longhold;
+    xml_unsigned8 longrepeat;
+    xml_unsigned8 nopartial;
+
+    /* Store dialed digits */
+    char kpmlDialed[MAX_DIALSTRING];
+    boolean last_dig_bkspace;
+
+    char q_digits[MAX_DIALSTRING];
+    int dig_head;
+    int dig_tail;
+
+    boolean sub_reject; /* If this varilable is set reject
+                         * the subscription. There are cases
+                         * where INV has been sent out premeturaly
+                         * If the B2BUA asks for more digit by
+                         * subsctiption, then reject thoese
+                         */
+
+    boolean pending_sub;
+
+    boolean enable_backspace;
+    /* Setting this variable will enable backspace
+     */
+
+} kpml_data_t;
+
+kpml_state_e kpml_update_dialed_digits(line_t line, callid_t call_id,
+                                       char digit);
+void kpml_flush_quarantine_buffer(line_t line, callid_t call_id);
+void kpml_sm_register(void);
+void kpml_quarantine_digits(line_t line, callid_t call_id, char digit);
+void kpml_init(void);
+void kpml_shutdown(void);
+void kpml_set_subscription_reject(line_t line, callid_t call_id);
+boolean kpml_get_state(void);
+void kpml_inter_digit_timer_callback(void *);
+void kpml_subscription_timer_callback(void *);
+boolean kpml_is_subscribed (callid_t call_id, line_t line);
+
+#endif //KPMLMAP_H
diff --git a/libs/sipcc/core/includes/md5.h b/libs/sipcc/core/includes/md5.h
new file mode 100644 (file)
index 0000000..9a7381c
--- /dev/null
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#ifndef _MD5_H
+#define _MD5_H
+
+/* delineate the cisco changes to the RSA supplied module */
+#define CISCO_MD5_MODS
+
+#if defined(CISCO_MD5_MODS)
+
+#define MD5_LEN 16
+
+#endif /* defined(CISCO_MD5_MODS) */
+
+/* MD5 context. */
+typedef struct {
+    unsigned long int state[4]; /* state (ABCD) */
+    unsigned long int count[2]; /* number of bits, modulo 2^64 (lsb first) */
+    unsigned char buffer[64];   /* input buffer */
+} MD5_CTX;
+
+void MD5Init(MD5_CTX *);
+void MD5Update(MD5_CTX *, unsigned char *, unsigned int);
+void MD5Final(unsigned char[16], MD5_CTX *);
+
+//#define MD5TESTSUITE 1
+#if defined(MD5TESTSUITE) /* only needed if running MD5 verfication tests */
+void MDString(char *string);
+void MDPrint(char *digest);
+void MDTestSuite(void);
+#endif
+
+#endif /* _MD5_H */
diff --git a/libs/sipcc/core/includes/memory.h b/libs/sipcc/core/includes/memory.h
new file mode 100755 (executable)
index 0000000..4a0fc35
--- /dev/null
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MEMORY_H_
+#define _MEMORY_H_
+
+/*
+ *  DESCRIPTION
+ *     This module contains the functions which implement the dynamic memory
+ *      management routines. The following assumptions/rules apply:
+ *   1) Packets are allocated a minimum of MIN_BLOCK + BLOCK_OVERHEAD bytes.
+ *   2) The size of the heap is set at link time, using the -heap flag
+ *      The allocation and sizing of the heap is a cooperative effort
+ *      involving the linker, this file, and "sysmem.c".
+ *   3) The heap can be reset at any time by calling the function "minit"
+ *
+ */
+
+
+/*
+ *--------------------DEPRECATED FILE-------------------------------
+ *
+ * As part of Skittles project this file is deprecated.
+ * DO NOT add anything to this file. use cpr_stdlib.h instead
+ * If you are doing SYNC merges _DO_ _NOT_ merge anything from parent
+ * This file is kept here because it comes from parent/grand parent branches
+ * and will not be removed from clearcase till Skittles collapses.
+ * [The fAQ on cc tools contains details of why ]
+ *
+ * If you have questions send email to skittles-dev
+ *---------------------------------------------------------------
+ */
+
+#endif
diff --git a/libs/sipcc/core/includes/misc_apps_task.h b/libs/sipcc/core/includes/misc_apps_task.h
new file mode 100644 (file)
index 0000000..baa5214
--- /dev/null
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MISC_APP_TASK_H
+#define MISC_APP_TASK_H
+
+extern cprMsgQueue_t s_misc_msg_queue;
+
+extern cpr_status_e MiscAppTaskSendMsg(uint32_t cmd, cprBuffer_t buf,
+                                       uint16_t len);
+extern void MiscAppTask(void *arg);
+
+#endif /* MISC_APP_TASK_H */
diff --git a/libs/sipcc/core/includes/misc_util.h b/libs/sipcc/core/includes/misc_util.h
new file mode 100644 (file)
index 0000000..fcd2e90
--- /dev/null
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __MISC_UTIL_H__
+#define __MISC_UTIL_H__
+unsigned long gmt_string_to_seconds(char *gmt_string, unsigned long *seconds);
+long diff_current_time(unsigned long t1, unsigned long *difference);
+#endif
diff --git a/libs/sipcc/core/includes/phntask.h b/libs/sipcc/core/includes/phntask.h
new file mode 100644 (file)
index 0000000..24b5fce
--- /dev/null
@@ -0,0 +1,309 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PHNTASK_H
+#define PHNTASK_H
+
+/* used for certain bugtraps */
+#define  ERRid       0xE0
+#define  ERR_1       0xE1
+#define  ERR_2       0XE2
+#define  ERR_3       0XE3
+#define  ERR_4       0XE4
+#define  ERR_5       0XE5
+
+/*
+ * SysHdr Network Command definitions (0x00 - 0x0F)
+ */
+#define  RCV_CMD       0x00
+#define  TRAP_CMD      0x01
+#define  OPEN_CMD      0x02
+#define  CLOSE_CMD     0x03
+#define  HANG_CMD      0x04
+#define  TO_CMD        0x05
+#define  ISR_CMD       0x06
+#define  ACK_CMD       0x07
+#define  NACK_CMD      0x08
+#define  PING_CMD      0x09
+#define  JOIN_CMD      0x0A
+#define  LEAVE_CMD     0x0B
+#define  TO2_CMD       0x0C
+#define  TO3_CMD       0x0D
+
+/*---------------------------------------------
+ * Message Definition
+ *
+ * original requirement is to get a unique id I guess
+ * for TNP we just get some unique numbers through enum
+ */
+
+enum {
+    /* CPR Messages */
+    TIMER_EXPIRATION,
+
+    /* DHCP Messages */
+    DHCP_UDP,
+    DHCP_TMR,
+    DHCP_TFTP,
+    DHCP_DHCP,
+    DHCP_TO,
+    DHCP_RESET_IP,
+    DHCP_DNS,
+    DHCP_CFG,
+    DHCP_CDP,
+    DHCP_RESTART,
+    DHCP_SOC,
+    DHCP_UI,
+
+    /* SNTP Messages */
+    SNTP_UDP,
+    SNTP_REQ,
+
+    /* TFTP Messages */
+    TFTP_PHN,
+    TFTP_CFG,
+    TFTP_DHCP,
+    TFTP_UDP,
+    TFTP_TO,
+
+    /* UDP Messages */
+    UDP_TFTP,
+    UDP_DHCP,
+    UDP_DNS,
+    UDP_RTP,
+    UDP_SNTP,
+
+    /* TCP Messages */
+    TCP_PHN,
+    TCP_PHN_OPEN,
+    TCP_PHN_CLOSE,
+    TCP_CFG,
+    TCP_CFG_OPEN,
+    TCP_CFG_CLOSE,
+    TCP_TO,
+    TCP_SOC,
+
+    /* HTTP Messages */
+    HTTP_RCV,
+
+    /* Socket Messages */
+    ICMP_DHCP,
+    ICMP_ARP,
+    ICMP_RTP,
+    SOC_ICMP,
+    SOC_IGMP,
+    SOC_ROUTE,
+    SOC_FRAG,
+    IGMP_PHN_JOIN,
+    IGMP_PHN_LEAVE,
+    IP_PHN,
+    SOC_ISR,
+    NET_CDP,
+
+    /* Phone Messages */
+    PHN_TCP,
+    PHN_CFG,
+    PHN_CFG_UI,
+    PHN_TFTP,
+    PHN_TICK_TO,
+    PHN_DSP,
+    PHN_REG,
+
+    PHN_DIS,
+
+    /* DNS Messages */
+    DNS_DHCP,
+    DNS_CFG,
+    DNS_CFG_URL,
+    DNS_UDP,
+    DNS_TO,
+
+
+    CFG_DNS,
+    CFG_TFTP,
+    CFG_TMR,
+    CFG_CDP_TMR,
+    CFG_UI,
+    CFG_NET_CFG,
+    CFG_FMW_CFG,
+    CFG_STA_CFG,
+    CFG_KAZOO_CFG,
+    CFG_ETH_MEDIA,
+    TCP_PHN_CFG_TCP_DONE,
+
+    NET_DNS,
+    CDP_C3PO,
+    CDP_TO_WAIT,
+    CDP_ETH,
+    CDP_TO_SYNC,
+    CDP_SEND,
+    CDP_CACHE_TO,
+    CDP_NET,
+    CDP_TRIG,
+    C3PO_ETH_MODE,
+
+    /* RTP Messages */
+    RTP_ISR,
+    RTP_UDP,
+
+    /* TTY Messages */
+    TTY_TTY,
+
+    /* SIP Task Messages */
+    SIP_UDP,
+    SIP_GSM,
+    SIP_REG_REQ,
+    SIP_REG_CANCEL,
+    SIP_REG_PHONE_IDLE,
+    SIP_REG_FALLBACK,
+    SIP_TMR_REG_ACK,
+    SIP_TMR_REG_EXPIRE,
+    SIP_TMR_REG_WAIT,
+    SIP_TMR_REG_RETRY,
+    SIP_TMR_REG_STABLE,
+    SIP_TMR_INV_LOCALEXPIRE,
+    SIP_TMR_INV_EXPIRE,
+    SIP_TMR_MSG_RETRY,
+    SIP_REG_CLEANUP,
+    CC_EVENT,
+    SIP_ICMP_UNREACHABLE,
+    SIP_TMR_SUPERVISION_DISCONNECT,
+    SIP_TMR_CALL_DISCONNECT,
+    SIP_TMR_MSG_RETRY_SUBNOT,
+    SIP_TMR_PERIODIC_SUBNOT,
+    SIP_TMR_GLARE_AVOIDANCE,
+    SIPSPI_EV_CC_SUBSCRIBE_REGISTER,
+    SIPSPI_EV_CC_SUBSCRIBE,
+    SIPSPI_EV_CC_SUBSCRIBE_RESPONSE,
+    SIPSPI_EV_CC_NOTIFY,
+    SIPSPI_EV_CC_NOTIFY_RESPONSE,
+    SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED,
+    SIPSPI_EV_CC_PUBLISH_REQ,
+    REG_MGR_STATE_CHANGE,
+    SUB_MSG_KPML_TERMINATE,
+    SUB_MSG_KPML_SUBSCRIBE,
+    SUB_MSG_KPML_NOTIFY_ACK,
+    SUB_MSG_KPML_SUBSCRIBE_TIMER,
+    SUB_MSG_KPML_DIGIT_TIMER,
+    DP_MSG_INIT_DIALING,
+    DP_MSG_DIGIT_TIMER,
+    DP_MSG_DIGIT_STR,
+    DP_MSG_STORE_DIGIT,
+    DP_MSG_DIGIT,
+    DP_MSG_DIAL_IMMEDIATE,
+    DP_MSG_REDIAL,
+    DP_MSG_ONHOOK,
+    DP_MSG_OFFHOOK,
+    DP_MSG_CANCEL_OFFHOOK_TIMER,
+    DP_MSG_UPDATE,
+    SIP_TMR_STANDBY_KEEPALIVE,
+    SUB_MSG_PRESENCE_GET_STATE,
+    SUB_MSG_PRESENCE_TERM_REQ,
+    SUB_MSG_PRESENCE_TERM_REQ_ALL,
+    SUB_MSG_PRESENCE_SUBSCRIBE_RESP,
+    SUB_MSG_PRESENCE_NOTIFY,
+    SUB_MSG_PRESENCE_UNSOLICITED_NOTIFY,
+    SUB_MSG_PRESENCE_TERMINATE,
+    SUB_HANDLER_INITIALIZED,
+    SIP_TMR_DM_SHR_WAIT_DM_UPD_EVENT,
+    SIP_SHUTDOWN,
+    SIP_TMR_SHUTDOWN_PHASE2,
+    SIP_RESTART,
+    SUB_MSG_CONFIGAPP_SUBSCRIBE,
+    SUB_MSG_CONFIGAPP_TERMINATE,
+    SUB_MSG_CONFIGAPP_NOTIFY,
+    SUB_MSG_CONFIGAPP_NOTIFY_ACK,
+    SIP_REG_UPDATE,
+    SUB_MSG_FEATURE_SUBSCRIBE_RESP,
+    SUB_MSG_FEATURE_NOTIFY,
+    SUB_MSG_FEATURE_TERMINATE,
+    SUB_MSG_B2BCNF_SUBSCRIBE_RESP,
+    SUB_MSG_B2BCNF_NOTIFY,
+    SUB_MSG_B2BCNF_TERMINATE,
+    THREAD_UNLOAD,
+    DCSM_EV_READY,
+
+    /* GSM Messages */
+    GSM_RM,
+    GSM_FM,
+    GSM_GSM,
+    GSM_SIP,
+    FM_GSM
+};
+
+#define REG_CMD_PRINT(arg) \
+        (arg == SIP_REG_REQ ?  "SIP_REG_REQ" : \
+        arg == SIP_REG_CANCEL ?  "SIP_REG_CANCEL" : \
+        arg == SIP_REG_PHONE_IDLE ?  "SIP_REG_PHONE_IDLE" : \
+        arg == SIP_REG_FALLBACK ?  "SIP_REG_FALLBACK" : \
+        arg == SIP_TMR_REG_ACK ?  "SIP_TMR_REG_ACK" : \
+        arg == SIP_TMR_REG_EXPIRE ?  "SIP_TMR_REG_EXPIRE" : \
+        arg == SIP_TMR_REG_WAIT ?  "SIP_TMR_REG_WAIT" : \
+        arg == SIP_TMR_REG_RETRY ?  "SIP_TMR_REG_RETRY" : \
+        arg == SIP_TMR_REG_STABLE ?  "SIP_TMR_REG_STABLE" : \
+        arg == SIP_REG_CLEANUP ?  "SIP_REG_CLEANUP" : "")\
+
+
+
+#define TCP_USR_OPEN   TCP_PHN_OPEN
+#define TCP_USR_CLOSE  TCP_PHN_CLOSE
+#define TCP_USR        TCP_PHN
+#define IGMP_USR_JOIN  IGMP_PHN_JOIN
+#define IGMP_USR_LEAVE IGMP_PHN_LEAVE
+
+/*
+ * END INTER-TASK COMMANDS
+ *
+ */
+
+
+
+/*
+ * This is used by the config task to allow it to
+ * accept messages sent to it by the phone task.
+ */
+#define TFTP_USR       TFTP_PHN
+
+
+/*
+ * Stack Definition
+ * Stacks need to be 32bit aligned.
+ */
+
+#define  STKSZ      61440       //default stacksize rountable platform
+
+
+#if defined SIP_OS_LINUX
+
+#define NICE_STEP -4
+#define TIMER_THREAD_RELATIVE_PRIORITY  4*(NICE_STEP) /* -16 */
+/* redid priorities to adjust relative priority with EDT cannot
+     use NICE_STEP so using absolute numbers here */
+#define GSM_THREAD_RELATIVE_PRIORITY    -14
+#define SIP_THREAD_RELATIVE_PRIORITY    -14
+#define APP_THREAD_RELATIVE_PRIORITY    -14
+#define CCPROVIDER_THREAD_RELATIVE_PRIORITY -14
+
+#elif defined SIP_OS_OSX
+
+#define NICE_STEP -4
+#define TIMER_THREAD_RELATIVE_PRIORITY  4*(NICE_STEP) /* -16 */
+#define GSM_THREAD_RELATIVE_PRIORITY    3*(NICE_STEP) /* -12 */
+#define SIP_THREAD_RELATIVE_PRIORITY    3*(NICE_STEP) /* -12 */
+#define APP_THREAD_RELATIVE_PRIORITY    3*(NICE_STEP) /* -12 */
+#define CCPROVIDER_THREAD_RELATIVE_PRIORITY 3*(NICE_STEP) /*-12 */
+
+#elif defined SIP_OS_WINDOWS
+
+#define TIMER_THREAD_RELATIVE_PRIORITY  0
+#define GSM_THREAD_RELATIVE_PRIORITY    -1
+#define SIP_THREAD_RELATIVE_PRIORITY    -1
+#define APP_THREAD_RELATIVE_PRIORITY    -1
+#define CCPROVIDER_THREAD_RELATIVE_PRIORITY    -1
+
+#endif
+
+extern int platThreadInit(char *threadName);
+
+#endif /* PHNTASK_H */
diff --git a/libs/sipcc/core/includes/phone.h b/libs/sipcc/core/includes/phone.h
new file mode 100644 (file)
index 0000000..62fa07d
--- /dev/null
@@ -0,0 +1,504 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PHONE_H
+#define PHONE_H
+
+#include "text_strings.h"
+
+#include "sessionConstants.h"
+
+// define device reset types
+typedef enum {
+    DEVICE_RESET   = CC_DEVICE_RESET,
+    DEVICE_RESTART = CC_DEVICE_RESTART,
+    ICMP_UNREACHABLE_RESET = CC_DEVICE_ICMP_UNREACHABLE
+} DeviceResetType;
+
+/*
+ * When not building IRX or attempting to emluate IRX
+ * define the irx_lst_blk structure
+ */
+typedef int32_t irx_lst_blk;
+#if defined SIP_OS_WINDOWS
+/*
+ * When attempting to emulate TNP, but use 79xx BTXML
+ * define the following
+ */
+typedef int32_t irx_tsk_blk;
+typedef int32_t irx_tmr_buf;
+
+#include "tnpphone.h"
+#endif
+
+
+#define APP_MAX_SIZE  0x60000L
+#define MAX_CTL_FILE_SIZE 0x8000L
+
+#define disableInt IRXDISAB
+#define enableInt IRXENAB
+#define intrOff IRXHRDDIS
+#define intrOn  IRXHRDENA
+
+#define MAX_LINES_7940       2
+#define MAX_LINES_7960       6
+#define DSP_LOAD_ID_MAX      28
+#define OLD_DSP_LOAD_ID_MAX  8
+
+/*
+ * System timer tick (clock interrupt) specifications.
+ */
+#define TICKS_PER_SECOND                 100      /* 10ms. per tick */
+#define SECONDS_TO_TICKS(SECS)           ((SECS) * TICKS_PER_SECOND)
+#define TICKS_TO_SECONDS(TICKS)          ((TICKS)/ TICKS_PER_SECOND)
+#define MILLISECONDS_TO_TICKS(MILLISECS) (((MILLISECS)*(TICKS_PER_SECOND))/1000)
+
+enum {
+    DSP_RST,
+    GPIO_A = 4,
+    GPIO_B,
+    GPIO_C,
+    GPIO_D,
+    gpioMax
+};
+
+typedef struct {
+    uint8_t  loadid[256];
+    uint8_t  apploadid[8];
+    uint32_t chksum;    // file checksum
+    uint32_t applen;    // file len
+    uint32_t appcmp;    // compressed/uncompressed
+    uint32_t cmpchksum; // file checksum
+    uint32_t cmplen;    // file len
+} LoadHdr;
+
+typedef struct {
+    uint8_t  apploadid[DSP_LOAD_ID_MAX];
+    uint32_t chksum;    // file checksum
+    uint32_t applen;    // file len
+    uint32_t appcmp;    // compressed/uncompressed
+    uint32_t cmpchksum; // file checksum
+    uint32_t cmplen;    // file len
+
+} DSPLoadHdr;
+
+typedef struct {
+    uint8_t  apploadid[OLD_DSP_LOAD_ID_MAX];
+    uint32_t chksum;    // file checksum
+    uint32_t applen;    // file len
+    uint32_t appcmp;    // compressed/uncompressed
+    uint32_t cmpchksum; // file checksum
+    uint32_t cmplen;    // file len
+
+} LegacyDSPLoadHdr;
+
+typedef struct {
+    uint16_t valid;
+    uint16_t notused[3];
+    uint16_t hw_ver_hi;
+    uint16_t hw_ver_low;
+    uint8_t  mac[6];
+    int8_t   model[20];
+    uint16_t feature_bits;
+    uint16_t ext;
+    uint16_t license[20];
+    int8_t   serial_number[20];
+} MfgBlk;
+
+#define GPIO_IO_SEL_SHIFT 8
+
+#define GPIOA_LBK_ON      1
+#define GPIOA_MSG_LED     4
+#define GPIOA_SPKR_LED    5
+#define GPIOA_MUTE_LED    6
+#define GPIOA_HDST_LED    7
+#define GPIOA_IO_MASK     ((1<<(GPIOA_LBK_ON+GPIO_IO_SEL_SHIFT))|\
+                          (1<<(GPIOA_MSG_LED+GPIO_IO_SEL_SHIFT))|\
+                          (1<<(GPIOA_SPKR_LED+GPIO_IO_SEL_SHIFT))|\
+                          (1<<(GPIOA_MUTE_LED+GPIO_IO_SEL_SHIFT))|\
+                          (1<<(GPIOA_HDST_LED+GPIO_IO_SEL_SHIFT)))
+
+#define GPIOD_HANDSET     0
+#define GPIOD_SPKER       1
+#define GPIOD_PHY_MDC     3
+#define GPIOD_PHY_MDIO    4
+#define GPIOD_IO_RST_HI   5
+#define GPIOD_IO_RST_LO   6
+#define GPIOD_IO_MASK     0xff00
+
+typedef volatile struct {
+    uint16_t reg;
+    uint16_t unused;
+} s_rbbreg;
+
+enum {
+    rbbTimeout,
+    rbbAPIWs,
+    rbbWbufEn,
+    rbbAccessFactor0,
+    rbbAccessFactor1,
+    rbbAccessFactor2,
+    rbbAccessFactor3,
+    rbbIntrSw,
+    rbbMax
+};
+
+extern s_rbbreg RBBReg[rbbMax];
+
+#define DCFG_PROGRAMMED   0x12300000L
+#define DCFG_FIXED_VLAN   0x00000001L
+#define DCFG_UI_SETTING   0x00000002L
+
+#define IF_MAX 1
+#define MAX_PORT 2
+enum { PORT_UP, PORT_DOWN };
+
+#define KA_TICKS 0  //Default to 10 seconds
+
+typedef struct {
+    uint32_t Active,
+             IP,
+             SSRC,
+             PktIn,
+             OctIn,
+             Delay,
+             Cycle,
+             Tick,
+             Time,
+             Mcast;
+    uint16_t Seq,
+             First;
+    int32_t  Jitter,
+             Trans;
+} _s_talkers;
+
+#define TX_CONNECT 1
+#define RX_CONNECT 2
+
+//defined values for IfActive
+#define IF_2MPS  1L
+#define IF_10MPS 2L
+
+//Flag definitions for timer
+#define KEYSCAN         0x00000001L
+#define HOOKSCAN        0x00000002L
+#define TIMETICK        0x00000004L
+
+#define ON 1
+#define OFF 0
+#define TALK 2
+#define SIZEOF_UISET 16
+
+typedef struct {
+    uint8_t diag;
+    uint32_t status;
+    uint16_t vlan;
+    uint16_t vvlan;
+    uint16_t port;
+    uint16_t uiset[SIZEOF_UISET];
+    uint16_t vvlanAdmin;
+    uint16_t vvlanIP;     //vvlan that our IP address was aquired on
+    uint8_t unused[2001]; //struct must remain 2046 in size
+} DeviceCfgBlk;
+
+
+// pulled from net_config.h
+#define MAX_NAME (64)
+#define BACKUP_SERVERS (7)
+#define MAX_DHCP_TFTP_FILE_NAME (128)
+#define DISKFULL       3      // file too large
+#define NOERR          8      // no error
+typedef struct
+{
+    int8_t   tftp_file[40];
+    uint32_t status;
+    uint32_t control;
+    uint32_t appStatus;
+    uint32_t appControl;
+    uint32_t tftp_backup;
+} DHCPBlkExt;
+
+typedef struct
+{
+    uint32_t status;      // This structure for sake of backward compatibility needs to
+    uint32_t my_ip_addr;  // remain as is until you and I are deceased, stop paying taxes
+    uint32_t subnet_mask; // and all the 79xx phones are long gone. A strategy could be
+    uint32_t defaultgw;   // developed to solve this and world hunger but heed this warning.
+    uint32_t dns_addr;
+    uint32_t tftp_addr;
+    uint8_t  my_mac_addr[6];
+    int8_t   domain_name[MAX_NAME];
+    int8_t   my_name[MAX_NAME];
+    uint32_t dhcp_server;
+    uint32_t spare;
+    uint32_t dns_backup[BACKUP_SERVERS];
+    uint32_t gw_backup[BACKUP_SERVERS];
+    int8_t   tftp_name[MAX_NAME];
+    union {
+        int8_t tftp_file[MAX_DHCP_TFTP_FILE_NAME];
+        DHCPBlkExt ext;
+    } extFields;
+} DHCPBlk;
+
+
+/*
+ * These structures are how the memory blocks are laid out
+ * Note that these are reflected in ASICAPP.CMD with:
+ *
+ *    BOOT_MEM  : org = 0x00000020   len = 0x00001FE0
+ *    MFG_ROM   : org = 0x00004000   len = 0x00002000
+ *    CFG_ROM   : org = 0x00006000   len = 0x00002000
+ *    VER_MEM   : org = 0x00008000   len = 0x00000004
+ *    A1_ROM    : org = 0x00010000   len = 0x00060000
+ *    A2_ROM    : org = 0x00070000   len = 0x00060000
+ *    DSP_ROM   : org = 0x000D0000   len = 0x00020000
+ *    DIR_ROM   : org = 0x000F0000   len = 0x00010000
+ */
+typedef struct {
+    DHCPBlk      dhcp_blk;           /* 0x00006000 - 0x0000619f  len=0x01a0 */
+    DeviceCfgBlk device_cfg_blk;     /* 0x000061a0 - 0x000069a3  len=0x0804 */
+
+// removing this area as we don't use it. Re-allocating this space
+// for use in the prot_cfg_area so we can store more config info.
+//  int8_t     skinny_cfg[0x065c];     /* 0x000069a4 - 0x00006fff  len=0x065c */
+
+    int8_t   prot_signature[0x0004]; /* 0x000069a4 - 0x000069a7  len=0x0004 */
+
+//  int8_t     prot_cfg_area[0x0ff8];  /* 0x00007004 - 0x00007ffb  len=0x0ff8 */
+    int8_t   prot_cfg_area[0x1654];  /* 0x000069a8 - 0x00007ffb  len=0x1654 */
+
+    uint32_t prot_cfg_offset;        /* 0x00007ffc - 0x00007fff  len=0x0004 */
+} cfg_rom_t;
+
+typedef struct {
+    int8_t   prot_signature[0x0004]; /* 0x000F0000 - 0x000F0003  len=0x0004 */
+    int8_t   personal_dir[0xDFFC];   /* 0x000F0004 - 0x000FDFFF  len=0xDFFC */
+    int8_t   dial_signature[0x0004]; /* 0x000FE000 - 0x000FE003  len=0x0004 */
+    int8_t   dial_plan[0x1FF0];      /* 0x000FE004 - 0x000FFFF3  len=0x1FF0 */
+    uint32_t unused_offset2;         /* 0x000FFFF4 - 0x000FFFF7  len=0x0004 */
+    uint32_t dial_plan_offset;       /* 0x000FFFF8 - 0x000FFFFB  len=0x0004 */
+    uint32_t prot_dir_offset;        /* 0x000FFFFC - 0x000FFFFF  len=0x0004 */
+} dir_rom_t;
+#define DIR_CONFIG_SIGNATURE "DIR0"
+#define CONFIG_MAX (0x2000 - sizeof(DHCPBlk) - sizeof(DeviceCfgBlk))
+
+// These variables are declared in the ASIC.CMD file
+extern volatile uint32_t WDTimer;
+extern volatile uint16_t GPIO1[gpioMax];
+extern DHCPBlk DHCPInfo;        // DHCP info stored in RAM
+extern LoadHdr Load;            // load header of current app
+
+#ifdef _WIN32
+extern uint8_t LoadArea[0xFFFF];
+extern uint8_t BootHeader[9];   // Magic number 9 = MAX_OLD_LOAD_ID_STRING
+#else
+extern uint8_t LoadArea;        // landing zone for app download
+extern int8_t BootHeader;
+#endif
+extern LoadHdr LoadHeader;      // landing zone for app download
+extern DHCPBlk DHCPInfoRom;     // DHCP info stored in ROM
+extern DHCPBlk DHCPInfoTemp;    // DHCP info stored in ROM
+extern MfgBlk  MfgInfo;         // Manufacturing information
+extern LoadHdr App1Hdr;         // Application 1 in ROM
+extern LoadHdr App2Hdr;         // Application 2 in ROM
+extern uint8_t App1Rom;         // Application 1 in ROM
+extern uint8_t App2Rom;         // Application 2 in ROM
+extern DSPLoadHdr DspHeader;
+extern dir_rom_t DirStorage;    // Actual personal directory stored in ROM
+extern uint32_t DcmpVer;        // Version of Decompression Algorithm in boot
+extern uint16_t DCVerReg;
+//extern SysHdr *pCFGTftpReq;
+
+typedef struct {
+    int16_t displayContrast;
+    int16_t ringType;
+    int16_t handsetVolume;
+    int16_t headsetVolume;
+    int16_t speakerVolume;
+    int16_t ringVolume;
+    int16_t reserved[10];
+} t_SettingsDesc;
+
+extern int16_t SavePhoneSettings;
+
+// Phone states
+enum {
+    STATE_INIT_CFG,
+    STATE_CDP_CFG,
+    STATE_IP_CFG,
+    STATE_FILE_CFG,
+    STATE_CFG_UPDATE,
+    STATE_CONNECTING,
+    STATE_REGISTER,
+    STATE_REGISTER_REJ,
+    STATE_LOADID_REQ,
+    STATE_BOOT_DSP,
+    STATE_LOADING,
+    STATE_CONNECTED,
+    STATE_RESETTING,
+    STATE_IP_RELEASE,
+    STATE_DUP_IP,
+    STATE_DONE_LOADING,
+    STATE_UNPROVISIONED,   /* KAZOO only */
+    maxPHNState
+};
+
+enum { CDP_CACHE_P0_CLEAR, CDP_CACHE_P1_CLEAR, CDP_CACHE_CLEAN };
+//enum { CDP_UNTRUSTED, CDP_TRUSTED, CDP_TRUST_NA = 255 };
+
+#define DC_NONE 0
+#define DC_C3PO 1
+#define DC_ETH  3
+#define DC_2MPS 7
+
+#define NO_TAGGING 4095
+#define NO_VID     4096
+
+/*
+ * Number of buffers in a pool.
+ */
+#define  SYSBUF_CNT  50
+#define  DSPBUF_CNT  30
+#define  SIPTMRBUF_CNT 32
+#define  SIPBUF_CNT MAX(MAX_TEL_LINES, 32)
+#define  GSMBUF_CNT  32
+#define  GUITMRBUF_CNT  5
+#define  TCPBUF_CNT    6  // for use by telnet etc
+#define  PKTBUF_TX_CNT 10
+#define  ENET_RX_BUF_CNT 10
+#define  PKTBUF_CNT  (ENET_RX_BUF_CNT + PKTBUF_TX_CNT)  //20
+
+/*
+ * Size of an indivdual buffer. CPR_MAX_MSG_SIZE
+ * declared in cpr_irx_ipc.h needs to be set to
+ * the largest value defined here.
+ */
+#define  SYSBUF_SIZ  512
+#define  GUITMRBUF_SIZ 32
+#define  PKTBUF_SIZ  3072
+#define  TCPBUF_SIZ 2560
+#define  DSPBUF_SIZ 96
+#define  GUIBUF_SIZ 512
+
+// Buffer Return Identifiers
+/*
+ * Since we now have this horrible mismash of CPR and IRX calls
+ * in the legacy phone, CPR has to have an unique buffer ID to
+ * place in the sys header of the buffers so when the application
+ * is done with them CPR will know not to call Main0RetBuffer. This
+ * value is not used in application code just #defined here to
+ * prevent it being used in the future.
+ */
+#define  SYS_BUFFER    1     //System Buffer
+#define  XMT_BUFFER    2     //Ether Transmit Packet Ram Buffer
+#define  RCV_BUFFER    3     //Ether Receive Packet Ram Buffer
+#define  TCP_BUFFER    4
+#define  DSP_BUFFER    5
+#define  GUITMR_BUFFER 6
+#define  CPR_BUFFER    256
+
+extern uint16_t ResetPend;
+extern uint32_t gtick;
+extern uint16_t ResetTimer; // used to delay the bugtrap, 10mse ticks
+extern uint8_t LinkState[MAX_PORT];
+extern uint16_t LinkStatus[MAX_PORT];
+extern uint8_t CurrentLinkState[MAX_PORT];
+extern uint16_t CurrentMode[MAX_PORT];
+
+extern int16_t CallInProg;
+extern int32_t BugTrapped;
+
+extern MfgBlk *pMfgInfo;
+extern DeviceCfgBlk *pDeviceInfo, *pDeviceInfoRom;
+extern LoadHdr *pMyLoad, *pPriLoad, *pSecLoad;
+extern int8_t *pMyPlatform;
+extern int8_t *pMyPhoneModel;
+extern DSPLoadHdr *pMyDSPLoad;
+
+extern int32_t PhoneStim;
+extern int32_t PhoneHookStim;
+
+extern uint32_t IfActive;
+extern volatile uint16_t VVLan, DCDetect, C3PODetected;
+extern uint32_t MaxPort;
+
+extern uint32_t BCastMax;
+extern boolean Is7940;
+extern boolean Is79x0G;
+
+void PHNChangeState(uint16_t state);
+uint16_t PHNGetState(void);
+int32_t PHNStateToStringIndex(uint16_t PhoneState);
+void PHNSpeakerMode(uint16_t mode);
+void PHNMuteMode(uint16_t mode);
+void PHNHeadsetMode(uint16_t mode);
+void PHNRequestFile(int8_t *pName, uint8_t *pDest, int32_t maxSize);
+void PHNMessageLedMode(uint16_t mode);
+void PHNRingerLedMode(uint16_t mode);
+void phone_reset(DeviceResetType resetType);
+void platform_reset_req(DeviceResetType resetType);
+void platform_sync_cfg_vers(char *configVersionStamp,
+                            char *dialplanVersionStamp, char *);
+int32_t phone_get_keypress_rand_seed(void);
+int16_t phone_call_in_progress(void);
+
+void tweak_watchdog(void);
+extern uint32_t set_wd_timeout(uint16_t new_time);
+extern void restore_wd_timeout(uint32_t prev_time);
+
+#define DEFAULT_WATCHDOG_TIME 8
+
+#define PROGRAM_HW_WATCHDOG_IN_MILLISECONDS(x); WDTimer = 0x00550000; \
+            WDTimer = 0x01AA0000 + ((x)); //Program the watchdog
+
+#define HIT_THE_WATCHDOG() tweak_watchdog()
+
+
+void BugInfoValidate(void);
+uint32_t BugInfoAbortPtr(void);
+
+
+int8_t *task2string(int32_t task_id);
+
+#define EXCEPTION_BUGCODE   0xdd //BugTrap Code:0xddxx for exceptions
+#define INITSTKSZ   500
+#define SVCSTKSZ    40      // interrupt off (svc) stack size
+
+
+/* SKITTLES moved syshdr for IPC from network.h. this will be the new home for
+ * SysHdr. it is used by all phone tasks and can not be plat specific.
+ */
+#define MISC_LN 8
+typedef struct
+{
+    void    *Next;
+    uint32_t Cmd;
+    uint16_t RetID;
+    uint16_t Port;
+    uint16_t Len;
+    uint8_t *Data;
+    union {
+        void *UsrPtr;
+        uint32_t UsrInfo;
+    } Usr;
+    uint8_t Misc[MISC_LN];
+ //  void  *TempPtr;
+} phn_syshdr_t;
+
+/* SKITTLES The line below is only temporary till we
+ * change refernces to SysHdr.
+ */
+typedef phn_syshdr_t SysHdr;
+
+/* settings.c needs it */
+extern DHCPBlk *pDHCPInfoTemp;
+
+extern SysHdr *pCFGTftpReq;
+
+typedef struct {
+    char newcfgVerStamp[64];
+    char newdialplanVerStamp[64];
+} PhoneSyncCfgVer;
+
+#endif /* PHONE_H */
diff --git a/libs/sipcc/core/includes/phone_debug.h b/libs/sipcc/core/includes/phone_debug.h
new file mode 100644 (file)
index 0000000..ce8f7ed
--- /dev/null
@@ -0,0 +1,275 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PHONE_DEBUG_H_
+#define _PHONE_DEBUG_H_
+
+#include "cpr_stdio.h"
+#include "cc_constants.h"
+
+extern cc_int32_t SipDebugMessage;
+extern cc_int32_t SipDebugState;
+extern cc_int32_t SipDebugTask;
+extern int32_t SipRelDevEnabled;
+extern cc_int32_t SipDebugRegState;
+extern int32_t SipDebugGenContactHeader;
+extern cc_int32_t SipDebugTrx;
+
+extern int32_t DebugPlatform;
+extern int32_t DebugSOCTask;
+extern int32_t DebugSNTP;
+extern int32_t DebugSNTPPacket;
+extern cc_int32_t TMRDebug;
+extern cc_int32_t GSMDebug;
+extern cc_int32_t FIMDebug;
+extern cc_int32_t LSMDebug;
+extern cc_int32_t VCMDebug;
+extern cc_int32_t PLATDebug;
+extern cc_int32_t CCEVENTDebug;
+extern cc_int32_t FSMDebugSM;
+extern int32_t CSMDebugSM;
+extern cc_int32_t CCDebug;
+extern cc_int32_t CCDebugMsg;
+extern cc_int32_t AuthDebug;
+extern int32_t DebugDTMFOutofBand;
+extern int32_t DebugFlash;
+
+extern int32_t StrlibDebug;
+extern cc_int32_t ConfigDebug;
+
+extern int32_t DebugMalloc;
+extern int32_t DebugMallocTable;
+extern int32_t SoftkeyDebug;
+extern int32_t arp_debug_flag;
+extern int32_t arp_debug_broadcast_flag;
+extern int32_t cdp_debug;
+extern int32_t timestamp_debug;
+extern int32_t cdp_debug;
+
+extern int32_t HTTPDebug;
+extern cc_int32_t KpmlDebug;
+extern cc_int32_t DpintDebug;
+extern cc_int32_t g_cacDebug;
+extern cc_int32_t g_blfDebug;
+extern int32_t Dpint32_tDebug;
+extern cc_int32_t TNPDebug;
+extern cc_int32_t g_configappDebug;
+extern cc_int32_t g_CCAppDebug;
+extern cc_int32_t g_CCLogDebug;
+extern cc_int32_t g_dcsmDebug;
+extern cc_int32_t g_DEFDebug;
+
+extern cc_int32_t g_NotifyCallDebug;
+extern cc_int32_t g_NotifyLineDebug;
+
+// ---------------- definitions based on bug_printf -----------------
+
+ // Now some level definitions for buf_printf
+#define BUG_BROADCAST 0  // can't be disabled
+#define BUG_EMERGENCY 1
+#define BUG_ALERT     2
+#define BUG_CRITICAL  3
+#define BUG_ERROR     4
+#define BUG_WARNING   5
+#define BUG_NOTICE    6
+#define BUG_INFO      7
+#define BUG_DEBUG     8  // debugs and such
+
+extern int32_t bug_printf(int32_t level, const char *_format, ...);
+extern int32_t nbuginf(const char *_format, ...);
+
+#define logMsg buginf
+
+/* SIP debug macros */
+#define CCSIP_DEBUG_MESSAGE     if (SipDebugMessage) buginf
+#define CCSIP_DEBUG_MESSAGE_PKT if (SipDebugMessage) platform_print_sip_msg
+#define CCSIP_DEBUG_STATE       if (SipDebugState) buginf
+#define CCSIP_DEBUG_TASK        if (SipDebugTask) buginf
+#define CCSIP_DEBUG_REG_STATE   if (SipDebugRegState) buginf
+#define CCSIP_DEBUG_TRX         if (SipDebugTrx) buginf
+#define CCSIP_DEBUG_DM          if (SipDebugDM) buginf
+
+/* Platform debug macros */
+#define PHN_DEBUG_SNTP        if (DebugSNTP)
+#define PHN_DEBUG_SNTP_PACKET if (DebugSNTPPacket)
+
+/* TNP adapter debugs */
+#define TNP_DEBUG  if (TNPDebug) buginf
+
+#define DEF_DISPLAY_BUF          128
+/*
+ * Only used in the IOS SIP Parser routines
+ */
+#define CCSIP_ERR_MSG           err_msg
+#define CCSIP_DEBUG_ERROR       err_msg
+#define CCSIP_ERR_DEBUG         if (1)
+#define CCSIP_INFO_DEBUG        if (1)
+
+#define TMR_DEBUG     if (TMRDebug)    buginf
+#define GSM_DEBUG     if (GSMDebug)    buginf
+#define FIM_DEBUG     if (FIMDebug)    buginf
+#define LSM_DEBUG     if (LSMDebug)    buginf
+#define CALL_EVENT     if (CCEVENTDebug)    buginf
+#define FSM_DEBUG_SM  if (FSMDebugSM)  buginf
+#define CSM_DEBUG_SM  if (CSMDebugSM)  buginf
+#define DEF_DEBUG     if (g_DEFDebug) notice_msg
+
+#define CC_DEBUG      if (CCDebug)       buginf
+#define CC_DEBUG_MSG  if (CCDebugMsg)
+#define AUTH_DEBUG    if (AuthDebug)     buginf
+#define ARP_DEBUG     if (arp_debug_flag) buginf
+#define ARP_BCAST_DEBUG if (arp_debug_broadcast_flag) buginf
+#define CDP_DEBUG     if (cdp_debug) buginf
+
+#define NOTIFY_CALL_DEBUG if (g_NotifyCallDebug) buginf
+#define NOTIFY_LINE_DEBUG if (g_NotifyLineDebug) buginf
+
+/* String library macro */
+#define CONFIG_DEBUG  if (ConfigDebug) buginf
+#define FLASH_DEBUG   if (DebugFlash) buginf
+#define KPML_DEBUG    if (KpmlDebug) buginf
+#define CAC_DEBUG    if (g_cacDebug) buginf
+#define DCSM_DEBUG    if (g_dcsmDebug) buginf
+#define BLF_DEBUG    if (g_blfDebug) buginf
+#define BLF_ERROR  err_msg
+#define MISC_APP_DEBUG  if (g_miscAppDebug) buginf
+#define DPINT_DEBUG   if (DpintDebug) buginf
+#define CONFIGAPP_DEBUG  if (g_configappDebug) buginf
+#define CCAPP_DEBUG  if (g_CCAppDebug) buginf
+#define CCLOG_DEBUG  if (g_CCLogDebug) buginf
+#define MSP_DEBUG    if (VCMDebug) buginf
+#define CCAPP_ERROR    err_msg
+#define MSP_ERROR    err_msg
+#define CCAPP_ERROR  err_msg
+#define TNP_ERR  err_msg
+#define VCM_ERR  err_msg
+#define DEF_ERROR  err_msg
+#define DEF_ERR    err_msg
+
+#define APP_NAME "SIPCC-"
+#define DEB_L_C_F_PREFIX APP_NAME"%s: %d/%d, %s: "
+#define DEB_L_C_F_PREFIX_ARGS(msg_name, line, call_id, func_name) \
+       msg_name, line, call_id, func_name
+#define DEB_F_PREFIX APP_NAME"%s: %s: "
+#define DEB_F_PREFIX_ARGS(msg_name, func_name) msg_name, func_name
+#define GSM_L_C_F_PREFIX "GSM : %d/%d : %s : " // requires 3 args: line_id, call_id, fname
+#define GSM_F_PREFIX "GSM : %s : " // requires 1 arg: fname
+#define GSM_DEBUG_ERROR       err_msg
+#define LSM_L_C_F_PREFIX "LSM : %d/%d : %s : " // requires 3 args: line_id, call_id, fname
+#define LSM_F_PREFIX "LSM : %s : " // requires 1 arg: fname
+#define CCA_L_C_F_PREFIX "CCA : %d/%d : %s : " // requires 3 args: line_id, call_id, fname
+#define CCA_F_PREFIX "CCA : %s : " // requires 1 arg: fname
+#define MSP_F_PREFIX "SIPCC-MSP: %s: "
+#define DEB_NOTIFY_PREFIX "AUTO.%s:"
+
+#define MISC_F_PREFIX "MSC : %s : " // requires 1 arg: fname
+
+#define KPML_L_C_F_PREFIX "KPM : %d/%d : %s : " // requires 3 args: line_id, call_id, fname
+#define KPML_F_PREFIX "KPM : %s : " // requires 1 arg: fname
+#define KPML_ERROR       err_msg
+#define CAC_ERROR        err_msg
+#define DCSM_ERROR        err_msg
+#define CONFIG_ERROR        err_msg
+#define PLAT_ERROR        err_msg
+
+#define CAC_L_C_F_PREFIX "CAC : %d/%d : %s : " // requires 3 args: line_id, call_id, fname
+#define CAC_F_PREFIX "CAC : %s : " // requires 1 arg: fname
+#define DCSM_L_C_F_PREFIX "DCSM : %d/%d : %s : " // requires 3 args: line_id, call_id, fname
+#define DCSM_F_PREFIX "DCSM : %s : " // requires 1 arg: fname
+#define CFG_F_PREFIX "CFG : %s : " // requires 1 arg: fname
+#define PLAT_COMMON_F_PREFIX "PLAT_COMMON : %s : " // requires 1 arg: fname
+#define MED_F_PREFIX "MED : %s : "
+#define MED_L_C_F_PREFIX "MED :  %d/%d : %s : " // line/call/fname as arg
+
+
+/* Debug MSGNAMEs */
+#define CC_API "CC_API" // CCAPI debugs
+#define FSM "FSM" // FSM debugs
+#define GSM "GSM" // gsm related debugs
+#define DCSM "DCSM" // gsm related debugs
+#define SIP_STATE "SIP_STATE"
+#define SIP_EVT "SIP_EVT" // SIP events
+#define SIP_MSG_RECV "SIP_MSG_RECV"
+#define SIP_MSG_SEND "SIP_MSG_SEND"
+#define SIP_REG_STATE "SIP_REG_STATE"
+#define SIP_REG_FREE_FALLBACK "SIP_REG_FREE_FALLBACK"
+#define DP_API "DP_API" // dial plan
+#define KPML_INFO "KPML_INFO"
+#define BLF_INFO "BLF_INFO"// presence information
+#define MED_API "MED_API" // media api
+#define UI_API "UI_API"
+#define FIM "FIM" // FIM debugs
+#define LSM "LSM"
+#define SIP_AUTH "SIP_AUTH"
+#define TNP_INIT "TNP_INIT"
+#define TNP_BLF "TNP_BLF"
+#define RM "RM" // resource manager
+#define SIP_KA "SIP_KA" // SIP keepalive
+#define JNI "JNI"
+#define CONFIG_API "CONFIG_API" // config table API
+#define BLF "BLF"
+#define CONFIG_APP "CONFIG_APP"
+#define SIP_MSG_QUE "SIP_MSG_QUE" // SIP message queue
+#define SIP_TRANS "SIP_TRANS" // SIP transport
+#define HTTPISH "HTTPISH" // Network to structure translation
+#define SIP_MSG "SIP_MSG"
+#define SIP_TIMER "SIP_TIMER"
+#define SIP_ID "SIP_ID"
+#define SIP_FWD "SIP_FWD"
+#define SIP_NOTIFY "SIP_NOTIFY"
+#define SIP_TASK "SIP_TASK"
+#define SIP_IP_MATCH "SIP_IP_MATCH"
+#define SIP_SUB "SIP_SUB" // SIP subscription
+#define SIP_SDP "SIP_SDP"
+#define SIP_STORE "SIP_STORE"
+#define SIP_PUB "SIP_PUB" // SIP publish
+//#define SIP_UDP "SIP_UDP"
+#define SIP_TLS "SIP_TLS"
+//#define SIP_TCP "SIP_TCP"
+#define SIP_SDP "SIP_SDP"
+#define SIP_REP "SIP_REP" // SIP replace info
+#define SIP_PROXY "SIP_PROXY"
+#define SIP_CALL_STATUS "SIP_CALL_STATUS"
+#define SIP_ACK "SIP_ACK" // SIP invite response ack
+#define CPR "CPR"
+#define SIP_BRANCH "SIP_BRANCH"
+#define SIP_CTRL "SIP_CTRL"
+#define SIP_TRX "SIP_TRX" // SIP transaction
+#define SIP_ROUTE "SIP_ROUTE"
+#define SIP_CALL_ID "SIP_CALL_ID"
+#define SIP_CSEQ "SIP_CSEQ"
+#define SIP_TAG "SIP_TAG"
+#define SIP_RESP "SIP_RESP" // SIP response message
+#define SIP_REQ_URI "SIP_REQ_URI"
+#define SIP "SIP"
+#define SIP_REG "SIP_REG"
+#define SIP_CCM_RESTART "SIP_CCM_RESTART"
+#define SIP_CRED "SIP_CRED" // SIP registration credentials
+#define SIP_BUTTON "SIP_BUTTON"
+#define SIP_SUB_RESP "SIP_SUB_RESP" // SIP subscription response
+#define SIP_NOTIFY "SIP_NOTIFY"
+#define SIP_FALLBACK "SIP_FALLBACK"
+#define SIP_FAILOVER "SIP_FAILOVER"
+#define SIP_STANDBY "SIP_STANDBY"
+#define SIP_CC_CONN "SIP_CC_CONN" // SIP CC connections
+#define SIP_CONFIG "SIP_CONFIG"
+#define DIALPLAN "DIALPLAN"
+#define SIP_REQ_DIGEST "SIP_REQ_DIGEST"
+#define SIP_CC_INIT "SIP_CC_INIT"
+#define SIP_CC_PROV "SIP_CC_PROV" // CC Provider
+#define SIP_SES_HASH "SIP_SES_HASH" // session hash
+#define PLAT_API "PLAT_API" // platform API
+#define CHUNK "CHUNK"
+#define MISC_UTIL "MISC_UTIL"
+#define SIP_CC_SES "SIP_CC_SES" // CC session
+#define CAPF_UI "CAPF_UI"
+#define CC_API_NATIVE "CC_API_NATIVE"
+#define TANDUN_STUB "TANDUN_STUB"
+#define SIP_SOCK "SIP_SOCK"
+#define SIP_TCP_MSG "SIP_TCP_MSG"
+#define MS_PROVIDER "MSP"
+#define SIP_CONTENT_TYPE "SIP_CONTENT_TYPE"
+#define SIP_INFO_PACKAGE "SIP_INFO_PACKAGE"
+
+#endif
diff --git a/libs/sipcc/core/includes/phone_platform_constants.h b/libs/sipcc/core/includes/phone_platform_constants.h
new file mode 100755 (executable)
index 0000000..1c0aa60
--- /dev/null
@@ -0,0 +1,210 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PHONE_PLATFORM_CONSTANTS_H_
+#define _PHONE_PLATFORM_CONSTANTS_H_
+
+// Defines for the various phone models. Note that the device numbers
+// appearing after the model name are pre-determined when new phone models
+// are added and must correspond to the ones programmed into CCM
+#define SIP_FIRMWARE_VERSION              "9.1.1"
+
+// Legacy
+#define PHONE_MODEL_NUMBER_7940           "8"
+#define PHONE_MODEL_NUMBER_7960           "7"
+#define LEGACY_MODEL_7940                 "CP-7940G"
+#define LEGACY_MODEL_7960                 "CP-7960G"
+#define CCSIP_SIP_7940_USER_AGENT         "CP7940G"
+#define CCSIP_SIP_7960_USER_AGENT         "CP7960G"
+
+// Communicator
+// Note: The Communicator will send its native device number of 30016
+// so that the CCM will recognize it as a CIPC. If talking to an older CCM
+// that expects the Communicator to register as a 7970, please define
+// _CIPC_7970_ in SIP project file
+#ifdef _CIPC_7970_
+#define PHONE_MODEL_NUMBER_COMMUNICATOR   "30006"
+#else
+#define PHONE_MODEL_NUMBER_COMMUNICATOR   "30016"
+#endif
+#define CCSIP_SIP_COMMUNICATOR_USER_AGENT "SIPIPCommunicator"
+
+// UC core
+#define PHONE_MODEL_NUMBER_UCCORE   "358"
+#define CCSIP_SIP_UCCORE_USER_AGENT "SIPUCCore"
+
+// 8961
+#define PHONE_MODEL_NUMBER_8961           "540"
+#define RT_MODEL_8961                     "CP-8961"
+#define CCSIP_SIP_8961_USER_AGENT         "CP8961"
+
+// 9951
+#define PHONE_MODEL_NUMBER_9951           "537"
+#define RT_MODEL_9951                     "CP-9951"
+#define CCSIP_SIP_9951_USER_AGENT         "CP9951"
+
+// 9971
+#define PHONE_MODEL_NUMBER_9971           "493"
+#define RT_MODEL_9971                     "CP-9971"
+#define CCSIP_SIP_9971_USER_AGENT         "CP9971"
+
+// 7970
+#define PHONE_MODEL_NUMBER_7970           "30006"
+#define TNP_MODEL_7970                    "CP-7970G"
+#define CCSIP_SIP_7970_USER_AGENT         "CP7970G"
+
+// 7971
+#define PHONE_MODEL_NUMBER_7971           "119"
+#define TNP_MODEL_7971                    "CP-7971G-GE"
+#define CCSIP_SIP_7971_USER_AGENT         "CP7971G-GE"
+
+// 7911
+#define PHONE_MODEL_NUMBER_7911           "307"
+#define TNP_MODEL_7911                    "CP-7911G"
+#define CCSIP_SIP_7911_USER_AGENT         "CP7911G"
+
+// 7906
+#define PHONE_MODEL_NUMBER_7906           "369"
+#define TNP_MODEL_7906                    "CP-7906G"
+#define CCSIP_SIP_7906_USER_AGENT         "CP7906G"
+
+// 7931
+#define PHONE_MODEL_NUMBER_7931           "348"
+#define TNP_MODEL_7931                    "CP-7931G"
+#define CCSIP_SIP_7931_USER_AGENT         "CP7931G"
+
+// Maximum Lines on 794X models
+#define MAX_REG_LINES_794X                 2
+
+// 7941
+#define PHONE_MODEL_NUMBER_7941           "115"
+#define TNP_MODEL_7941                    "CP-7941G"
+#define CCSIP_SIP_7941_USER_AGENT         "CP7941G"
+
+// 7941 GE
+#define PHONE_MODEL_NUMBER_7941GE         "309"
+#define TNP_MODEL_7941GE                  "CP-7941G-GE"
+#define CCSIP_SIP_7941_GE_USER_AGENT      "CP7941G-GE"
+
+// 7961
+#define PHONE_MODEL_NUMBER_7961           "30018"
+#define TNP_MODEL_7961                    "CP-7961G"
+#define CCSIP_SIP_7961_USER_AGENT         "CP7961G"
+
+// 7961 GE
+#define PHONE_MODEL_NUMBER_7961GE         "308"
+#define TNP_MODEL_7961GE                  "CP-7961G-GE"
+#define CCSIP_SIP_7961_GE_USER_AGENT      "CP7961G-GE"
+
+// 7942
+#define PHONE_MODEL_NUMBER_7942           "434"
+#define TNP_MODEL_7942                    "CP-7942G"
+#define CCSIP_SIP_7942_USER_AGENT         "CP7942G"
+
+// 7945
+#define PHONE_MODEL_NUMBER_7945           "435"
+#define TNP_MODEL_7945                    "CP-7945G"
+#define CCSIP_SIP_7945_USER_AGENT         "CP7945G"
+
+// 7962
+#define PHONE_MODEL_NUMBER_7962           "404"
+#define TNP_MODEL_7962                    "CP-7962G"
+#define CCSIP_SIP_7962_USER_AGENT         "CP7962G"
+
+// 7965
+#define PHONE_MODEL_NUMBER_7965           "436"
+#define TNP_MODEL_7965                    "CP-7965G"
+#define CCSIP_SIP_7965_USER_AGENT         "CP7965G"
+
+// 7975
+#define PHONE_MODEL_NUMBER_7975           "437"
+#define TNP_MODEL_7975                    "CP-7975G"
+#define CCSIP_SIP_7975_USER_AGENT         "CP7975G"
+
+// CSF
+#define PHONE_MODEL_NUMBER_CSF            "503"
+#define CSF_MODEL                         "CSF"
+#define CCSIP_SIP_CSF_USER_AGENT          "IKRAN"
+
+// CIUS
+#define PHONE_MODEL_NUMBER_CIUS           "593"
+#define CIUS_MODEL                        "CP-CIUS"
+#define CCSIP_SIP_CIUS_USER_AGENT         "CPCIUS"
+
+// SOUNDWAVE
+#define PHONE_MODEL_NUMBER_SOUNDWAVE      "575"
+#define SOUNDWAVE_MODEL                   "SOUNDWAVE"
+#define CCSIP_SIP_SOUNDWAVE_USER_AGENT    "SOUNDWAVE"
+
+
+
+
+#define MAX_SIDECAR_LINES       28  // Max number of lines supported on sidecars
+
+#define MAX_BKEM_LINES          48  // Max number of lines supported on BKEMs
+
+/****************************************************
+ * Start: definitions for vendor's phones
+ */
+//Definition for 6901
+#define PHONE_MODEL_NUMBER_6901      "547"
+#define RTLITE_MODEL_6901            "CP-6901"
+#define CCSIP_SIP_6901_USER_AGENT    "CP6901"
+
+//Definitions for 6911
+#define PHONE_MODEL_NUMBER_6911      "548"
+#define RTLITE_MODEL_6911            "CP-6911"
+#define CCSIP_SIP_6911_USER_AGENT    "CP6911"
+
+//Definition for 6921
+#define PHONE_MODEL_NUMBER_6921      "495"
+#define RTLITE_MODEL_6921            "CP-6921"
+#define CCSIP_SIP_6921_USER_AGENT    "CP6921"
+
+//Definition for 6941
+#define PHONE_MODEL_NUMBER_6941      "496"
+#define RTLITE_MODEL_6941            "CP-6941"
+#define CCSIP_SIP_6941_USER_AGENT    "CP6941"
+
+//Definition for 6945
+#define PHONE_MODEL_NUMBER_6945      "564"
+#define RTLITE_MODEL_6945            "CP-6945"
+#define CCSIP_SIP_6945_USER_AGENT    "CP6945"
+
+//Definition for 6961
+#define PHONE_MODEL_NUMBER_6961      "497"
+#define RTLITE_MODEL_6961            "CP-6961"
+#define CCSIP_SIP_6961_USER_AGENT    "CP6961"
+
+//Definition for 7937
+#define PHONE_MODEL_NUMBER_7937      "431"
+#define RTLITE_MODEL_7937            "7937G"
+#define CCSIP_SIP_7937_USER_AGENT    "7937G"
+/**
+ * End of vendor's definition
+ ******************************************************/
+
+//Default set
+#define PHONE_MODEL_NUMBER      "493"
+#define PHONE_MODEL             "CP-9971"
+#define CCSIP_SIP_USER_AGENT    "CP9971"
+
+#define MAX_PHONE_LINES       8
+#define MAX_REG_LINES        51
+#define MAX_CALLS            51
+#define MAX_CALLS_PER_LINE   51
+
+/*
+ * MAX_INSTANCES (call_instances) should equal to maximum number of calls
+ * allowed by the phone but MAX_CALLS is defined to be 1 more than the
+ * actual maximum capacity. Therefore define MAX_INSTANCES to MAX_CALLS -1
+ */
+#define MAX_INSTANCES        (MAX_CALLS - 1) /* max number of instance ID */
+
+/* MAX_CONFIG_LINES - java side defined fixed number to 36 for non-Buckfast TNP
+ * models so use 36 in call cases. Changing this needs java side change
+ */
+#define MAX_CONFIG_LINES 51
+
+#endif
diff --git a/libs/sipcc/core/includes/phone_types.h b/libs/sipcc/core/includes/phone_types.h
new file mode 100644 (file)
index 0000000..0b4f11f
--- /dev/null
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PHONE_TYPES_H_
+#define _PHONE_TYPES_H_
+
+#include "sessionConstants.h"
+#include "cc_constants.h"
+
+#define MAC_ADDRESS_LENGTH 6
+
+typedef cc_lineid_t line_t;
+typedef cc_callid_t callid_t;
+typedef cc_groupid_t groupid_t;
+typedef unsigned short calltype_t;
+typedef unsigned short media_refid_t;
+typedef cc_streamid_t streamid_t;
+typedef cc_mcapid_t mcapid_t;
+
+typedef enum {
+    NORMAL_CALL = CC_ATTR_NORMAL,
+    XFR_CONSULT = CC_ATTR_XFR_CONSULT,
+    CONF_CONSULT = CC_ATTR_CONF_CONSULT,
+    ATTR_BARGING = CC_ATTR_BARGING,
+    RIUHELD_LOCKED = CC_ATTR_RIUHELD_LOCKED,
+    LOCAL_CONF_CONSULT = CC_ATTR_LOCAL_CONF_CONSULT,
+    ATTR_MAX
+} call_attr_t;
+
+#define CC_GCID_LEN           (129)
+#endif
diff --git a/libs/sipcc/core/includes/platform_api.h b/libs/sipcc/core/includes/platform_api.h
new file mode 100644 (file)
index 0000000..f791e67
--- /dev/null
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PLATFORM_API_H_
+#define _PLATFORM_API_H_
+
+#include "cpr_types.h"
+#include "cpr_socket.h"
+#include "ccsip_pmh.h"
+#include "plat_api.h"
+#include "sessionTypes.h"
+
+void     platform_get_wired_mac_address(uint8_t *addr);
+void     platform_get_active_mac_address(uint8_t *addr);
+void platform_get_ip_address(cpr_ip_addr_t *ip_addr);
+cpr_ip_mode_e platform_get_ip_address_mode(void);
+void platform_apply_config (char * configVersionStamp, char * dialplanVersionStamp, char * fcpVersionStamp, char * cucmResult, char * loadId, char * inactiveLoadId, char * loadServer, char * logServer, boolean ppid);
+
+/**
+ * Set ip address mode
+ * e.g.
+ */
+cpr_ip_mode_e platGetIpAddressMode();
+
+/**
+ * @brief Given a msg buffer, returns a pointer to the buffer's header
+ *
+ * The cprGetSysHeader function retrieves the system header buffer for the
+ * passed in message buffer.
+ *
+ * @param[in] buffer  pointer to the buffer whose sysHdr to return
+ *
+ * @return        Abstract pointer to the msg buffer's system header
+ *                or #NULL if failure
+ */
+void *
+cprGetSysHeader (void *buffer);
+
+/**
+ * @brief Called when the application is done with this system header
+ *
+ * The cprReleaseSysHeader function returns the system header buffer to the
+ * system.
+ * @param[in] syshdr  pointer to the sysHdr to be released
+ *
+ * @return        none
+ */
+void
+cprReleaseSysHeader (void *syshdr);
+
+#endif
diff --git a/libs/sipcc/core/includes/pres_sub_not_handler.h b/libs/sipcc/core/includes/pres_sub_not_handler.h
new file mode 100644 (file)
index 0000000..bc021a7
--- /dev/null
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PRES_SUB_NOT_HANDLER_H
+#define PRES_SUB_NOT_HANDLER_H
+
+#include "cpr_types.h"
+
+extern void pres_get_state(int request_id, int duration, const char *watcher,
+                           const char *presentity, int app_id, int feature_mask);
+extern void pres_terminate_req(int request_id);
+extern void pres_terminate_req_all(void);
+extern void pres_process_msg_from_msgq(uint32_t cmd, void *msg_p);
+extern cpr_status_e pres_create_retry_after_timers(void);
+extern void pres_destroy_retry_after_timers(void);
+extern void pres_play_blf_audible_alert(void);
+extern void pres_sub_handler_initialized(void);
+
+#endif /* PRES_SUB_NOT_HANDLER_H */
diff --git a/libs/sipcc/core/includes/publish_int.h b/libs/sipcc/core/includes/publish_int.h
new file mode 100644 (file)
index 0000000..c76b666
--- /dev/null
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PUBLISH_INT_H
+#define PUBLISH_INT_H
+
+#include "ccsip_subsmanager.h"
+#include "cpr_types.h"
+#include "ccsip_callinfo.h"
+
+
+typedef uint32_t pub_handle_t;
+#define NULL_PUBLISH_HANDLE  0 /* this is to indicate that handle is not assigned  yet */
+
+
+typedef struct  {
+    pub_handle_t         pub_handle;  // handle assigned by the SIP stack
+    pub_handle_t         app_handle;  // handle assigned by the Application
+    char                 ruri[MAX_URI_LENGTH];  // Address of the resource
+    char                 esc[MAX_URI_LENGTH];   // Event State Compositor
+    uint32_t             expires;    // suggested Expires in seconds
+    cc_subscriptions_t   event_type; // EventType, for example, prescence
+    ccsip_event_data_t  *event_data_p;  // Event Data
+    cc_srcs_t            callback_task;  // CallBack Task ID
+    int                  resp_msg_id; //Response Msg ID
+} pub_req_t;
+
+typedef struct {
+     unsigned int       resp_code;  // response code
+     pub_handle_t       pub_handle; // handle assigned by the SIP stack
+     pub_handle_t       app_handle; // handle assigned by the Application
+} pub_rsp_t;
+
+extern
+void publish_init(pub_handle_t             app_handle,
+                  char                    *ruri,
+                  char                    *esc,
+                  unsigned int             expires,
+                  cc_subscriptions_t       event_type,
+                  ccsip_event_data_t      *event_data_p,
+                  cc_srcs_t                callback_task,
+                  int                      message_id
+                 );
+
+extern
+void publish_update(pub_handle_t          pub_handle,
+                    cc_subscriptions_t    event_type,
+                    ccsip_event_data_t   *event_data_p,
+                    cc_srcs_t             callback_task,
+                    int                   message_id
+                   );
+
+extern
+void publish_terminate(pub_handle_t          pub_handle,
+                       cc_subscriptions_t    event_type,
+                       cc_srcs_t             callback_task,
+                       int                   message_id
+                      );
+
+extern
+cc_rcs_t publish_int_response(pub_rsp_t               *pub_rsp_p,
+                              cc_srcs_t                callback_task,
+                              int                      message_id
+                             );
+
+#endif  /*PUBLISH_INT_H*/
+
+
+
diff --git a/libs/sipcc/core/includes/rcc_int_types.h b/libs/sipcc/core/includes/rcc_int_types.h
new file mode 100755 (executable)
index 0000000..b55e00e
--- /dev/null
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RCC_INT_TYPES_H_
+#define _RCC_INT_TYPES_H_
+
+#include "phone_types.h"
+#include "xml_parser_defines.h"
+#include "sll_lite.h"
+
+// The table below should be kept in sync with the enum type
+// in the RemoteCC XML
+typedef enum {
+    RCC_SOFTKEY_UNDEFINED = 0,
+    RCC_SOFTKEY_REDIAL = 1,
+    RCC_SOFTKEY_NEWCALL,
+    RCC_SOFTKEY_HOLD,
+    RCC_SOFTKEY_TRANSFER,
+    RCC_SOFTKEY_CFWDALL,
+    RCC_SOFTKEY_CFWDBUSY,
+    RCC_SOFTKEY_CFWDNOANS,
+    RCC_SOFTKEY_BACKSPACE,
+    RCC_SOFTKEY_ENDCALL,
+    RCC_SOFTKEY_RESUME,
+    RCC_SOFTKEY_ANSWER,
+    RCC_SOFTKEY_INFO,
+    RCC_SOFTKEY_CONFERENCE,
+    RCC_SOFTKEY_JOIN,
+    RCC_SOFTKEY_RMLASTCONF,
+    RCC_SOFTKEY_BARGE,
+    RCC_SOFTKEY_DIRECTXFER,
+    RCC_SOFTKEY_SELECT,
+    RCC_SOFTKEY_CBARGE,
+    RCC_SOFTKEY_DIV_ALL,
+    RCC_SOFTKEY_TRANSFERTOVM,
+    RCC_SOFTKEY_UNSELECT,
+    RCC_SOFTKEY_CANCEL,
+    RCC_SOFTKEY_CONFDETAILS,
+    RCC_IPMA_SKEY_TRANSFAS = 66,
+    RCC_IPMA_SKEY_INTRCPT = 67,
+    RCC_IPMA_SKEY_TRANSFVM = 68,
+    RCC_IPMA_SKEY_TRANSMG = 69,
+    RCC_IPMA_SKEY_SETWTCH = 70,
+} rcc_softkey_event_t;
+
+/*
+ * 400 - Invalid line handle
+ * 401 - Invalid calling address
+ * 402 - Invalid called address
+ * 403 - User data too large
+ * 404 - PassThrough data too large
+ * 405 - Cannot process request in current line state
+ */
+
+typedef enum {
+    RCC_ERR = -1,
+    RCC_NONE = 0,
+    RCC_TRYING = 100,
+    RCC_SUCCESS = 200,
+    RCC_SUCCESS_COMPLETE = 201,
+    RCC_COMPLETE = 300,
+    RCC_FAILED = 400,
+    RCC_INVALID_LINE = 401,
+    RCC_INVALID_CALLING_ADDR = 402,
+    RCC_INVALID_CALLED_ADDR = 403,
+    RCC_LARGE_DATA = 404,
+    RCC_PROCESS_REQ_FAILED = 405,
+    RCC_RESP_MAX
+} rcc_resp_code_e;
+
+#endif //_RCC_INT_TYPES_H_
diff --git a/libs/sipcc/core/includes/regexp.h b/libs/sipcc/core/includes/regexp.h
new file mode 100644 (file)
index 0000000..99fb19b
--- /dev/null
@@ -0,0 +1,83 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __REGEXP_H__
+#define __REGEXP_H__
+
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#define NSUBEXP  10
+struct regexp_ {
+    char *startp[NSUBEXP];
+    char *endp[NSUBEXP];
+    char regflag;       /* Flags used for pattern matching */
+    char regstart;      /* Internal use only. */
+    char reganch;       /* Internal use only. */
+    char regatom;       /* Internal use only. */
+    char *regmust;      /* Internal use only. */
+    int regmlen;        /* Internal use only. */
+    char program[1];    /* Unwarranted chumminess with compiler. */
+};
+
+typedef struct regexp_ regexp;
+
+
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define MAGIC    0234
+
+/*
+ * The parameters for regflag
+ */
+#define REG_NOSUB 0x02   /* Don't do sub-string matches */
+
+/* Return Values from regexecstring()
+ */
+typedef enum {
+    REG_NO_MATCH = 0,
+    REG_MATCHED,
+    REG_MAYBE
+} regval;
+
+\f
+/***********************************************************************
+ *
+ *                      Externs and Prototypes
+ *
+ ***********************************************************************/
+
+/*
+ * regexp.c
+ */
+extern char *reg(int, int *);
+extern char *regatom(int *);
+extern char *regbranch(int *);
+extern char *regnode(char);
+extern char *regpiece(int *);
+extern char *regprop(char *);
+extern int regexec(regexp *, char *);
+extern int regmatch(char *);
+extern int regrepeat(char *);
+extern int regtry(regexp *, char *);
+extern regexp *regcomp(char *, char);
+extern void regc(char);
+extern void regdump(regexp *);
+extern void regerror(char *);
+extern void reginsert(char, char *);
+extern void regoptail(char *, char *);
+extern void regtail(char *, char *);
+extern regval regexecstring(regexp *, char *);
+
+/*
+ * regsub.c
+ */
+extern int regsub(regexp *, char *, char *, int);
+
+#endif /* __REGEXP_H__ */
diff --git a/libs/sipcc/core/includes/ringlist.h b/libs/sipcc/core/includes/ringlist.h
new file mode 100755 (executable)
index 0000000..71ac3bd
--- /dev/null
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RINGLIST_H
+#define RINGLIST_H
+
+#define MAX_RINGS              56
+#define RING_LIST_BUFFER_SIZE  (MAX_RINGS*64)
+
+extern uint16_t rng_get_num_ringers(void);
+extern int8_t *rng_get_ring_name(uint16_t);
+extern void rng_play_ring(uint16_t);
+extern void rng_set_ringer(void);
+extern boolean rng_select_ringer(uint16_t);
+extern uint16_t rng_get_selected_ringnum(void);
+extern int16_t handle_ringlist(int16_t, void *);
+extern void rng_request_ringlist(void);
+extern void rng_init(void);
+extern void rng_cancel_ring(void);
+
+#endif
diff --git a/libs/sipcc/core/includes/rtp_defs.h b/libs/sipcc/core/includes/rtp_defs.h
new file mode 100644 (file)
index 0000000..b12f5fe
--- /dev/null
@@ -0,0 +1,123 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RTP_DEFS_H
+#define RTP_DEFS_H
+
+#include "cpr_types.h"
+
+#define SAMPLES_TO_MILLISECONDS(SAMPLES) ((SAMPLES)>>3)
+#define MILLISECONDS_TO_SAMPLES(PERIOD)  ((PERIOD)<<3)
+
+#define MAX_TX_RTP_PORTS             2
+#define MAX_FRAMES_PER_PACKET        6
+#define MAX_VOICE_FRAME_SIZE       320
+
+#define GSM_EFR_FRAME_SIZE          32
+#define GSM_FR_FRAME_SIZE           33
+#define G729_FRAME_SIZE             10
+#define G723_FRAME_SIZE63           24
+#define G723_FRAME_SIZE53           20
+#define G723_SID_FRAME_SIZE          4
+#define G729_SID_FRAME_SIZE          2
+
+#define GSM_SAMPLES_PER_FRAME      160
+#define G729_SAMPLES_PER_FRAME      80
+#define G723_SAMPLES_PER_FRAME     240
+#define LINEAR_16KHZ_SAMPLES_PER_FRAME  160  // 10 ms = 160 samples @ 16 kHz
+
+#define MAX_ARM_TO_DSP_CHANNEL  3
+#define MAX_DSP_TO_ARM_CHANNEL  2
+#define HALF_SIZE_DATA_INGRESS  240
+#define RX_MAX MAX_ARM_TO_DSP_CHANNEL
+
+#define OPEN_OK                      0
+#define OPEN_ERROR_DUPLICATE        -1
+
+#define ASSIGN_TX_CHANNEL           (0x1)
+#define ASSIGN_RX_CHANNEL           (0x2)
+#define CHANNEL_CLOSE_IN_PROGRESS   (0x80000000)
+
+#define RTP_START_PORT              0x4000
+#define RTP_END_PORT                0x7FFE
+
+#define GET_DYN_PAYLOAD_TYPE_VALUE(a) ((a & 0XFF00) ? ((a & 0XFF00) >> 8) : a)
+#define SET_PAYLOAD_TYPE_WITH_DYNAMIC(a,b) ((a << 8) | b)
+
+
+//=============================================================================
+//
+//  Enumeration Types
+//
+//-----------------------------------------------------------------------------
+enum RTP_PAYLOAD_TYPES
+{
+    G711_MULAW_PAYLOAD_TYPE         = 0,
+    GSM_FR_PAYLOAD_TYPE             = 3,
+    G723_PAYLOAD_TYPE               = 4,
+    G711_ALAW_PAYLOAD_TYPE          = 8,
+    LINEAR_8KHZ_PAYLOAD_TYPE        = 12,
+    TYPE13_SID_PAYLOAD_TYPE         = 13,
+    G729_PAYLOAD_TYPE               = 18,
+    GSM_EFR_PAYLOAD_TYPE            = 20,
+    LINEAR_16KHZ_PAYLOAD_TYPE       = 25,
+    AVT_PAYLOAD_TYPE                = 101,
+    MASK_PAYLOAD_TYPE               = 0x7f
+};
+
+enum RTP_TRANSMIT_STATES
+{
+    RTP_TX_FRAME,
+    RTP_TX_START,
+    RTP_TX_NO_FRAME,
+    RTP_TX_END = RTP_TX_NO_FRAME,
+    RTP_TX_SID
+};
+
+enum RTP_RX_STATES
+{
+    RTP_RX_NORMAL,
+    RTP_RX_FLUSH_SOON,
+    RTP_RX_FLUSH_NOW
+};
+
+enum RTP_TALKERS_TYPES
+{
+    FIRST_TALKER = 0,
+    LAST_TALKER = RX_MAX - 1,
+    NO_TALKER
+};
+
+typedef enum
+{
+    RTP_INGRESS = 0,
+    RTP_EGRESS
+} t_RtpDirection;
+
+//=============================================================================
+//
+//  Structure/Type definitions
+//
+//-----------------------------------------------------------------------------
+typedef uint16_t rtp_channel_t;
+
+/********************************/
+/* RTP Call Stats Descriptor    */
+/*                              */
+/********************************/
+
+typedef struct
+{
+    int call_id;
+    unsigned long Rxduration;
+    unsigned long Rxpackets;
+    unsigned long Rxoctets;
+    unsigned long Rxlatepkts;
+    unsigned long Rxlostpkts;
+    unsigned long Txduration;
+    unsigned long Txpackets;
+    unsigned long Txoctets;
+} t_callstats;
+
+#endif
diff --git a/libs/sipcc/core/includes/scSession.h b/libs/sipcc/core/includes/scSession.h
new file mode 100755 (executable)
index 0000000..0525d19
--- /dev/null
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "string_lib.h"
+#include "sessionConstants.h"
+#include "sessionTypes.h"
+
+/* CallControl Provider Management Interfaces */
+void scSessionProviderCmd(sessionProvider_cmd_t *data);
+
+/* CallControl Provider Management Updates */
+void scSessionProviderState(unsigned int state, scProvider_state_t *data);
+
+/* Session mgmt */
+session_id_t scCreateSession(session_create_param_t *param);
+void scCloseSession(session_id_t sess_id);
+void scInvokeFeature(session_feature_t *featData);
+
+/* Session Updates */
+void scSessionUpdate(session_update_t *session);
+void scFeatureUpdate(feature_update_t *data);
+
+
diff --git a/libs/sipcc/core/includes/session.h b/libs/sipcc/core/includes/session.h
new file mode 100755 (executable)
index 0000000..2b2390a
--- /dev/null
@@ -0,0 +1,168 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SESSION_H_
+#define _SESSION_H_
+
+#include "sessionConstants.h"
+#include "sessionTypes.h"
+#include "sessuri.h"
+
+/**
+ *  sessionProviderCmd
+ *      Session Provider Management Interfaces
+ *      Called by Application to issue cmds to Session Provider
+ *
+ *  @param data -  sessionProvider_cmd_t
+ *              Contains the command session provider type and provider specific data
+ *
+ *  @return  none
+ *
+ */
+void sessionProviderCmd(sessionProvider_cmd_t *);
+
+/**
+ *  sessionProviderState
+ *      Method to report session provider state updates to Application
+ *
+ *  @param state  - provider_state_t
+ *                Contains the INS/OOS state along with provider specific data
+ *
+ *  @return  none
+ *
+ */
+void sessionProviderState(provider_state_t *state);
+
+/**
+ *  createSession
+ *
+ *      Called to create a session of requested type
+ *
+ *  @param param - uri
+ *                indicates type of session and specific params
+ *
+ *  @return  ccSession_id_t - id of the session created
+ */
+
+session_id_t createSession(uri_t uri_info);
+
+/**
+ *  closeSession
+ *
+ *      Called to close an existing session
+ *
+ *  @param sess_id - session id of the session to be closed
+ *
+ *  @return  >=0 success, -1 failure
+ */
+
+int closeSession(session_id_t sess_id);
+
+/**
+ *  sessionCmd
+ *      Session Lifecycle Management Interfaces
+ *      Called by Application to manage Session States
+ *
+ *  @param data -  sessionCmd_t
+ *              Contains the command session type and session specific data
+ *
+ *  @return  none
+ *
+ */
+void sessionCmd(sessionCmd_t *sCmd);
+
+/**
+ *  invokeFeature
+ *
+ *      Called to invoke a feature on session or device
+ *
+ *  @param feat - feature specific data along with its id
+ *  @param featData  - Additional info if needed for the feature
+ *
+ *  @return  none
+ *
+ */
+
+void invokeFeature(session_feature_t *feat);
+
+/**
+ *  invokeProviderFeature
+ *
+ *      Called to invoke a feature on session or device
+ *
+ *  @param feat - feature specific data along with its id
+ *  @param featData  - Additional info if needed for the feature
+ *
+ *  @return  none
+ *
+ */
+
+void invokeProviderFeature(session_feature_t *feat);
+
+/**
+ *  sessionUpdate
+ *
+ *      Called by session provider to update session state and data
+ *
+ *  @param session - session_update_t
+ *                 Contains session specific event state and data
+ *
+ *  @return  none
+ *
+ */
+void sessionUpdate(session_update_t *session);
+
+/**
+ *  featureUpdate
+ *
+ *      Called by session provider to update feature state and data
+ *  not specific to a session
+ *
+ *  @param feature - feature specific events and data
+ *
+ *  @return  none
+ *
+ */
+void featureUpdate(feature_update_t *feature);
+
+
+/**
+ *  sessionMgmt
+ *
+ *      Called to manage various misc. functions of the device
+ *
+ *  @param sessMgmt - the data
+ *
+ *  @return  none
+ *
+ */
+void sessionMgmt (session_mgmt_t *sess_mgmt);
+
+/**
+ *  sessionSendInfo
+ *
+ *      Called to send an Info Package
+ *
+ *  @param send_info - the session ID and the Info Package to be sent
+ *
+ *  @return  none
+ *
+ */
+void sessionSendInfo (session_send_info_t *send_info);
+
+/**
+ *  sessionRcvdInfo
+ *
+ *      Called to forward a received Info Package (either parsed or unparsed)
+ *      to the Java side
+ *
+ *  @param rcvd_info - the session ID and Info Package received
+ *
+ *  @return  none
+ *
+ */
+void sessionRcvdInfo (session_rcvd_info_t *rcvd_info);
+
+#endif
+
diff --git a/libs/sipcc/core/includes/sessionConstants.h b/libs/sipcc/core/includes/sessionConstants.h
new file mode 100755 (executable)
index 0000000..a094c3d
--- /dev/null
@@ -0,0 +1,364 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SESSION_CONSTANTS_H_
+#define _SESSION_CONSTANTS_H_
+
+#include "cc_constants.h"
+
+typedef enum {
+    GROUP_TAG,
+    GROUP_SESSION_TYPE,
+    GROUP_CMD,
+    GROUP_STATE,
+    GROUP_CC_MODE,
+    GROUP_CC_REG_CAUSE,
+    GROUP_CC_FEATURE,
+    GROUP_DEVICE_FEATURE,
+    GROUP_RINGER_RESERVATION,
+    GROUP_CALL_STATE,
+    GROUP_CC_ATTR,
+    GROUP_CC_CALL_TYPE,
+    GROUP_CC_SECURITY,
+    GROUP_INFO_PKG_ID,
+    GROUP_CC_POLICY,
+    GROUP_UI_PRIVACY,
+    GROUP_SIP_BLF,
+    GROUP_SESSION_EVENT,
+    GROUP_CALL_EVENT,
+    GROUP_FILEPLAYER,
+    GROUP_MEDIA_EVENT,
+    GROUP_MEDIA_DIRECTION,
+    GROUP_PRIORITY,
+    GROUP_SESSION,
+    GROUP_CC_CAUSE
+} group_t;
+
+typedef enum {
+    TAG_LINE = 1L,
+    TAG_STATE,
+    TAG_CCM_ADDR,
+    TAG_STATUS,
+    TAG_LCLCFWD,
+    TAG_CFANUM,
+    TAG_COUNT,
+    TAG_INSTANCE,
+    TAG_TIMEOUT,
+    TAG_PRIORITY,
+    TAG_NOTPROG,
+    TAG_RESET_TYPE,
+    TAG_ATTR,
+    TAG_INST,
+    TAG_SECURITY,
+    TAG_CLD_NAME,
+    TAG_CLD_NUMB,
+    TAG_CLG_NAME,
+    TAG_CLG_NUMB,
+    TAG_PRIVACY,
+    TAG_FEAT_SET,
+    TAG_FEATURE,
+    TAG_PROMPT,
+    TAG_ORIG_NAME,
+    TAG_ORIG_NUMB,
+    TAG_REDIR_NAME,
+    TAG_REDIR_NUMB,
+    TAG_ALT_CLG,
+    TAG_DISP_CLG,
+    TAG_DISP_CLD,
+    TAG_CALL_TYPE,
+    TAG_MODE,
+    TAG_CAUSE,
+    TAG_CALL_SELECTED,
+    TAG_BUTTON_NUMB,
+    TAG_SPEED_DIAL,
+    TAG_LABEL,
+    TAG_GCID,
+    TAG_LOGDISP,
+    TAG_MWI_TYPE,
+    TAG_NEW_COUNT,
+    TAG_OLD_COUNT,
+    TAG_HP_NEW_COUNT,
+    TAG_HP_OLD_COUNT,
+    TAG_MEDIA_TYPE,
+    TAG_MEDIA_DIRECTION,
+    TAG_MEDIA_MODE,
+    TAG_DURATION,
+    TAG_SESSION_HANDLE,
+    TAG_MCAP_ID,
+    TAG_GROUP_ID,
+    TAG_STREAM_ID,
+    TAG_REF_COUNT,
+    TAG_SESSION_ID,
+    TAG_RECV_INFO_LIST,
+    TAG_INFO_PACKAGE,
+    TAG_CONTENT_TYPE,
+    TAG_MESSAGE_BODY,
+    TAG_POLICY,
+    TAG_CFG_VER,
+    TAG_DP_VER,
+    TAG_SK_VER,
+    TAG_METHOD,
+    TAG_SIS_VER_NAME,
+    TAG_SIS_VER_MAJOR,
+    TAG_SIS_VER_MINOR,
+    TAG_SIS_VER_ADDTNL
+} group_tag_t;
+
+
+/* Session types supported */
+/* SESSIONTYPE_* is encoded into the MSB of session_feature_t.session_id */
+// XXX TODO figure out how to decouple this from the Java side constant
+typedef enum {
+    SESSIONTYPE_CALLCONTROL = 1L,
+    SESSIONTYPE_RSTP,
+    SESSIONTYPE_RTP,
+    SESSIONTYPE_FILEPLAYER,
+    SESSIONTYPE_TONE,
+    SESSIONTYPE_CAPTURE
+} group_session_type_t;
+
+/* Session Provider Management Commands */
+typedef enum {
+    CMD_INIT = 1L,
+    CMD_INSERVICE,
+    CMD_RESTART,
+    CMD_SHUTDOWN,
+    CMD_UNLOAD,
+    CMD_PRE_INIT,
+    CMD_PRO_BASE,
+    CMD_UNREGISTER_ALL_LINES = 10L,
+    CMD_REGISTER_ALL_LINES,
+    CMD_BLF_INIT
+} group_cmd_t;
+
+/* Other provider specific cmds can be defined beginning with CMD_PRO_BASE */
+/* TBD from JNI */
+#define CC_CMD_UPDATELINES  CMD_PRO_BASE
+
+/**
+ * Defines registration state
+ */
+typedef enum {
+    CC_CREATED_IDLE,
+    CC_OOS_FAILOVER,
+    CC_OOS_REGISTERING,
+    CC_OOS_AWAIT_CFG_SYNC,
+    CC_OOS_AWAIT_RESTART,
+    CC_INSERVICE,
+    CC_OOS_IDLE
+} cc_reg_state_t;
+
+/* Other provider specific cmds can be defined beginning with STATE_PRO_BASE */
+
+
+
+/* Device specific feature update IDs */
+typedef enum {
+    DEVICE_FEATURE_CFWD = 1L,
+    DEVICE_FEATURE_MWI,
+    DEVICE_FEATURE_MWILAMP,
+    DEVICE_FEATURE_MNC_REACHED,
+    DEVICE_SERVICE_CONTROL_REQ,
+    DEVICE_NOTIFICATION,
+    DEVICE_LABEL_N_SPEED,
+    DEVICE_REG_STATE,
+    DEVICE_CCM_CONN_STATUS,
+    DEVICE_CONDITIONAL_RESTART = 14L,
+    DEVICE_SYNC_CONFIG_VERSION,
+    DEVICE_ENABLE_VIDEO,
+    DEVICE_ENABLE_CAMERA,
+    DEVICE_FEATURE_BLF,
+    DEVICE_SUPPORTS_NATIVE_VIDEO
+} group_device_feature_t;
+
+/* Ringer Reservation feature update IDs */
+typedef enum {
+    RINGER_RESERVATION_CREATED = 100L,
+    RINGER_RESERVATION_UPDATE
+} group_ringer_reservation_t;
+
+/* Info Package */
+typedef enum {
+    INFO_PKG_ID_GENERIC_RAW = 0L
+} group_info_pkg_id_t;
+
+/* Session Events */
+typedef enum {
+    SESSION_CREATED = 1L,
+    SESSION_CLOSED
+} group_session_event_t;
+
+/* Call Session Events */
+typedef enum {
+    CALL_SESSION_CREATED = SESSION_CREATED,
+    CALL_SESSION_CLOSED = SESSION_CLOSED,
+    CALL_STATE = 3L,
+    CALL_NEWCALL,
+    CALL_INFORMATION,
+    CALL_ATTR,
+    CALL_SECURITY,
+    CALL_LOGDISP,
+    CALL_PLACED_INFO,
+    CALL_STATUS,
+    CALL_DELETE_LAST_DIGIT,
+    CALL_ENABLE_BKSP,
+    CALL_SELECT_FEATURE_SET,
+    CALL_SELECTED,
+    CALL_PRESERVATION_ACTIVE,
+    CALL_GCID,
+    CALL_FEATURE_CANCEL,
+    VIDEO_AVAIL = 20L,
+    CALL_RECV_INFO_LIST,
+    VIDEO_OFFERED,
+    RINGER_STATE,
+    CALL_CALLREF,
+    MEDIA_INTERFACE_UPDATE_BEGIN,
+    MEDIA_INTERFACE_UPDATE_SUCCESSFUL,
+    MEDIA_INTERFACE_UPDATE_FAIL,
+    CREATE_OFFER,
+    CREATE_ANSWER,
+    SET_LOCAL_DESC,
+    SET_REMOTE_DESC,
+    REMOTE_STREAM_ADD
+} group_call_event_t;
+
+/* File Player Session Events */
+typedef enum {
+    FILEPLAYER_PLAYED = 300L,
+    FILEPLAYER_ALLOCATED
+} group_fileplayer_t;
+
+typedef enum {
+    TONE_STARTED = 101L,
+    TONE_STOPPED,
+    MEDIA_INFO,
+    MEDIA_UPDATE
+} group_media_event_t;
+
+//#include "com_cisco_sessionapi_MediaDirection.h"
+typedef enum {
+    RX_DIRECTION = 0L,
+    TX_DIRECTION,
+    BI_DIRECTION
+} group_media_direction_t;
+
+typedef enum {
+    PROMPTSTATUS_PROMPT = 10L,
+    PROMPTSTATUS_HIGH = 11L,
+    PROMPTSTATUS_NORMAL = 15L,
+    PROMPTSTATUS_MEDIA_MANAGER = 16L,
+    PROMPTSTATUS_NOTIFICATION = 20L,
+    PROMPTSTATUS_STATUS = 30L,
+    PROMPTSTATUS_LOW = 31L,
+    SOFTKEYBAR_APPLICATION_MANAGER = 100L,
+    SOFTKEYBAR_MEDIA_MANAGER = 50L
+} group_priority_t;
+
+/* Session Features that can be invoked TBD should come from JNI */
+#define FEATURE_NONE        0
+#define FEATURE_VOLUME_CTRL 1
+#define FEATURE_PRO_BASE    0
+
+/* Call Priority TBD should come from JNI */
+#define CC_CALL_PRIORITY_NORMAL  0
+#define CC_CALL_PRIORITY_URGENT  1
+
+/* Session Commands  TBD should come from JNI */
+typedef enum {
+    SESSION_REALIZE = 1,
+    SESSION_PREFETCH,
+    SESSION_START,
+    SESSION_STOP,
+    SESSION_DEALLOCATE,
+    SESSION_CLOSE,
+    SESSION_ALLOCATE
+} group_session_t;
+
+/*
+ * CC Provider specific constants.
+ * These do not come from JNI Files
+ */
+
+#include "phone_types.h"
+
+#define CC_ALL_LINES       255
+#define CC_SESSION_INVALID 0x01FFFFFF
+#define CC_MAX_GCID        CC_GCID_LEN
+
+/* 1-9 * # A B C D are number 1 thru 16 */
+#define BKSP_KEY   90
+
+#ifdef __CC_CAUSE_STRINGS__
+static const char *cc_cause_names[] = {
+    "OK",
+    "ERR",
+    "UNASSIGNED_NUM",
+    "NO_RESOURCE",
+    "NO_ROUTE",
+    "NORMAL",
+    "BUSY",
+    "NO_USER_RESP",
+    "NO_USER_ANS",
+    "REJECT",
+    "INVALID_NUMBER",
+    "FACILITY_REJECTED",
+    "CALL_ID_IN_USE",
+    "XFER_LOCAL",
+    "XFER_REMOTE",
+    "XFER_BY_REMOTE",
+    "XFER_CONFERENCE",
+    "CONGESTION",
+    "ANONYMOUS",
+    "REDIRECT",
+    "PAYLOAD_MISMATCH",
+    "CONF",
+    "REPLACE",
+    "NO_REPLACE_CALL",
+    "NO_RESUME",
+    "NO_MEDIA",
+    "REQUEST_PENDING",
+    "INVALID_PARTICIPANT",
+    "NO_CONF_BRIDGE",
+    "MAX_PARTICIPANT",
+    "KEY_NOT_ACTIVE",
+    "TEMP_NOT_AVAILABLE",
+    "REMOTE_SERVER_ERROR",
+    "BARGE",
+    "CBARGE",
+    "NOT_FOUND",
+    "SECURITY_FAILURE",
+    "MONITOR",
+    "UI_STATE_BUSY",
+    "SIP_CAUSE_ANSWERED_ELSEWHERE",
+    "RETRIEVED",
+    "FORWARDED",
+    "ABANDONED",
+    "XFER_LOCAL_WITH_DIALSTRING",
+    "CAC_BW_OK",
+    "ONHOOK_FEAT_COMP",
+    "RESP_TIMEOUT",
+    "SERV_ERR_UNAVAIL",
+    "REMOTE_DISCONN_REQ_PLAYTONE",
+    "MAX_CAUSE"
+};
+#endif //__CC_CAUSE_STRINGS__
+
+#define MAX_SOFT_KEYS    16
+
+
+// eventually these should come from the Java side
+typedef enum {
+    SESSION_MGMT_APPLY_CONFIG,
+    SESSION_MGMT_SET_TIME,
+    SESSION_MGMT_GET_PHRASE_TEXT,
+    SESSION_MGMT_SET_UNREG_REASON,
+    SESSION_MGMT_GET_UNREG_REASON,
+    SESSION_MGMT_UPDATE_KPMLCONFIG,
+    SESSION_MGMT_GET_AUDIO_DEVICE_STATUS,
+    SESSION_MGMT_CHECK_SPEAKER_HEADSET_MODE,
+    SESSION_MGMT_LINE_HAS_MWI_ACTIVE,
+    SESSION_MGMT_EXECUTE_URI
+} session_mgmt_func_e;
+
+#endif
diff --git a/libs/sipcc/core/includes/sessionTypes.h b/libs/sipcc/core/includes/sessionTypes.h
new file mode 100755 (executable)
index 0000000..cc9b235
--- /dev/null
@@ -0,0 +1,427 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SESSIONTYPES_H_
+#define _SESSIONTYPES_H_
+
+#include "string_lib.h"
+#include "sessionConstants.h"
+#include "ccsip_pmh.h"
+#include "cc_constants.h"
+#include "sip_ccm_transport.h"
+#include "plat_api.h"
+
+/*********************** SESSION ID *****************/
+typedef unsigned int session_id_t ;
+
+typedef struct {
+  unsigned int  reason;
+  string_t      reason_info;
+} ccSessionProvider_cmd_t;
+
+typedef struct {
+  string_t sis_ver_name ;  //could be "cme" now.
+  unsigned int  sis_ver_major;
+  unsigned int  sis_ver_minor;
+  unsigned int  sis_ver_addtnl;
+}sis_ver;
+
+typedef struct {
+  unsigned int   cause;
+  unsigned int   mode;
+  sis_ver        sis_ver_info;
+} ccProvider_state_t;
+
+typedef struct {
+  line_t  line_id;
+  string_t dial;
+} ccSession_create_param_t;
+
+typedef struct {
+  string_t                  info;
+  string_t                  info1;
+  unsigned int              state;
+  cc_jsep_action_t          action;
+  cc_media_stream_id_t      stream_id;
+  cc_media_track_id_t       track_id;
+  cc_media_type_t           media_type;
+  cc_level_t                level;
+  unsigned int              sessionid;
+  cc_boolean                has_constraints;
+} ccSession_feature_t;
+
+typedef struct {
+  int          state;
+  int          attr;
+  int          inst;
+  line_t       line_id;
+  int          cause;
+  string_t        sdp;
+  unsigned int media_stream_id;
+  unsigned int media_stream_track_id;
+} cc_call_state_data_t;
+/* CALL_SESSION_CREATED shall use the call_state as data*/
+
+typedef struct
+{
+  string_t      cldNum;
+  string_t      cldName;
+} cc_placed_call_info_t;
+
+typedef struct
+{
+  string_t clgName;
+  string_t clgNumber;
+  string_t altClgNumber;
+  boolean dispClgNumber;
+  string_t cldName;
+  string_t cldNumber;
+  boolean dispCldNumber;
+  string_t origCalledName;
+  string_t origCalledNumber;
+  string_t lastRedirectingName;
+  string_t lastRedirectingNumber;
+  unsigned short call_type;
+  unsigned short instance_id;
+  int      security;
+  int      policy;
+} cc_callinfo_t;
+
+typedef struct {
+    string_t     featSet;
+    int          featMask[MAX_SOFT_KEYS];
+} cc_featurekey_set_t;
+
+typedef struct {
+    cc_boolean      start;
+    vcm_ring_mode_t mode;
+    cc_boolean      once;
+} cc_ringer_state_t;
+
+/**
+ * Define call status to carry over timeout/priority that might be sent from CUCM.
+ * Note: if the values of timeout and priority are zero, then 2 second is the
+ *      derfault value for the timeout. It's mostly the application based on UI
+ *      design.
+ */
+typedef struct {
+    string_t    status;
+    int         timeout;
+    int         priority;
+} cc_call_status_t;
+
+typedef struct
+{
+  union {
+    cc_call_state_data_t  state_data;
+    cc_placed_call_info_t plcd_info;
+    cc_callinfo_t         call_info;
+    cc_call_status_t      status;
+    char                  gcid[CC_MAX_GCID];
+    int                   action;
+    int                   security;
+    cc_featurekey_set_t   feat_set;
+    unsigned int          target_sess_id;
+    unsigned int          callref;
+    string_t              recv_info_list;
+    cc_ringer_state_t     ringer;
+  } data;
+} ccSession_update_t;
+
+typedef struct {
+  line_t line;
+  unsigned int  info;
+} cc_line_data_t;
+
+typedef struct {
+  int           state;
+  int           info;
+} cc_feature_state_t;
+
+typedef struct {
+  cc_blf_state_t  state;
+  int             request_id;
+  int             app_id;
+} cc_feature_blf_state_t;
+
+typedef struct {
+  int           timeout;
+  boolean       notifyProgress;
+  char          priority;
+  string_t      prompt;
+} cc_notification_data_t;
+
+typedef struct {
+  line_t        line;
+  unsigned char button;
+  string_t      speed;
+  string_t      label;
+} cc_label_n_speed_t;
+
+typedef struct {
+  string_t      cfg_ver;
+  string_t      dp_ver;
+  string_t      softkey_ver;
+} cc_cfg_version_t;
+
+typedef struct {
+  line_t        line;
+  boolean       isFwd;
+  boolean       isLocal;
+  string_t      cfa_num;
+} cc_cfwd_status_t;
+
+typedef struct {
+  string_t      addr;
+  int           status;
+} cc_ccm_conn_t;
+
+typedef struct {
+  line_t        line;
+  boolean       status;
+  int  type;
+  int  newCount;
+  int  oldCount;
+  int  hpNewCount;
+  int  hpOldCount;
+} cc_mwi_status_t;
+
+typedef struct {
+  union {
+    cc_line_data_t line_info;     // For line specific features
+    cc_feature_state_t state_data; // For device specific feature
+    cc_feature_blf_state_t blf_data; // For blf state updates.
+    cc_notification_data_t notification;
+    cc_label_n_speed_t  cfg_lbl_n_spd;
+    cc_cfwd_status_t    cfwd;     // For CFWD ALL feature
+    cc_ccm_conn_t       ccm_conn;
+    cc_mwi_status_t     mwi_status;
+    unsigned int        reset_type;
+    cc_cfg_version_t    cfg_ver_data;
+  } data;
+} ccFeature_update_t;
+
+typedef struct {
+  int           data;
+} ccSessionCmd_t;
+
+/*********************** STREAM SESSION TYPES *****************/
+
+typedef struct {
+  unsigned int  reason;
+} scSessionProvider_cmd_t;
+
+typedef struct {
+  unsigned int  mode;
+} scSession_state_t;
+
+typedef struct {
+  line_t    line_id;
+} scSession_create_param_t;
+
+typedef struct {
+  string_t info;
+} scSession_feature_t;
+
+typedef struct {
+  string_t info;
+} scProvider_state_t;
+
+typedef struct {
+  int type;
+  int mcap_id;
+  int group_id;
+  int stream_id;
+  int call_id;
+  int direction;
+  int ref_count;
+  int session_handle;//session handle for ms rtp session
+} rtp_session_info;
+
+typedef struct {
+  int refcount;
+} rtp_session_update;
+
+typedef struct {
+  union {
+    int state;
+    rtp_session_info rtp_info;
+    rtp_session_update rtp_update;
+  } data;
+} scSession_update_t;
+
+typedef struct {
+  string_t info;
+} scFeature_update_t;
+
+typedef struct {
+  string_t info;
+} scSessionCmd_t;
+
+typedef struct {
+  int id;
+  int data;
+} rcFeature_update_t;
+
+/********************** SESSION TYPES ****************************/
+
+
+typedef struct {
+  unsigned int sessionType;
+  unsigned int  cmd;
+  union {
+    ccSessionProvider_cmd_t ccData;
+    scSessionProvider_cmd_t scData;
+  } cmdData;
+} sessionProvider_cmd_t;
+
+typedef struct {
+  unsigned int sessionType;
+  unsigned int  state;
+  union {
+    ccProvider_state_t ccData;
+    scProvider_state_t scData;
+  } stateData;
+} provider_state_t;
+
+typedef struct {
+  unsigned int sessionType;
+  string_t uri;
+  union {
+    ccSession_create_param_t ccData;
+    scSession_create_param_t scData;
+  } createData;
+} session_create_param_t;
+
+typedef struct {
+  unsigned int session_id;
+  unsigned int featureID;
+  union {
+    ccSession_feature_t ccData;
+    scSession_feature_t scData;
+  } featData;
+} session_feature_t;
+
+typedef struct {
+  unsigned int sessionID;
+  unsigned int eventID;
+  unsigned int sessType;
+  union {
+    ccSession_update_t ccSessionUpd;
+    scSession_update_t scSessionUpd;
+  } update;
+}session_update_t;
+
+typedef struct {
+  unsigned int sessID;
+  unsigned int cmd;
+  union {
+    ccSessionCmd_t ccCmd;
+    scSessionCmd_t scCmd;
+  } cmdData;
+}sessionCmd_t;
+
+
+typedef struct {
+  unsigned int sessionType;
+  unsigned int featureID;
+  union {
+    ccFeature_update_t ccFeatUpd;
+    scFeature_update_t scFeatUpd;
+    rcFeature_update_t rcFeatUpd;
+  } update;
+}feature_update_t;
+
+
+typedef struct {
+  string_t config_version_stamp;
+  string_t dialplan_version_stamp;
+  string_t fcp_version_stamp;
+  string_t cucm_result;
+  string_t load_id;
+  string_t inactive_load_id;
+  string_t load_server;
+  string_t log_server;
+  boolean ppid;
+} session_mgmt_config_t;
+
+typedef struct {
+    int result;
+} session_mgmt_apply_config_result_t;
+
+typedef struct {
+  long gmt_time;
+} session_mgmt_time_t;
+
+typedef struct {
+  int ret_val;
+  int ndx;
+  char *outstr;
+  uint32_t len;
+} session_mgmt_phrase_text_t;
+
+typedef struct {
+  int unreg_reason;
+} session_mgmt_unreg_reason_t;
+
+typedef struct {
+  int kpml_val;
+} session_mgmt_kpmlconfig_t;
+
+typedef struct {
+  int enabled;
+  plat_audio_device_t device_type;
+} session_mgmt_audio_device_status_t;
+
+typedef struct {
+  boolean enabled;
+} session_mgmt_speaker_headset_mode_t;
+
+typedef struct {
+  boolean ret_val;
+  line_t line;
+} session_mgmt_line_mwi_active_t;
+
+typedef struct {
+  string_t uri;
+} session_mgmt_uri_t;
+
+typedef struct {
+  session_mgmt_func_e func_id;
+  union {
+    session_mgmt_config_t config;
+    session_mgmt_apply_config_result_t apply_config_result;
+    session_mgmt_time_t time;
+    session_mgmt_phrase_text_t phrase_text;
+    session_mgmt_unreg_reason_t unreg_reason;
+    session_mgmt_kpmlconfig_t kpmlconfig;
+    session_mgmt_audio_device_status_t audio_device_status;
+    session_mgmt_speaker_headset_mode_t speaker_headset_mode;
+    session_mgmt_line_mwi_active_t line_mwi_active;
+    session_mgmt_uri_t uri;
+  } data;
+} session_mgmt_t;
+
+
+typedef struct {
+  string_t info_package;
+  string_t content_type;
+  string_t message_body;
+} info_generic_raw_t;
+
+typedef struct {
+  unsigned int sessionID;
+  info_generic_raw_t generic_raw;
+} session_send_info_t;
+
+typedef struct {
+  unsigned int sessionID;
+  int packageID;
+  union {
+    info_generic_raw_t generic_raw;
+  } info;
+} session_rcvd_info_t;
+
+#endif
+
diff --git a/libs/sipcc/core/includes/sessuri.h b/libs/sipcc/core/includes/sessuri.h
new file mode 100644 (file)
index 0000000..8c5ffbf
--- /dev/null
@@ -0,0 +1,171 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SESSURI_H_
+#define  _SESSURI_H_
+
+#define MAX_LEN_SCHEME_INFO 256
+#define MAX_STR_LEN_PARAM_TYPE 64
+#define MAX_STR_LEN_PARAM_VAL  64
+
+/*
+ * Scheme names
+ */
+#define SCHEME_SIP "sip"
+#define SCHEME_FILE "file"
+#define SCHEME_RTP  "rtp"
+#define SCHEME_RTSP "rtsp"
+#define SCHEME_CAPTURE "capture"
+
+/**
+ *
+ * Different URI types.
+ *
+ */
+typedef enum {
+    SCHEME_NONE=0,
+    SIP_URI,
+    FILE_URI,
+    CAPTURE_URI,
+    RTP_URI,
+    RTSP_URI
+} scheme_e;
+
+/**
+ * parameter tags/names
+ *
+ */
+
+#define LINE_TAG "line"
+
+/**
+ * Definitions for file player session
+ */
+#define CADENCE_TAG       "cadence"
+#define MEDIA_TYPE_TAG    "media_type"
+#define LOOP_COUNT_TAG    "loop_count"
+#define PRIORITY_TAG      "priority"
+#define FILEPTYPE_TAG     "type"
+
+/**
+ * Definitions for raw rtp session
+ */
+#define DIRECTION_TAG     "direction"
+#define MULTICAST_TAG     "mcast"
+#define PAYLOADTYPE_TAG   "payloadtype"
+#define FRAMESIZE_TAG     "framesize"
+#define VADENABLE_TAG     "vad"
+#define PRECEDENCE_TAG    "precedence"
+#define MIXINGMODE_TAG    "mode"
+#define MIXINGPARTY_TAG   "party"
+#define CHANNELTYPE_TAG   "channeltype"
+#define LOCALADDRESS_TAG  "localaddress"
+#define LOCALPORT_TAG     "localport"
+#define ALGORITHM_TAG     "algorithm"
+
+/**
+ * Param types required for various URIs.
+ *
+ */
+typedef enum {
+    LINE_PARAM=0,
+    MEDIA_TYPE_PARAM,
+    CADENCE_PARAM,
+    LOOP_COUNT_PARAM,
+    PRIORITY_PARAM,
+    MAX_QUERY_PARAM,
+    DIRECTION_PARAM,
+    MULTICAST_PARAM,
+    PAYLOADTYPE_PARAM,
+    FRAMESIZE_PARAM,
+    VADENABLE_PARAM,
+    PRECEDENCE_PARAM,
+    MIXINGMODE_PARAM,
+    MIXINGPARTY_PARAM,
+    CHANNELTYPE_PARAM,
+    LOCALADDRESS_PARAM,
+    LOCALPORT_PARAM,
+    ALGORITHM_PARAM,
+    FILETYPE_PARAM
+} param_e;
+
+typedef enum {
+    MEDIA_TYPE_AUDIO,
+    MEDIA_TYPE_VIDEO,
+    MEDIA_TYPE_AUDIO_VIDEO,
+} media_type_e;
+
+/**
+ * params related to call sessions.
+ *
+ */
+typedef struct {
+    int           line_id;
+    media_type_e  media_type;
+} call_session_param_t;
+
+/**
+ * params related to capture sessions.
+ *
+ */
+typedef struct {
+    media_type_e  media_type;
+} capture_session_param_t;
+
+/**
+ *
+ * params related file sessions
+ */
+typedef struct {
+    int type;
+    int loop_count;
+    int cadence;
+    int priority;
+} file_session_param_t;
+
+typedef struct {
+    int direction;
+    int multicast;
+    int payloadtype;
+    int framesize;
+    int vadenable;
+    int precedence;
+    int mixingmode;
+    int mixingparty;
+    int channeltype;
+    int localaddress;
+    int localport;
+    int algorithm;
+} raw_rtp_session_param_t;
+
+/*
+ * generic param type
+ */
+typedef union params {
+    call_session_param_t call_session_param;
+    file_session_param_t file_session_param;
+    raw_rtp_session_param_t raw_session_param;
+    capture_session_param_t capture_session_param;
+} param_t;
+
+
+/*
+ * URI information. This is output of the Parser.
+ *
+ */
+typedef struct uri_s {
+    scheme_e     scheme;
+    char         scheme_specific[MAX_LEN_SCHEME_INFO];
+    union
+    {
+        call_session_param_t call_session_param;
+        file_session_param_t file_session_param;
+        raw_rtp_session_param_t raw_session_param;
+    }param;
+
+} uri_t;
+
+int parse_uri(const char *uri, uri_t *uri_info);
+
+#endif
diff --git a/libs/sipcc/core/includes/singly_link_list.h b/libs/sipcc/core/includes/singly_link_list.h
new file mode 100644 (file)
index 0000000..a0046ab
--- /dev/null
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SINGLY_LINK_LIST_H
+#define _SINGLY_LINK_LIST_H
+
+typedef enum {
+    SLL_MATCH_FOUND,
+    SLL_MATCH_NOT_FOUND
+} sll_match_e;
+
+/*
+ * type definition for find function pointer. The find function takes two arguments:
+ * 1. find_by_p - pointer to the key
+ * 2. data_p - pointer to the linked list node data.
+ */
+typedef sll_match_e(*sll_find_callback_t)(void *find_by_p, void *data_p);
+
+typedef void *sll_handle_t;
+
+typedef enum {
+    SLL_RET_SUCCESS,
+    SLL_RET_INVALID_ARGS,
+    SLL_RET_MALLOC_FAILURE,
+    SLL_RET_NODE_NOT_FOUND,
+    SLL_RET_LIST_NOT_EMPTY,
+    SLL_RET_OTHER_FAILURE
+} sll_return_e;
+
+/*
+ * sll_create(): creates a signly linked list control block and initializes it.
+ *               Applications shall call this first before performing any singly
+ *               linked list primitives, such as append, remove, find or destroy.
+ *
+ * Parameters: find_fp - function pointer which will be used to find the matching node.
+ *
+ * Returns: list handle or NULL if it can not create the list.
+ */
+extern sll_handle_t sll_create(sll_find_callback_t find_fp);
+
+/*
+ * sll_destroy(): if the list is empty, it frees the list.
+ *                It is the responsibility of the applications to empty
+ *                the list before destroying the list.
+ *
+ * Parameters: list_handle - handle to the list.
+ *
+ * Returns: SLL_RET_SUCCESS if it successfully destroys.
+ *          SLL_RET_INVALID_ARGS if the arguments are invalid.
+ *          SLL_RET_LIST_NOT_EMPTY if the list is not empty.
+ */
+extern sll_return_e sll_destroy(sll_handle_t list_handle);
+
+/*
+ * sll_append(): creates a list node and appends it to the list.
+ *               Applications are responsible for memory management of the
+ *               data that the node will point to.
+ *
+ * Parameters: list_handle - handle to the list.
+ *             data_p - pointer to the data that the list node will point to.
+ *
+ * Returns: SLL_RET_SUCCESS if it successfully appends.
+ *          SLL_RET_INVALID_ARGS if the arguments are invalid.
+ *          SLL_RET_MALLOC_FAILURE if memory allocation fails.
+ */
+extern sll_return_e sll_append(sll_handle_t list_handle, void *data_p);
+
+/*
+ * sll_remove(): removes the node from the list and frees the node.
+ *               Applications are responsible for memory management of the
+ *               data that the node points to.
+ *
+ * Parameters: list_handle - handle to the list.
+ *             data_p - pointer to the data that the list node points to.
+ *
+ * Returns: SLL_RET_SUCCESS if it successfully removes.
+ *          SLL_RET_INVALID_ARGS if the arguments are invalid.
+ *          SLL_RET_NODE_NOT_FOUND if the node is not found in the list.
+ */
+extern sll_return_e sll_remove(sll_handle_t list_handle, void *data_p);
+
+/*
+ * sll_find(): finds the matching node data using find_fp function.
+ *
+ * Parameters: list_handle - handle to the list.
+ *             find_by_p - pointer to the opaque data that will be used by find_fp function.
+ *
+ * Returns: pointer to the data or NULL if it can not find.
+ */
+extern void *sll_find(sll_handle_t list_handle, void *find_by_p);
+
+/*
+ * sll_next(): returns pointer to the data in the next node to the node holding data_p.
+ *             if data_p is NULL, then returns pointer to the data in the first node.
+ *             Applications can use this primitive to walk through the list. Typically,
+ *             it can be used to remove the individual nodes and to destroy the list
+ *             before shutting down/resetting the application.
+ *
+ * Parameters: list_handle - handle to the list.
+ *             data_p - pointer to the data that the list node points to.
+ *
+ * Returns: pointer to the data or NULL if it can not find.
+ */
+extern void *sll_next(sll_handle_t list_handle, void *data_p);
+
+/*
+ * sll_count(): returns the number of elements in the list.
+ *              count of the linked list.
+ *
+ * Parameters: list_handle - handle to the list.
+ *
+ * Returns: returns the number of elements in the list.
+ */
+extern unsigned int sll_count(sll_handle_t list_handle);
+
+#endif
diff --git a/libs/sipcc/core/includes/sip_socket_api.h b/libs/sipcc/core/includes/sip_socket_api.h
new file mode 100755 (executable)
index 0000000..2969d7d
--- /dev/null
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __SIP_SOCKET_API_H__
+#define __SIP_SOCKET_API_H__
+
+#include "cpr.h"
+#include "cpr_socket.h"
+
+/**
+ * sipSocketSend
+ *
+ * @brief The sipSocketSend() function is a wrapper used by the sipstack to send
+ * data over a socket. This function decides to use the secure versus unsecure
+ * connection based on the "secure" flag.
+ *
+ * @note - The implementation of both secure/non-secure is the same in RT/TNP
+ * products. It is different for the other vendors and hence we need this
+ * flexibility.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags  - The options used for the send.
+ *
+ *
+ */
+ssize_t
+sipSocketSend (cpr_socket_t soc,
+         CONST void *buf,
+         size_t len,
+         int32_t flags,
+         boolean secure);
+
+/**
+ * sipSocketRecv
+ *
+ * @brief The sipSocketRecv() function is a wrapper used by the sipstack to send
+ * data over a socket. This function decides to use the secure versus unsecure
+ * connection based on the "secure" flag.
+ *
+ * @note - The implementation of both secure/non-secure is the same in RT/TNP
+ * products. It is different for the other vendors and hence we need this
+ * flexibility.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags  - The options used for the recv.
+ */
+ssize_t
+sipSocketRecv (cpr_socket_t soc,
+         void * RESTRICT buf,
+         size_t len,
+         int32_t flags,
+         boolean secure);
+
+/**
+ * sipSocketClose
+ *
+ * @brief The sipSocketClose() function is a wrapper used by the sipstack to
+ * close a socket. This function decides to use the secure versus unsecure
+ * connection based on the "secure" flag.
+ *
+ * @note - The implementation of both secure/non-secure is the same in RT/TNP
+ * products. It is different for the other vendors and hence we need this
+ * flexibility.
+ *
+ * @param[in] soc  - The socket that needs to be destroyed
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+sipSocketClose (cpr_socket_t soc,
+                boolean secure);
+#endif
diff --git a/libs/sipcc/core/includes/sntp.h b/libs/sipcc/core/includes/sntp.h
new file mode 100644 (file)
index 0000000..93f53ea
--- /dev/null
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SNTP_H
+#define SNTP_H
+
+#include <cpr_types.h>
+#include <cpr_stdio.h>
+#include <cpr_string.h>
+#include <cpr_socket.h>
+
+#include <time2.h>
+// Begin system specific includes
+//#include <irx.h>
+#include <dns_utils.h>
+#include <phone_debug.h>
+#include <logger.h>
+#include <logmsg.h>
+#include <phone.h>
+#include <text_strings.h>
+// End system specific includes
+
+#define MAX_IPADDR_STR_LEN 48
+
+#define NTP_UTC_OFFSET     (2208963600)
+#define PORT_NTP           (123)
+#define MULTICAST_ADDR_NTP (0xE0000101L)
+#define BROADCAST_ADDR     (0xFFFFFFFF)
+#define TIMEPORT           (37)
+
+typedef enum { unicast, multicast, anycast, directed_broadcast } SNTP_Mode;
+
+typedef struct {
+    unsigned int precision:8;
+    unsigned int poll:8;
+    unsigned int stratum:8;
+    unsigned int mode:3;
+    unsigned int versionNumber:3;
+    unsigned int leapIndicator:2;
+} NTPHeader_s;
+
+typedef struct {
+    union {
+        NTPHeader_s   header;
+        unsigned long rawheader;
+    } u;
+    unsigned long rootDelay;
+    unsigned long rootDispersion;
+    unsigned long referenceIdentifier;
+    unsigned long referenceTimestamp[2];
+    unsigned long originateTimestamp[2];
+    unsigned long receiveTimestamp[2];
+    unsigned long transmitTimestamp[2];
+    // Removed the following fields since they are version 4
+    // specific.
+    //unsigned long   keyIdentifier;
+    //unsigned long   messageDigest[4];
+} NTPStruct_s;
+
+typedef struct {
+    Socket        *server_socket;             // Socket to SNTP server
+    Socket        *lsocket;                   // Local listening socket
+    Socket        *bsocket;                   // Broadcast listening socket
+    Socket        *msocket;                   // Multicast listening socket
+    char          address[MAX_IPADDR_STR_LEN];
+    unsigned long sntp_server_addr;
+    SNTP_Mode     mode;
+
+    unsigned long destinationTimestamp[2];
+    long          roundtripDelay[2];
+    long          timeOffset[2];
+    long          time_zone;
+} SNTP_State;
+
+typedef union {
+    unsigned long NTPDataBuffer[17];
+    NTPStruct_s   NTPData;
+} NTPPacket_u;
+
+void SNTPInit(void);
+void SNTPStop(void);
+int SNTPSend(void);
+void SNTPCallback(irx_tmr_buf *pTmrBlk);
+int SNTPRecv(SysHdr *pSm);
+long NTPSemanticCheck(const NTPStruct_s *ntpdata);
+void SNTPSecondUpdate(void);
+void printNTPStruct(const NTPStruct_s *ntpdata, const SNTP_State *state);
+long sntp_get_rand_seed(void);
+void SNTPDebugInit(void);
+
+#endif
diff --git a/libs/sipcc/core/includes/string_lib.h b/libs/sipcc/core/includes/string_lib.h
new file mode 100755 (executable)
index 0000000..1efb231
--- /dev/null
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef  _STRING_LIB_INCLUDED_H /* allows multiple inclusion */
+#define  _STRING_LIB_INCLUDED_H
+
+#include "cpr_types.h"
+
+#define LEN_UNKNOWN -1
+
+typedef struct string_block_t_
+{
+    struct string_block_t_ *next;
+    uint16_t    refcount;
+    uint16_t    length;
+    const char *fname;
+    int         line;
+    short       signature;
+    char        data[1];
+} string_block_t;
+
+
+/*
+ * Prototypes for functions
+ */
+
+string_t strlib_malloc(const char *str, int length, const char *fname,
+                       int line);
+string_t strlib_copy(string_t str);
+string_t strlib_update(string_t destination, const char *source,
+                       const char *fname, int line);
+string_t strlib_append(string_t str, const char *toappend_str,
+                       const char *fname, int line);
+string_t strlib_prepend(string_t str, const char *toprepend_str,
+                        const char *fname, int line);
+void strlib_free(string_t str);
+char *strlib_open(string_t str, int length, const char *fname, int line);
+string_t strlib_close(char *str);
+string_t strlib_printf(const char *format, ...);
+string_t strlib_empty(void);
+void strlib_debug_init(void);
+long strlib_mem_used(void);
+int strlib_test_memory_is_string(void *mem);
+void strlib_init (void);
+
+#ifndef __STRINGLIB_INTERNAL__
+#define strlib_malloc(x,y) strlib_malloc(x,y,__FILE__,__LINE__)
+#define strlib_update(x,y) strlib_update(x,y,__FILE__,__LINE__)
+#define strlib_append(x,y) strlib_append(x,y,__FILE__,__LINE__)
+#define strlib_prepend(x,y) strlib_prepend(x,y,__FILE__,__LINE__)
+#define strlib_open(x,y) strlib_open(x,y,__FILE__,__LINE__)
+#endif
+
+
+#endif
diff --git a/libs/sipcc/core/includes/subapi.h b/libs/sipcc/core/includes/subapi.h
new file mode 100755 (executable)
index 0000000..3678427
--- /dev/null
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SUBAPI_H_
+#define _SUBAPI_H_
+
+#include "ccsip_subsmanager.h"
+
+cc_rcs_t sub_int_subnot_register(cc_srcs_t src_id, cc_srcs_t dst_id,
+                                 cc_subscriptions_t evt_pkg, void *callback_fun,
+                                 cc_srcs_t dest_task, int msg_id,
+                                 void *term_callback, int term_msg_id,
+                                 long min_duration, long max_duration);
+
+cc_rcs_t sub_int_subscribe(sipspi_msg_t *msg_p);
+
+cc_rcs_t sub_int_subscribe_ack(cc_srcs_t src_id, cc_srcs_t dst_id,
+                               sub_id_t sub_id, uint16_t response_code,
+                               int duration);
+
+cc_rcs_t sub_int_notify(cc_srcs_t src_id, cc_srcs_t dst_id, sub_id_t sub_id,
+                        ccsipNotifyResultCallbackFn_t notifyResultCallback,
+                        int subsNotResCallbackMsgID,
+                        ccsip_event_data_t *eventData,
+                        subscriptionState subState);
+
+cc_rcs_t sub_int_notify_ack(sub_id_t sub_id, uint16_t response_code,
+                            uint32_t cseq);
+
+cc_rcs_t sub_int_subscribe_term(sub_id_t sub_id, boolean immediate,
+                                int request_id,
+                                cc_subscriptions_t event_package);
+
+cc_rcs_t sip_send_message(ccsip_sub_not_data_t *msg_data,
+                          cc_srcs_t dest_task, int msg_id);
+
+cc_rcs_t app_send_message(void *msg_data, int msg_len, cc_srcs_t dest_id,
+                          int msg_id);
+extern cc_rcs_t
+sub_send_msg (cprBuffer_t buf, uint32_t cmd, uint16_t len, cc_srcs_t dst_id);
+
+#endif
diff --git a/libs/sipcc/core/includes/task.h b/libs/sipcc/core/includes/task.h
new file mode 100755 (executable)
index 0000000..6cbc937
--- /dev/null
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TASK_H
+#define TASK_H
+
+/*
+ *--------------------DEPRECATED FILE-------------------------------
+ *
+ * As part of Skittles project this file is deprecated.
+ * DO NOT add anything to this file.
+ * The contents of this file are really platform specific and
+ * have moved to phntask.h under
+ *    src-bcm-tnp/h/phntask.h for CNU phones.
+ *    src-arm-79xx/phntask.h  for IRX phones.
+ *
+ * If you are doing SYNC merges _DO_ _NOT_ merge anything from parent
+ * This file is kept here because it comes from parent/grand parent branches
+ * and will not be removed from clearcase till Skittles collapses.
+ * [The fAQ on cc tools contains details of why ]
+ *
+ * If you have questions send email to skittles-dev
+ *---------------------------------------------------------------
+ */
+#define SIP_TMR                 0xffff  //TODO CPR a temporary bogus number
+
+#endif /* TASK_H */
diff --git a/libs/sipcc/core/includes/time2.h b/libs/sipcc/core/includes/time2.h
new file mode 100644 (file)
index 0000000..cc02ce7
--- /dev/null
@@ -0,0 +1,192 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TIME2_H
+#define TIME2_H
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+// System specific includes
+
+// End system specific
+
+#define EPOCH_YEAR                  (1900)
+#define EPOCH_SECONDS               (0x80000000)
+#define DAYS_PER_LEAP_YEAR          (366)
+#define DAYS_PER_YEAR               (365)
+#define DAYS_PER_WEEK               (7)
+#define LEAP_FOUR_CENTURY           (400)
+#define LEAP_CENTURY                (100)
+#define LEAP_YEAR                   (4)
+// Calculated with 365.2425 days/year for the Gregorian calendar
+#define SECONDS_PER_GREGORIAN_YEAR  (31556952)
+#define SECONDS_PER_YEAR            (31536000)
+#define SECONDS_PER_LEAP_YEAR       (31622400)
+#define SECONDS_PER_DAY             (86400)
+#define SECONDS_PER_HOUR            (3600)
+#define SECONDS_PER_MINUTE          (60)
+#define MINUTES_PER_HOUR            (60)
+#define HOURS_PER_DAY               (24)
+#define MAX_WEEKS_PER_MONTH         (6)
+#define MONTHS_PER_YEAR             (12)
+
+#define STARTING_TIME (101 * SECONDS_PER_GREGORIAN_YEAR)
+
+//If 30 minutes have elapsed, clear time display
+#define SNTP_MAX_TIME_SINCE_UPDATE 30*60
+
+typedef enum {
+    SUNDAY = 0, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
+} DAYS_NAMES_TO_NUMBER;
+
+typedef enum {
+    JANUARY = 0,
+    FEBRUARY,
+    MARCH,
+    APRIL,
+    MAY,
+    JUNE,
+    JULY,
+    AUGUST,
+    SEPTEMBER,
+    OCTOBER,
+    NOVEMBER,
+    DECEMBER
+} MONTH_NAMES_TO_NUMBER;
+
+typedef struct {
+    time_t         time;
+    unsigned short millitm;
+    short          timezone;
+    short          dstflag;
+} timeb;
+
+typedef struct {
+    // Standard Gregorian calendar date
+    unsigned long year;         // Range >= EPOCH_YEAR
+    unsigned long month;        // Range 0-11, use MONTH_NAME_x for text
+    unsigned long day;          // Range 1-31
+
+    // Standard 24 hour time format
+    unsigned long hour;         // Range 0-23
+    unsigned long minute;       // Range 0-59
+    unsigned long seconds;      // Range 0-59
+
+    // Specifics about a year and the current date
+    unsigned long day_of_year;  // Range 0-365
+    unsigned long day_of_january_first; // Range 0-6, use DAY_NAME_x for text
+    unsigned long day_of_week;  // Range 0-6, use DAY_NAME_x for text
+    unsigned long week_of_year; // Range 1-53
+
+    // Variables that are associated with leap years and calculations
+    unsigned long leap_years;   // Non negative integer
+    unsigned long leap_flag;    // Range 0-1, boolean
+
+    // Time zone specifics set by user
+    long time_zone;                                   // Timezone offset in seconds
+    unsigned long daylight_saving_offset;             // Number of seconds to add (adjust) when DST starts
+                                                      //      e.g.  daylight_saving_offset = 3600; // 1 hour
+                                                      //      when DST starts the code will add 1 hour to the GMT time and
+                                                      //      when DST stops it will stop adding 1 hour
+    unsigned long daylight_saving_start_month;        // Range 0-11, use MONTH_NAME_x for text
+    unsigned long daylight_saving_start_day;          // Range 1-31, if set to 0, only then will week of month is used
+    unsigned long daylight_saving_start_day_of_week;  // Range 0-6, use DAY_NAME_x for text
+    unsigned long daylight_saving_start_week_of_month;// Range 0-6, First, second, etc. DAY_NAME of the month, zero denotes last week in month, e.g. day_of_week = 0, week_of_month = 1 => First Sunday of month
+    unsigned long daylight_saving_start_time;         // Range 0-86399, number of seconds past midnight
+    unsigned long daylight_saving_stop_month;         // Range 0-11, use MONTH_NAME_x for text
+    unsigned long daylight_saving_stop_day;           // Range 1-31, if set to 0, only then will week of month is used
+    unsigned long daylight_saving_stop_day_of_week;   // Range 0-6, use DAY_NAME_x for text
+    unsigned long daylight_saving_stop_week_of_month; // Range 0-6, First, second, etc. DAY_NAME of the month, zero denotes last week in month, e.g. day_of_week = 0, week_of_month = 1 => First Sunday of month
+    unsigned long daylight_saving_stop_time;          // Range 0-86399, number of seconds past midnight
+    unsigned long daylight_saving_year_calc;          // Non negative integer, non 0 means daylight saving start and stop second is calculated for given year
+    unsigned long daylight_saving_auto_adjust;        // Range 0-1, boolean
+
+    // Time zone specifics set by make_date
+    unsigned long is_daylight_saving;           // Range 0-1, boolean
+    unsigned long daylight_saving_start_second; // Second in the year that daylight saving starts
+    unsigned long daylight_saving_stop_second;  // Second in the year that daylight saving stops
+} time2_s;
+
+typedef struct {
+    const char *TZ_NAME;
+    const long offset;
+} TZ_STRUCT;
+
+// Time date externs
+extern const char *const   MONTH_NAMES_LONG[];
+extern const char *const   MONTH_NAMES_SHORT[];
+extern const unsigned long DAYS_IN_MONTH[];
+extern const char *const   DAY_NAMES_LONG[];
+extern const char *const   DAY_NAMES_SHORT[];
+extern const char *const   DAY_NAMES_ABBREV[];
+extern const TZ_STRUCT     TZ_TABLE[];
+
+long make_date(unsigned long secondsPastEpoch, time2_s *timeStruct);
+const char *get_month_name_long(unsigned long month);
+const char *get_month_name_short(unsigned long month);
+const char *get_day_name_long(unsigned long day);
+const char *get_day_name_short(unsigned long day);
+const char *get_day_name_abrrev(unsigned long day);
+long get_leap_years(unsigned long year);
+unsigned long get_seconds_past_epoch(unsigned long nth, unsigned long day_name, unsigned long month, unsigned long year);
+long is_leap_year(unsigned long year);
+long range_check_time_struct(time2_s *ts);
+unsigned long date_to_seconds(const time2_s *ts);
+unsigned long time_to_seconds(const time2_s *ts);
+unsigned long date_time_to_seconds(const time2_s *ts);
+unsigned long get_local_time(void);
+void get_precise_local_time(unsigned long *local_time);
+long get_local_timezone(void);
+long get_local_dst_active(void);
+unsigned long time_to_short_string(const time2_s *ts, char *time_string);
+unsigned long gmt_string_to_seconds(char *gmt_string, unsigned long *seconds);
+unsigned long seconds_to_gmt_string(unsigned long seconds, char *gmt_string);
+unsigned long diff_time(unsigned long t1, unsigned long t2);
+
+// This function is similar to the strcmp function in the string.h library
+// It includes support for handling the NTP most significant bit rollover
+// If t1 < t2 cmp_time returns -1
+// If t1 == t2 cmp_time returns 0
+// If t1 > t2 cmp_time returns 1
+long cmp_time(unsigned long t1, unsigned long t2);
+
+long diff_current_time(unsigned long t1, unsigned long *difference);
+long parse_month(char *pString);
+long parse_day_of_week(char *pString);
+long parse_timezone(char *pString);
+long parse_time(char *pString);
+
+/*
+ * Formats ulMilliseconds into elasped time in the form of
+ * HRS:MIN:SEC for less than 24 hours and then
+ *  <n> DAY(S) HRS:MIN:SEC for greater than 24 hours
+*/
+void ascTimeDuration(unsigned long ulMilliseconds, char *ptimStr, int length);
+void ascFormatDate(time2_s *ts, char * pDateStr, int length);
+void ascFormatHrMinTime(time2_s *ts, char * pTimeStr, int length, char bFlashColon);
+void FormatDateTime(char * ptimeStr, int length);
+void FormatDebugTime(char *timeStr, int length,long ts);
+char *GetDateTimeString(void);
+
+// Called once a second to update the time. Returns true indicating
+// a roll-over indicating the display needs updated.
+// Once a minute we need to update the time/date display and
+// update the minute/hour/day etc.
+//
+int TimeOfDayUpdate(void);
+
+// Set the time of day clock
+void SetTimeOfDay(time2_s *time);
+
+// Blank "turn off" the display of time
+// called when there is a change to
+// the time keeping systems on the phone
+void ClearTimeDisplay(void);
+
+#endif
diff --git a/libs/sipcc/core/includes/timer.h b/libs/sipcc/core/includes/timer.h
new file mode 100755 (executable)
index 0000000..eb02e9e
--- /dev/null
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MGCP_TIMER_H
+#define MGCP_TIMER_H
+
+#define TIMER_FREE 0x1          /* Indicates timer is free */
+#define TIMER_INITIALIZED 0x2   /* Indicates timer is initialized */
+#define TIMER_ACTIVE 0x4        /* Indicates timer is in list */
+
+/* Timer event structure */
+typedef struct timer_struct
+{
+    unsigned int expiration_time; /* Expiration time */
+    int interval;                 /* Timer period */
+    void *parameter1;             /* Timer expiration callback param */
+    void *parameter2;             /* Second timer expiration callback param */
+    void (*expiration_callback)
+      (void *timer, void *parameter1, void *parameter2);  /* Expiry handler */
+    int flags;                    /* Debugging flags */
+    struct timer_struct *pred;    /* List predecessor */
+    struct timer_struct *next;    /* List successor */
+} timer_struct_type;
+
+extern unsigned long current_time(void);
+extern void timer_event_activate(timer_struct_type *timer);
+extern void *timer_event_allocate(void);
+extern void timer_event_cancel(timer_struct_type *timer);
+extern void timer_event_free(timer_struct_type *timer);
+extern void timer_event_initialize(timer_struct_type *timer,
+                                   int period,
+                                   void (*expiration)(void *timer_event,
+                                                      void *param1,
+                                                      void *param2),
+                                   void *param1, void *param2);
+extern void timer_event_process(void);
+extern void timer_event_system_init(void);
+extern boolean timer_expired(void);
+extern void platform_timer_tick(void);
+extern void platform_timer_init(void);
+
+#ifdef _WIN32
+extern void platform_timer_stop(void);
+#endif
+extern int timer_ms_to_ticks(int milliseconds);
+extern boolean is_timer_active(timer_struct_type *timer);
+
+#endif
diff --git a/libs/sipcc/core/includes/tnpphone.h b/libs/sipcc/core/includes/tnpphone.h
new file mode 100644 (file)
index 0000000..80d4d62
--- /dev/null
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TNPPHONE_H
+#define TNPPHONE_H
+
+#include "phone.h"
+
+
+#ifdef SIP_OS_WINDOWS
+
+
+#ifndef MAX_FILE_NAME
+#define MAX_FILE_NAME 256
+#endif
+
+/* Defined in net_config.h */
+typedef struct
+{
+    uint8_t  fName[MAX_FILE_NAME];   // Name of file being transfered
+    uint32_t rcvLen;
+    uint32_t maxSize;           // The maximum size of the file to be read
+    uint8_t *fDest;             // The destination address for transfer
+    uint32_t fHost;             // Foreign IP address
+    uint16_t rtCode;
+    uint8_t  taskId;
+    irx_lst_blk *pTaskList;
+} TftpOpen;                     // TFTP Request
+
+#define CFG_PROGRAMMED   0x12300000L
+#define CFG_PROGRAM_TFTP 0x12200000L
+#define CFG_DHCP_DISABLE 0x00000001L
+#define CFG_DHCP_TIMEOUT 0x00000002L
+#define CFG_TFTP_TIMEOUT 0x00000004L
+#define CFG_TFTP_NTFOUND 0x00000008L
+#define CFG_TFTP_ACCESS  0x00000010L
+#define CFG_TFTP_ERR     0x00000020L
+#define CFG_DNS_ERROR    0x00000040L
+#define CFG_DNS_TIMEOUT  0x00000080L
+#define CFG_DNS_IP       0x00000100L
+#define CFG_VER_ERR      0x00000200L
+#define CFG_CKSUM_ERR    0x00000400L
+//#define CFG_TFTP_FILE    0x00000800L
+#define CFG_DFLT_GWY     0x00001000L
+#define CFG_DUP_IP       0x00002000L
+#define CFG_PATCHED      0x00004000L
+#define CFG_LINK_DOWN    0x00008000L
+#define CFG_FIXED_TFTP   0x00010000L
+#define CFG_PROG_ERR     0x00020000L
+#define CFG_BOOTP        0x00040000L
+#define CFG_DHCP_RELEASE 0x00080000L
+
+
+#ifndef ERROR
+#define ERROR   (-1)
+#endif
+
+#ifndef PHONE_TICK_TO
+#define PHONE_TICK_TO 10
+#endif
+
+
+#endif
+#endif
diff --git a/libs/sipcc/core/includes/uart.h b/libs/sipcc/core/includes/uart.h
new file mode 100755 (executable)
index 0000000..30596cf
--- /dev/null
@@ -0,0 +1,119 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _UART_H_
+#define _UART_H_
+
+#include "cpr_types.h"
+
+
+/*
+ * Telecaster specific defines
+ */
+#define SYSCLOCK                ((unsigned long)25000000)  /* 25 MHZ */
+#define UART_BASE_ADDRESS       (UART_16550_REGS *)0xFFFE4000
+
+/*
+ * 16550 register definition
+ */
+typedef struct {
+    volatile uint8_t dc;        /* RBR/THR */
+    volatile uint8_t ier;       /* Interrupt Enable Reg */
+    volatile uint8_t iir;       /* Interrupt ID Reg */
+    volatile uint8_t lcr;       /* Line Control Reg / FIFO Control Reg */
+    volatile uint8_t mcr;       /* Modem Control Register */
+    volatile uint8_t lsr;       /* Line Status Register */
+    volatile uint8_t msr;       /* Modem Status Register */
+    volatile uint8_t scr;       /* Scratch Register */
+} UART_16550_REGS;
+
+/*
+ * UART register bit symbols
+ */
+#define UART_IER_MASK      0xF0
+#define IER_RX_IE          0x01
+#define IER_TX_IE          0x02
+#define IER_LS_IE          0x04
+#define IER_MS_IE          0x08
+
+#define UART_IIR_RV        0x01
+#define IIR_IPEND          0x01
+#define IIR_MODEM_STAT     0x00
+#define IIR_TX_EMPTY       0x02
+#define IIR_RX_AVAIL       0x04
+#define IIR_RX_LINE_STAT   0x06
+#define IIR_CHAR_TIMOUT    0x0C
+
+#define UART_FCR_MASK      0x30
+#define FCR_FIFO_EN        0x01
+#define FCR_CLR_RXF        0x02
+#define FCR_CLR_TXF        0x04
+#define FCR_TRIGR_1BYT     0x00
+#define FCR_TRIGR_4BYT     0x40
+#define FCR_TRIGR_8BYT     0x80
+#define FCR_TRIGR_14BYT    0xC0
+
+#define LCR_CHR_LEN_5BIT   0x00
+#define LCR_CHR_LEN_6BIT   0x01
+#define LCR_CHR_LEN_7BIT   0x02
+#define LCR_CHR_LEN_8BIT   0x03
+#define LCR_STOP1          0x00
+#define LCR_STOP2          0x04
+#define LCR_PAR_EN         0x08
+#define LCR_PAR_ODD        0x10
+#define LCR_FIX_PAR        0x20
+#define LCR_BRK_CTL        0x40
+#define LCR_DLAB           0x80
+
+#define MCR_DTR            0x01
+#define MCR_RTS            0x02
+#define MCR_OUT1           0x04
+#define MCR_OUT2           0x08
+#define MCR_LPBK           0x10
+#define MCR_AUTO_FLW_EN    0x20
+
+#define UART_LSR_RV        0x60
+#define LSR_RX_AVAIL       0x01
+#define LSR_OVRUN          0x02
+#define LSR_PAR_ERR        0x04
+#define LSR_FRAM_ERR       0x08
+#define LSR_BRK_INT        0x10
+#define LSR_TX_EMPTY       0x20
+#define LSR_XMITR_EMPTY    0x40
+#define LSR_RX_FIFO_ERR    0x80
+
+#define UART_MSR_RV        0x00
+#define MSR_CTS_CHG        0x01
+#define MSR_DSR_CHG        0x02
+#define MSR_TERI           0x04
+#define MSR_CD_CHG         0x08
+#define MSR_CTS_IN         0x10
+#define MSR_DSR_IN         0x20
+#define MSR_RI_IN          0x40
+#define MSR_CD_IN          0x80
+
+
+#define DATAREADY     0x01
+#define XMITFIFOEMPTY 0x20
+
+
+#define RXLINESTATUS  (3<<1)
+#define RXDATAAVAIL   (2<<1)
+#define CHARTIMEOUT   (6<<1)
+#define TXEMPTY       (1<<1)
+#define MODEMSTATUS   (0<<1)
+#define RXERRORS      0x8E
+#define DATAREADY     0x01
+#define XMITFIFOEMPTY 0x20
+
+
+#define TX_EMPTY 0x01
+
+//extern int uart_init(int speed, char *buf, int buf_size);
+extern int32_t uart_init();
+extern void uart_flush(int32_t dev);
+extern int32_t uart_outc(int8_t c, void *);
+extern int32_t uart_outs(int8_t *s, void *);
+
+#endif
diff --git a/libs/sipcc/core/includes/uiapi.h b/libs/sipcc/core/includes/uiapi.h
new file mode 100644 (file)
index 0000000..6604264
--- /dev/null
@@ -0,0 +1,164 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _UIAPI_H_
+#define _UIAPI_H_
+
+#include "cpr_types.h"
+#include "phone_types.h"
+#include "string_lib.h"
+#include "vcm.h"
+#include "ccapi.h"
+
+#include "sessionConstants.h"
+typedef enum {
+    evMinEvent = 0,
+    evOffHook = OFFHOOK,
+    evOnHook = ONHOOK,
+    evRingOut = RINGOUT,
+    evRingIn = RINGIN,
+    evProceed = PROCEED,
+    evConnected = CONNECTED,
+    evHold = HOLD,
+    evRemHold = REMHOLD,
+    evResume = RESUME,
+    evBusy = BUSY,
+    evReorder = REORDER,
+    evConference = CONFERENCE,
+    evRemInUse = REMINUSE,
+    evCallPreservation = PRESERVATION,
+    evHoldRevert = HOLDREVERT,
+    evWhisper = WHISPER,
+    evWaitingForDigits = WAITINGFORDIGITS,
+    evCreateOffer = CREATEOFFER,
+    evCreateAnswer = CREATEANSWER,
+    evCreateOfferError = CREATEOFFERERROR,
+    evCreateAnswerError = CREATEANSWERERROR,
+    evSetLocalDesc = SETLOCALDESC,
+    evSetRemoteDesc = SETREMOTEDESC,
+    evSetLocalDescError = SETLOCALDESCERROR,
+    evSetRemoteDescError = SETREMOTEDESCERROR,
+    evOnRemoteStreamAdd = REMOTESTREAMADD,
+    evMaxEvent
+} call_events;
+
+#define MWI_STATUS_YES 1
+
+/* call operations : dialing, state change and info related */
+void ui_new_call(call_events event, line_t nLine, callid_t nCallID,
+                 int call_attr, uint16_t call_instance_id, boolean dialed_digits);
+void ui_set_call_attr(line_t line_id, callid_t call_id, call_attr_t attr);
+void ui_call_info(string_t clgName, string_t clgNumber, string_t altClgNumber, boolean dispClgNumber,
+                  string_t cldName, string_t cldNumber, boolean dispCldNumber,
+                  string_t pOrigCalledNameStr, string_t pOrigCalledNumberStr,
+                  string_t pLastRedirectingNameStr,
+                  string_t pLastRedirectingNumberStr,
+                  calltype_t call_type,
+                  line_t line, callid_t call_id, uint16_t call_instance_id,
+                  cc_security_e call_security, cc_policy_e call_policy);
+void ui_cc_capability(line_t line_id, callid_t call_id,
+                      string_t recv_info_list);
+void ui_info_received(line_t line_id, callid_t call_id,
+                      const char *info_package, const char *content_type,
+                      const char *message_body);
+void ui_update_placed_call_info(line_t line, callid_t call_id, string_t cldName,
+                                string_t cldNumber);
+void ui_log_disposition( callid_t nCallID, int logDisp);
+void ui_call_state(call_events event, line_t nLine, callid_t nCallID, cc_causes_t cause);
+void ui_set_local_hold(line_t line, callid_t call_id);
+void ui_delete_last_digit(line_t line_id, callid_t call_id);
+void ui_update_label_n_speeddial(line_t line, line_t button_no, string_t
+                                 speed_dial, string_t label);
+void ui_mnc_reached(line_t line, boolean mnc_reached);
+
+void ui_call_selected(line_t line_id, callid_t call_id, int selected);
+void ui_call_in_preservation(line_t line_id, callid_t call_id);
+void ui_update_call_security(line_t line, callid_t call_id,
+                             cc_security_e call_security);
+void ui_update_conf_invoked(line_t line, callid_t call_id,
+                             boolean invoked);
+void ui_terminate_feature(line_t line, callid_t call_id,
+                        callid_t target_call_id);
+
+void ui_update_gcid(line_t line, callid_t call_id, char *gcid);
+void ui_update_callref(line_t line, callid_t call_id, unsigned int callref);
+
+/* status line related */
+void ui_set_call_status(string_t pString, line_t line, callid_t callID);
+char *ui_get_idle_prompt_string(void);
+void ui_set_idle_prompt_string(string_t pString, int prompt);
+void ui_set_notification(line_t line, callid_t callID,
+                         char *promptString, int timeout,
+                         boolean notifyProgress, char priority);
+void ui_clear_notification();
+
+/* softkey manipulation */
+void ui_control_feature(line_t line_id, callid_t call_id, int list[], int len, int enable);
+void ui_select_feature_key_set(line_t line_id, callid_t call_id, char *set_name,
+                               int sk_list[], int len);
+void ui_control_featurekey_bksp(line_t line_id, callid_t call_id,
+                                boolean enable);
+
+/* speaker */
+void ui_set_speaker_mode(boolean mode);
+
+/* mwi */
+void ui_set_mwi(line_t line, boolean status, int type, int newCount, int oldCount, int hpNewCount, int hpOldCount);
+void ui_change_mwi_lamp(int status);
+boolean ui_line_has_mwi_active(line_t line);
+
+/* call forward */
+void ui_cfwd_status(line_t line, boolean cfa, char *cfa_number,
+                    boolean lcl_fwd);
+
+/* registration, stack init related */
+void ui_sip_config_done(void);
+void ui_set_sip_registration_state(line_t line, boolean registered);
+void ui_reg_all_failed(void);
+
+void ui_keypad_button(char *digitstr, int direction);
+
+void ui_offhook(line_t line, callid_t call_id);
+void ui_dial_call(line_t line, callid_t call_id, char *to,
+                  char *global_call_id);
+int ui_dial_digits(line_t line, char *digitstr);
+int ui_dial_dtmf(line_t line, char *digitstr);
+void ui_answer_call(line_t line, callid_t call_id);
+void ui_disconnect_call(line_t line, callid_t call_id);
+int ui_xfer_setup(line_t line, callid_t call_id, char *to,
+                  char *global_call_id, boolean media);
+void ui_xfer_complete(line_t line, callid_t call_id, callid_t consult_call_id);
+int ui_conf_setup(line_t line, callid_t call_id, char *to,
+                  char *global_call_id);
+void ui_conf_complete(line_t line, callid_t call_id, callid_t consult_call_id);
+int ui_hold_call(line_t line, callid_t call_id);
+void ui_hold_retrieve_call(line_t line, callid_t call_id);
+void ui_cfwdall_req(unsigned int line);
+
+/* Test Interface */
+void ui_execute_uri(char *);
+
+/* Status Message Interface */
+void ui_log_status_msg(char *msg);
+
+void ui_init_ccm_conn_status(void);
+void ui_update_video_avail (line_t line, callid_t call_id, int avail);
+void ui_update_video_offered (line_t line, callid_t call_id, int dir);
+void ui_call_stop_ringer(line_t line, callid_t call_id);
+void ui_call_start_ringer(vcm_ring_mode_t ringMode, short once, line_t line, callid_t call_id);
+void ui_BLF_notification (int request_id, cc_blf_state_t blf_state, int app_id);
+void ui_update_media_interface_change(line_t line, callid_t call_id, group_call_event_t event);
+void ui_create_offer(call_events event, line_t nLine, callid_t nCallID,
+                        uint16_t call_instance_id, char* sdp);
+void ui_create_answer(call_events event, line_t nLine, callid_t nCallID,
+                        uint16_t call_instance_id, char* sdp);
+void ui_set_local_description(call_events event, line_t nLine, callid_t nCallID,
+                        uint16_t call_instance_id, char* sdp, cc_int32_t status);
+void ui_set_remote_description(call_events event, line_t nLine, callid_t nCallID,
+                        uint16_t call_instance_id, char* sdp, cc_int32_t status);
+void ui_on_remote_stream_added(call_events event, line_t nLine, callid_t nCallID,
+                     uint16_t call_instance_id, cc_media_remote_track_table_t media_tracks);
+
+
+#endif
diff --git a/libs/sipcc/core/includes/upgrade.h b/libs/sipcc/core/includes/upgrade.h
new file mode 100644 (file)
index 0000000..96f2bd9
--- /dev/null
@@ -0,0 +1,37 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _UPGRADE_INCLUDED_H
+#define _UPGRADE_INCLUDED_H
+
+#include "cpr_types.h"
+#include "phone.h"
+
+/*
+ * Telecaster has these extra 32 bytes of data
+ * in front of the load header.  I have no idea what
+ * they are for, but having a structure makes it easier
+ * to deal with this as opposed to adding 0x20 to the
+ * address.
+ */
+typedef struct
+{
+    uint8_t vectors[0x20];
+    LoadHdr hdr;
+} BigLoadHdr;
+
+/*
+ * Prototypes for public functions
+ */
+void upgrade_bootup_init(void);
+int upgrade_done(int tftp_rc);
+void upgrade_start(const char *fname);
+void upgrade_memcpy(void *dst, const void *src, int len);
+int upgrade_check(const char *loadid);
+int upgrade_validate_app_image(const BigLoadHdr *load);
+int upgrade_validate_dsp_image(const DSPLoadHdr *dsp);
+void upgrade_erase_dir_storage(void);
+void upgrade_write_dir_storage(char *buffer, char *flash, int size);
+
+#endif /* _UPGRADE_INCLUDED_H */
diff --git a/libs/sipcc/core/includes/util_ios_queue.h b/libs/sipcc/core/includes/util_ios_queue.h
new file mode 100644 (file)
index 0000000..98ad646
--- /dev/null
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _UTIL_IOS_QUEUE_H
+#define _UTIL_IOS_QUEUE_H
+
+/*
+ * Define the queue data type and a basic enqueue structure
+ */
+
+typedef struct queuetype_ {
+    void *qhead;                /* head of queue */
+    void *qtail;                /* tail of queue */
+    int count;                  /* possible count */
+    int maximum;                /* maximum entries */
+} queuetype;
+
+typedef queuetype *queue_ptr_t;
+
+typedef struct nexthelper_
+{
+    struct nexthelper_ *next;
+    unsigned char data[4];
+} queue_node, nexthelper, *node_ptr_t;
+
+typedef enum get_node_status_ {
+    GET_NODE_FAIL_EMPTY_LIST,
+    GET_NODE_FAIL_END_OF_LIST,
+    GET_NODE_SUCCESS
+} get_node_status;
+
+void queue_init(queuetype *q, int maximum);
+void *peekqueuehead(queuetype* q);
+
+/*Functions used by SIP */
+int queryqueuedepth(queuetype const *q);
+boolean checkqueue(queuetype *q, void *e);
+void *p_dequeue(queuetype *qptr);
+void p_enqueue(queuetype *qptr, void *eaddr);
+void p_requeue(queuetype *qptr, void *eaddr);
+void p_swapqueue(queuetype *qptr, void *enew, void *eold);
+void p_unqueue(queuetype *q, void *e);
+void p_unqueuenext(queuetype *q, void **prev);
+void enqueue(queuetype *qptr, void *eaddr);
+void *dequeue(queuetype *qptr);
+void unqueue(queuetype *q, void *e);
+void requeue(queuetype *qptr, void *eaddr);
+void *remqueue(queuetype *qptr, void *eaddr, void *paddr);
+void insqueue(queuetype *qptr, void *eaddr, void *paddr);
+boolean queueBLOCK(queuetype *qptr);
+boolean validqueue(queuetype *qptr, boolean print_message);
+
+boolean queue_create_init(queue_ptr_t *queue);
+void add_list(queue_ptr_t queue, node_ptr_t node);
+get_node_status get_node(queue_ptr_t queue, node_ptr_t *node);
+void queue_delete(queue_ptr_t queue);
+
+#endif
diff --git a/libs/sipcc/core/includes/util_parse.h b/libs/sipcc/core/includes/util_parse.h
new file mode 100644 (file)
index 0000000..e334112
--- /dev/null
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _UTIL_PARSE_H_
+#define _UTIL_PARSE_H_
+
+#include "xml_defs.h"
+
+XMLToken parse_xml_tokens(char **parseptr, char *value, int maxlen);
+
+#endif
diff --git a/libs/sipcc/core/includes/util_string.h b/libs/sipcc/core/includes/util_string.h
new file mode 100644 (file)
index 0000000..04a53d9
--- /dev/null
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _UTIL_STRING_H_
+#define _UTIL_STRING_H_
+
+#include "cpr_types.h"
+#include "cpr_socket.h"
+
+#define EMPTY_STR ""
+#define EMPTY_STR_LEN 1
+
+boolean is_empty_str(char *str);
+void init_empty_str(char *str);
+
+void ipaddr2dotted(char *addr_str, cpr_ip_addr_t *addr);
+uint32_t dotted2ipaddr(const char *addr_str);
+int str2ip(const char *str, cpr_ip_addr_t *addr);
+void util_ntohl(cpr_ip_addr_t *ip_addr_out, cpr_ip_addr_t *ip_addr_in);
+boolean util_compare_ip(cpr_ip_addr_t *ip_add1, cpr_ip_addr_t *ip_addr2);
+void util_extract_ip(cpr_ip_addr_t *ip_addr, cpr_sockaddr_storage *from);
+uint16_t util_get_port(cpr_sockaddr_storage *sock_storage);
+void util_get_ip_using_mode(cpr_ip_addr_t *ip_addr, cpr_ip_mode_e ip_mode,
+                             uint32_t  ip4, char *ip6);
+boolean util_check_if_ip_valid(cpr_ip_addr_t *ip_addr);
+
+#endif
diff --git a/libs/sipcc/core/includes/www.h b/libs/sipcc/core/includes/www.h
new file mode 100644 (file)
index 0000000..e5c40b0
--- /dev/null
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _WWW_H_
+#define _WWW_H_
+
+#define MAXCHARSPERLINE 400
+
+typedef struct {
+    int soc;
+    unsigned char buf[MAXCHARSPERLINE];
+    unsigned char text[MAXCHARSPERLINE];
+    int idx, n, clen, send;
+} WWWRBuf;
+
+extern int Connect2WWW(char *hostName);
+extern int Connect2WWWIPPort(unsigned long nIPAddr, unsigned short nPort,
+                             char *hostName);
+extern void wwwSend(WWWRBuf *wrb, char *fmt, ...);
+extern boolean www_proxy_used(int connection);
+extern boolean www_socket_open(int connection);
+extern void HTTP_Init_ConnTbl();
+
+#endif
diff --git a/libs/sipcc/core/includes/xml_defs.h b/libs/sipcc/core/includes/xml_defs.h
new file mode 100644 (file)
index 0000000..b91558f
--- /dev/null
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _XML_DEFS_H_
+#define _XML_DEFS_H_
+
+#include "cpr_types.h"
+
+#define TOK_MAX_LEN   (24)
+
+#define TOK_NONE      (0)
+#define TOK_PROC      (1)
+#define TOK_ELEMENT   (2)
+#define TOK_ATTRIBUTE (3)
+#define TOK_CONTENT   (4)
+
+#define XML_START       (1199)
+#define XML_END         (1200)
+#define XML_CONTENT     (1201)
+#define XML_ALL_CONTENT (1202)
+
+typedef struct XMLTokenStruc {
+    const int8_t strTok[TOK_MAX_LEN];
+    int16_t TokType;
+    void *pData; // This may be another XMLTable,
+                 // or an array of valid attribute values.
+} XMLTokenStruc;
+
+typedef void (*XmlFunc)(int16_t, void *);
+
+typedef struct XMLTableStruc {
+    void (*xml_func)( int16_t TokID, void *pData );
+    uint8_t nNumTok;
+    const XMLTokenStruc *pTok;
+} XMLTableStruc;
+
+typedef enum {
+    TOK_ERR,
+    TOK_EOF,          /* reached end of the input */
+    TOK_LBRACKET,     /* "<" */
+    TOK_ENDLBRACKET,  /* "</" */
+    TOK_EMPTYBRACKET, /* "/>" */
+    TOK_RBRACKET,     /* ">" */
+    TOK_EQ,           /* "=" */
+    TOK_STR,          /* string on the rhs of = */
+    TOK_KEYWORD,      /* string on lhs of = */
+    TOK_CONTENT_STR
+} XMLToken;
+
+#endif
diff --git a/libs/sipcc/core/sdp/ccsdp.c b/libs/sipcc/core/sdp/ccsdp.c
new file mode 100644 (file)
index 0000000..585a954
--- /dev/null
@@ -0,0 +1,326 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp.h"
+#include "ccapi.h"
+
+
+const char* ccsdpAttrGetFmtpParamSets(void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return NULL;
+  }
+  return sdp_attr_get_fmtp_param_sets(sdpp->dest_sdp, level, cap_num, inst_num);
+}
+
+sdp_result_e ccsdpAttrGetFmtpPackMode(void *sdp_ptr, u16 level,
+                         u8 cap_num, u16 inst_num, u16 *val)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_get_fmtp_pack_mode(sdpp->dest_sdp, level, cap_num, inst_num, val);
+}
+
+sdp_result_e ccsdpAttrGetFmtpLevelAsymmetryAllowed(void *sdp_ptr, u16 level,
+                         u8 cap_num, u16 inst_num, u16 *val)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_get_fmtp_level_asymmetry_allowed(sdpp->dest_sdp, level, cap_num, inst_num, val);
+}
+
+const char* ccsdpAttrGetFmtpProfileLevelId (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return NULL;
+  }
+  return sdp_attr_get_fmtp_profile_id(sdpp->dest_sdp, level, cap_num, inst_num);
+}
+
+
+
+sdp_result_e ccsdpAttrGetFmtpMaxMbps (void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num, u32 *val)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_get_fmtp_max_mbps(sdpp->dest_sdp, level, cap_num, inst_num, val);
+}
+
+sdp_result_e ccsdpAttrGetFmtpMaxFs (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, u32 *val)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_get_fmtp_max_fs(sdpp->dest_sdp, level, cap_num, inst_num, val);
+}
+
+sdp_result_e ccsdpAttrGetFmtpMaxCpb (void *sdp_ptr, u16 level,
+                                 u8 cap_num, u16 inst_num, u32 *val)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_get_fmtp_max_cpb(sdpp->dest_sdp, level, cap_num, inst_num, val);
+}
+
+sdp_result_e ccsdpAttrGetFmtpMaxBr (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, u32* val)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_get_fmtp_max_br(sdpp->dest_sdp, level, cap_num, inst_num, val);
+}
+
+int ccsdpGetBandwidthValue (void *sdp_ptr, u16 level, u16 inst_num)
+
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_get_bw_value(sdpp->dest_sdp, level, inst_num);
+}
+
+sdp_result_e ccsdpAttrGetFmtpMaxDpb (void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num, u32 *val)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->dest_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_get_fmtp_max_dpb(sdpp->dest_sdp, level, cap_num, inst_num, val);
+}
+
+sdp_result_e ccsdpAddNewAttr (void *sdp_ptr, u16 level, u8 cap_num,
+                               sdp_attr_e attr_type, u16 *inst_num)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_add_new_attr(sdpp->src_sdp, level, cap_num, attr_type, inst_num);
+}
+
+sdp_result_e ccsdpAttrSetFmtpPayloadType (void *sdp_ptr, u16 level,
+                              u8 cap_num, u16 inst_num, u16 payload_num)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_payload_type(sdpp->src_sdp, level, cap_num, inst_num, payload_num);
+}
+
+
+sdp_result_e ccsdpAttrSetFmtpPackMode (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num, u16 pack_mode)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_pack_mode(sdpp->src_sdp, level, cap_num, inst_num, pack_mode);
+}
+
+sdp_result_e ccsdpAttrSetFmtpLevelAsymmetryAllowed (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num, u16 asym_allowed)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_level_asymmetry_allowed(sdpp->src_sdp, level, cap_num, inst_num, asym_allowed);
+}
+
+
+sdp_result_e ccsdpAttrSetFmtpProfileLevelId (void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num, const char *profile_level_id)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_profile_level_id(sdpp->src_sdp, level, cap_num, inst_num, profile_level_id);
+}
+
+
+sdp_result_e ccsdpAttrSetFmtpParameterSets (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num, const char *parameter_sets)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_parameter_sets(sdpp->src_sdp, level, cap_num, inst_num, parameter_sets);
+}
+
+
+
+sdp_result_e ccsdpAttrSetFmtpMaxBr (void *sdp_ptr, u16 level,
+                              u8 cap_num, u16 inst_num, u32 max_br)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_max_br(sdpp->src_sdp, level, cap_num, inst_num, max_br);
+}
+
+
+sdp_result_e ccsdpAttrSetFmtpMaxMbps (void *sdp_ptr, u16 level,
+                              u8 cap_num, u16 inst_num, u32 max_mbps)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_max_mbps(sdpp->src_sdp, level, cap_num, inst_num, max_mbps);
+}
+
+sdp_result_e ccsdpAttrSetFmtpMaxFs (void *sdp_ptr, u16 level,
+                        u8 cap_num, u16 inst_num, u32 max_fs)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_max_fs(sdpp->src_sdp, level, cap_num, inst_num, max_fs);
+}
+
+sdp_result_e ccsdpAttrSetFmtpMaxCpb (void *sdp_ptr, u16 level,
+                            u8 cap_num, u16 inst_num, u32 max_cpb)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_max_cpb(sdpp->src_sdp, level, cap_num, inst_num, max_cpb);
+}
+
+sdp_result_e ccsdpAttrSetFmtpMaxDbp (void *sdp_ptr, u16 level,
+                                  u8 cap_num, u16 inst_num, u32 max_dpb)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_max_dpb(sdpp->src_sdp, level, cap_num, inst_num, max_dpb);
+}
+
+
+sdp_result_e ccsdpAttrSetFmtpQcif  (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, u16 qcif)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_qcif(sdpp->src_sdp, level, cap_num, inst_num, qcif);
+}
+
+sdp_result_e ccsdpAttrSetFmtpSqcif  (void *sdp_ptr, u16 level,
+                            u8 cap_num, u16 inst_num, u16 sqcif)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_attr_set_fmtp_sqcif(sdpp->src_sdp, level, cap_num, inst_num, sqcif);
+}
+
+sdp_result_e ccsdpAddNewBandwidthLine (void *sdp_ptr, u16 level, sdp_bw_modifier_e bw_modifier, u16 *inst_num)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_add_new_bw_line(sdpp->src_sdp, level, bw_modifier, inst_num);
+}
+
+
+sdp_result_e ccsdpSetBandwidth (void *sdp_ptr, u16 level, u16 inst_num,
+                         sdp_bw_modifier_e bw_modifier, u32 bw_val)
+{
+  cc_sdp_t *sdpp = sdp_ptr;
+
+  if ( sdpp->src_sdp == NULL ) {
+    return SDP_INVALID_PARAMETER;
+  }
+  return sdp_set_bw(sdpp->src_sdp, level, inst_num, bw_modifier, bw_val);
+}
+
+const char * ccsdpCodecName(rtp_ptype ptype)
+{
+  switch (ptype)
+ {
+    case RTP_NONE:       return "NONE";
+    case RTP_PCMU:       return "PCMU";
+    case RTP_CELP:       return "CELP";
+    case RTP_G726:       return "G726";
+    case RTP_GSM:        return "GSM";
+    case RTP_G723:       return "G723";
+    case RTP_DVI4:       return "DVI4";
+    case RTP_DVI4_II:    return "DVI4_II";
+    case RTP_LPC:        return "LPC";
+    case RTP_PCMA:       return "PCMA";
+    case RTP_G722:       return "G722";
+    case RTP_G728:       return "G728";
+    case RTP_G729:       return "G729";
+    case RTP_JPEG:       return "JPEG";
+    case RTP_NV:         return "NV";
+    case RTP_H261:       return "H261";
+    case RTP_H264_P0:    return "H264_P0";
+    case RTP_H264_P1:    return "H264_P1";
+    case RTP_AVT:        return "AVT";
+    case RTP_L16:        return "L16";
+    case RTP_H263:       return "H263";
+    case RTP_ILBC:       return "iLBC";
+    case RTP_OPUS:       return "OPUS";
+    case RTP_VP8:        return "VP8";
+    case RTP_I420:       return "I420";
+    /* case RTP_ISAC:       return "ISAC"; */
+  }
+  return "UNKNOWN";
+}
+
diff --git a/libs/sipcc/core/sdp/sdp.h b/libs/sipcc/core/sdp/sdp.h
new file mode 100644 (file)
index 0000000..a7ad11d
--- /dev/null
@@ -0,0 +1,2010 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDP_H_
+#define _SDP_H_
+
+#include "cc_constants.h"
+#include "sdp_os_defs.h"
+#include "ccsdp.h"
+
+/* SDP Defines */
+
+/* The following defines are used to indicate params that are specified
+ * as the choose parameter or parameters that are invalid.  These can
+ * be used where the value required is really a u16, but is represented
+ * by an int32.
+ */
+#define SDP_CHOOSE_PARAM           (-1)
+#define SDP_SESSION_LEVEL        0xFFFF
+
+#define UNKNOWN_CRYPTO_SUITE              "UNKNOWN_CRYPTO_SUITE"
+#define AES_CM_128_HMAC_SHA1_32           "AES_CM_128_HMAC_SHA1_32"
+#define AES_CM_128_HMAC_SHA1_80           "AES_CM_128_HMAC_SHA1_80"
+#define F8_128_HMAC_SHA1_80               "F8_128_HMAC_SHA1_80"
+
+/*
+ * SDP_SRTP_MAX_KEY_SIZE_BYTES
+ *  Maximum size for a SRTP Master Key in bytes.
+ */
+#define SDP_SRTP_MAX_KEY_SIZE_BYTES  16
+/*
+ * SDP_SRTP_MAX_SALT_SIZE_BYTES
+ *  Maximum size for a SRTP Master Salt in bytes.
+ */
+#define SDP_SRTP_MAX_SALT_SIZE_BYTES 14
+/*
+ * SDP_SRTP_MAX_MKI_SIZE_BYTES
+ *  Maximum size for a SRTP Master Key Index in bytes.
+ */
+#define SDP_SRTP_MAX_MKI_SIZE_BYTES   4
+
+/* Max number of characters for Lifetime */
+#define SDP_SRTP_MAX_LIFETIME_BYTES 16
+
+#define SDP_SDESCRIPTIONS_KEY_SIZE_UNKNOWN      0
+#define SDP_SRTP_CRYPTO_SELECTION_FLAGS_UNKNOWN 0
+
+/*
+ * SRTP_CONTEXT_SET_*
+ *  Set a SRTP Context field flag
+ */
+#define SDP_SRTP_ENCRYPT_MASK           0x00000001
+#define SDP_SRTP_AUTHENTICATE_MASK      0x00000002
+#define SDP_SRTCP_ENCRYPT_MASK          0x00000004
+#define SDP_SRTCP_SSRC_MASK             0x20000000
+#define SDP_SRTCP_ROC_MASK              0x10000000
+#define SDP_SRTCP_KDR_MASK              0x08000000
+#define SDP_SRTCP_KEY_MASK              0x80000000
+#define SDP_SRTCP_SALT_MASK             0x40000000
+
+#define SDP_SRTP_CONTEXT_SET_SSRC(cw)          ((cw) |= SDP_SRTCP_SSRC_MASK)
+#define SDP_SRTP_CONTEXT_SET_ROC(cw)           ((cw) |= SDP_SRTCP_ROC_MASK)
+#define SDP_SRTP_CONTEXT_SET_KDR(cw)           ((cw) |= SDP_SRTCP_KDR_MASK)
+#define SDP_SRTP_CONTEXT_SET_MASTER_KEY(cw)    ((cw) |= SDP_SRTCP_KEY_MASK)
+#define SDP_SRTP_CONTEXT_SET_MASTER_SALT(cw)   ((cw) |= SDP_SRTCP_SALT_MASK)
+#define SDP_SRTP_CONTEXT_SET_ENCRYPT_AUTHENTICATE(cw) \
+     ((cw) |= (SDP_SRTP_ENCRYPT_MASK | SDP_SRTP_AUTHENTICATE_MASK | \
+               SDP_SRTCP_ENCRYPT_MASK))
+
+#define SDP_SRTP_CONTEXT_RESET_SSRC(cw)     ((cw) &= ~(SDP_SRTCP_SSRC_MASK))
+#define SDP_SRTP_CONTEXT_RESET_ROC(cw)      ((cw) &= ~(SDP_SRTCP_ROC_MASK))
+#define SDP_SRTP_CONTEXT_RESET_KDR(cw)      ((cw) &= ~(SDP_SRTCP_KDR_MASK))
+#define SDP_CONTEXT_RESET_MASTER_KEY(cw)    ((cw) &= ~(SDP_SRTCP_KEY_MASK))
+#define SDP_CONTEXT_RESET_MASTER_SALT(cw)   ((cw) &= ~(SDP_SRTCP_SALT_MASK))
+
+/* SDP Enum Types */
+
+typedef enum {
+    SDP_DEBUG_TRACE,
+    SDP_DEBUG_WARNINGS,
+    SDP_DEBUG_ERRORS,
+    SDP_MAX_DEBUG_TYPES
+} sdp_debug_e;
+
+typedef enum {
+    SDP_CHOOSE_CONN_ADDR,
+    SDP_CHOOSE_PORTNUM,
+    SDP_MAX_CHOOSE_PARAMS
+} sdp_choose_param_e;
+
+
+/* Token Lines - these must be in the same order they should
+ *               appear in an SDP.
+ */
+typedef enum {
+    SDP_TOKEN_V = 0,
+    SDP_TOKEN_O,
+    SDP_TOKEN_S,
+    SDP_TOKEN_I,
+    SDP_TOKEN_U,
+    SDP_TOKEN_E,
+    SDP_TOKEN_P,
+    SDP_TOKEN_C,
+    SDP_TOKEN_B,
+    SDP_TOKEN_T,
+    SDP_TOKEN_R,
+    SDP_TOKEN_Z,
+    SDP_TOKEN_K,
+    SDP_TOKEN_A,
+    SDP_TOKEN_M,
+    SDP_MAX_TOKENS
+} sdp_token_e;
+
+/* Media Types */
+typedef enum {
+    SDP_MEDIA_AUDIO = 0,
+    SDP_MEDIA_VIDEO,
+    SDP_MEDIA_APPLICATION,
+    SDP_MEDIA_DATA,
+    SDP_MEDIA_CONTROL,
+    SDP_MEDIA_NAS_RADIUS,
+    SDP_MEDIA_NAS_TACACS,
+    SDP_MEDIA_NAS_DIAMETER,
+    SDP_MEDIA_NAS_L2TP,
+    SDP_MEDIA_NAS_LOGIN,
+    SDP_MEDIA_NAS_NONE,
+    SDP_MEDIA_TEXT,
+    SDP_MEDIA_IMAGE,
+    SDP_MAX_MEDIA_TYPES,
+    SDP_MEDIA_UNSUPPORTED,
+    SDP_MEDIA_INVALID
+} sdp_media_e;
+
+
+/* Connection Network Type */
+typedef enum {
+    SDP_NT_INTERNET = 0,              /*  0 -> IP - In SDP "IN" is defined */
+                                     /*       to mean "Internet"          */
+    SDP_NT_ATM,                              /*  1 -> ATM                         */
+    SDP_NT_FR,                        /*  2 -> FRAME RELAY                 */
+    SDP_NT_LOCAL,                     /*  3 -> local                       */
+    SDP_MAX_NETWORK_TYPES,
+    SDP_NT_UNSUPPORTED,
+    SDP_NT_INVALID
+} sdp_nettype_e;
+
+
+/* Address Type  */
+typedef enum {
+    SDP_AT_IP4 = 0,                   /* 0 -> IP Version 4 (IP4)           */
+    SDP_AT_IP6,                       /* 1 -> IP Version 6 (IP6)           */
+    SDP_AT_NSAP,                      /* 2 -> 20 byte NSAP address         */
+    SDP_AT_EPN,                       /* 3 -> 32 bytes of endpoint name    */
+    SDP_AT_E164,                      /* 4 -> 15 digit decimal number addr */
+    SDP_AT_GWID,                      /* 5 -> Private gw id. ASCII string  */
+    SDP_MAX_ADDR_TYPES,
+    SDP_AT_UNSUPPORTED,
+    SDP_AT_FQDN,
+    SDP_AT_INVALID
+} sdp_addrtype_e;
+
+
+/* Transport Types */
+
+#define SDP_MAX_PROFILES 3
+
+typedef enum {
+    SDP_TRANSPORT_RTPAVP = 0,
+    SDP_TRANSPORT_UDP,
+    SDP_TRANSPORT_UDPTL,
+    SDP_TRANSPORT_CES10,
+    SDP_TRANSPORT_LOCAL,
+    SDP_TRANSPORT_AAL2_ITU,
+    SDP_TRANSPORT_AAL2_ATMF,
+    SDP_TRANSPORT_AAL2_CUSTOM,
+    SDP_TRANSPORT_AAL1AVP,
+    SDP_TRANSPORT_UDPSPRT,
+    SDP_TRANSPORT_RTPSAVP,
+    SDP_TRANSPORT_TCP,
+    SDP_TRANSPORT_RTPSAVPF,
+    SDP_TRANSPORT_SCTPDTLS,
+    SDP_MAX_TRANSPORT_TYPES,
+    SDP_TRANSPORT_UNSUPPORTED,
+    SDP_TRANSPORT_INVALID
+} sdp_transport_e;
+
+
+/* Encryption KeyType */
+typedef enum {
+    SDP_ENCRYPT_CLEAR,                /* 0 -> Key given in the clear       */
+    SDP_ENCRYPT_BASE64,               /* 1 -> Base64 encoded key           */
+    SDP_ENCRYPT_URI,                  /* 2 -> Ptr to URI                   */
+    SDP_ENCRYPT_PROMPT,               /* 3 -> No key included, prompt user */
+    SDP_MAX_ENCRYPT_TYPES,
+    SDP_ENCRYPT_UNSUPPORTED,
+    SDP_ENCRYPT_INVALID
+} sdp_encrypt_type_e;
+
+
+/* Known string payload types */
+typedef enum {
+    SDP_PAYLOAD_T38,
+    SDP_PAYLOAD_XTMR,
+    SDP_PAYLOAD_T120,
+    SDP_MAX_STRING_PAYLOAD_TYPES,
+    SDP_PAYLOAD_UNSUPPORTED,
+    SDP_PAYLOAD_INVALID
+} sdp_payload_e;
+
+
+/* Payload type indicator */
+typedef enum {
+    SDP_PAYLOAD_NUMERIC,
+    SDP_PAYLOAD_ENUM
+} sdp_payload_ind_e;
+
+
+/* Image payload types */
+typedef enum {
+    SDP_PORT_NUM_ONLY,                  /* <port> or '$'                */
+    SDP_PORT_NUM_COUNT,                 /* <port>/<number of ports>     */
+    SDP_PORT_VPI_VCI,                   /* <vpi>/<vci>                  */
+    SDP_PORT_VCCI,                      /* <vcci>                       */
+    SDP_PORT_NUM_VPI_VCI,               /* <port>/<vpi>/<vci>           */
+    SDP_PORT_VCCI_CID,                  /* <vcci>/<cid> or '$'/'$'      */
+    SDP_PORT_NUM_VPI_VCI_CID,           /* <port>/<vpi>/<vci>/<cid>     */
+    SDP_MAX_PORT_FORMAT_TYPES,
+    SDP_PORT_FORMAT_INVALID
+} sdp_port_format_e;
+
+
+/* Fmtp attribute format Types */
+typedef enum {
+    SDP_FMTP_NTE,
+    SDP_FMTP_CODEC_INFO,
+    SDP_FMTP_MODE,
+    SDP_FMTP_DATACHANNEL,
+    SDP_FMTP_UNKNOWN_TYPE,
+    SDP_FMTP_MAX_TYPE
+} sdp_fmtp_format_type_e;
+
+
+/* T.38 Rate Mgmt Types */
+typedef enum {
+    SDP_T38_LOCAL_TCF,
+    SDP_T38_TRANSFERRED_TCF,
+    SDP_T38_UNKNOWN_RATE,
+    SDP_T38_MAX_RATES
+} sdp_t38_ratemgmt_e;
+
+
+/* T.38 udp EC Types */
+typedef enum {
+    SDP_T38_UDP_REDUNDANCY,
+    SDP_T38_UDP_FEC,
+    SDP_T38_UDPEC_UNKNOWN,
+    SDP_T38_MAX_UDPEC
+} sdp_t38_udpec_e;
+
+/* Bitmaps for manipulating sdp_direction_e */
+typedef enum {
+    SDP_DIRECTION_FLAG_SEND=0x01,
+    SDP_DIRECTION_FLAG_RECV=0x02
+} sdp_direction_flag_e;
+
+/* Media flow direction */
+typedef enum {
+    SDP_DIRECTION_INACTIVE = 0,
+    SDP_DIRECTION_SENDONLY = SDP_DIRECTION_FLAG_SEND,
+    SDP_DIRECTION_RECVONLY = SDP_DIRECTION_FLAG_RECV,
+    SDP_DIRECTION_SENDRECV = SDP_DIRECTION_FLAG_SEND | SDP_DIRECTION_FLAG_RECV,
+    SDP_MAX_QOS_DIRECTIONS
+} sdp_direction_e;
+
+#define SDP_DIRECTION_PRINT(arg) \
+    (((sdp_direction_e)(arg)) == SDP_DIRECTION_INACTIVE ? "SDP_DIRECTION_INACTIVE " : \
+     ((sdp_direction_e)(arg)) == SDP_DIRECTION_SENDONLY ? "SDP_DIRECTION_SENDONLY": \
+     ((sdp_direction_e)(arg)) == SDP_DIRECTION_RECVONLY ? "SDP_DIRECTION_RECVONLY ": \
+     ((sdp_direction_e)(arg)) == SDP_DIRECTION_SENDRECV ? " SDP_DIRECTION_SENDRECV": "SDP_MAX_QOS_DIRECTIONS")
+
+
+/* QOS Strength tag */
+typedef enum {
+    SDP_QOS_STRENGTH_OPT,
+    SDP_QOS_STRENGTH_MAND,
+    SDP_QOS_STRENGTH_SUCC,
+    SDP_QOS_STRENGTH_FAIL,
+    SDP_QOS_STRENGTH_NONE,
+    SDP_MAX_QOS_STRENGTH,
+    SDP_QOS_STRENGTH_UNKNOWN
+} sdp_qos_strength_e;
+
+
+/* QOS direction */
+typedef enum {
+    SDP_QOS_DIR_SEND,
+    SDP_QOS_DIR_RECV,
+    SDP_QOS_DIR_SENDRECV,
+    SDP_QOS_DIR_NONE,
+    SDP_MAX_QOS_DIR,
+    SDP_QOS_DIR_UNKNOWN
+} sdp_qos_dir_e;
+
+/* QoS Status types */
+typedef enum {
+    SDP_QOS_LOCAL,
+    SDP_QOS_REMOTE,
+    SDP_QOS_E2E,
+    SDP_MAX_QOS_STATUS_TYPES,
+    SDP_QOS_STATUS_TYPE_UNKNOWN
+} sdp_qos_status_types_e;
+
+/* QoS Status types */
+typedef enum {
+    SDP_CURR_QOS_TYPE,
+    SDP_CURR_UNKNOWN_TYPE,
+    SDP_MAX_CURR_TYPES
+} sdp_curr_type_e;
+
+/* QoS Status types */
+typedef enum {
+    SDP_DES_QOS_TYPE,
+    SDP_DES_UNKNOWN_TYPE,
+    SDP_MAX_DES_TYPES
+} sdp_des_type_e;
+
+/* QoS Status types */
+typedef enum {
+    SDP_CONF_QOS_TYPE,
+    SDP_CONF_UNKNOWN_TYPE,
+    SDP_MAX_CONF_TYPES
+} sdp_conf_type_e;
+
+
+/* Named event range result values. */
+typedef enum {
+    SDP_NO_MATCH,
+    SDP_PARTIAL_MATCH,
+    SDP_FULL_MATCH
+} sdp_ne_res_e;
+
+/* Fmtp attribute parameters for audio/video codec information */
+typedef enum {
+
+    /* mainly for audio codecs */
+    SDP_ANNEX_A,     /* 0 */
+    SDP_ANNEX_B,
+    SDP_BITRATE,
+
+    /* for video codecs */
+    SDP_QCIF,
+    SDP_CIF,
+    SDP_MAXBR,
+    SDP_SQCIF,
+    SDP_CIF4,
+    SDP_CIF16,
+    SDP_CUSTOM,
+    SDP_PAR,
+    SDP_CPCF,
+    SDP_BPP,
+    SDP_HRD,
+    SDP_PROFILE,
+    SDP_LEVEL,
+    SDP_INTERLACE,
+
+    /* H.264 related */
+    SDP_PROFILE_LEVEL_ID,     /* 17 */
+    SDP_PARAMETER_SETS,
+    SDP_PACKETIZATION_MODE,
+    SDP_INTERLEAVING_DEPTH,
+    SDP_DEINT_BUF_REQ,
+    SDP_MAX_DON_DIFF,
+    SDP_INIT_BUF_TIME,
+
+    SDP_MAX_MBPS,
+    SDP_MAX_FS,
+    SDP_MAX_CPB,
+    SDP_MAX_DPB,
+    SDP_MAX_BR,
+    SDP_REDUNDANT_PIC_CAP,
+    SDP_DEINT_BUF_CAP,
+    SDP_MAX_RCMD_NALU_SIZE,
+
+    SDP_PARAMETER_ADD,
+
+    /* Annexes - begin */
+    /* Some require special handling as they don't have token=token format*/
+    SDP_ANNEX_D,
+    SDP_ANNEX_F,
+    SDP_ANNEX_I,
+    SDP_ANNEX_J,
+    SDP_ANNEX_T,
+
+    /* These annexes have token=token format */
+    SDP_ANNEX_K,
+    SDP_ANNEX_N,
+    SDP_ANNEX_P,
+
+    SDP_MODE,
+    SDP_LEVEL_ASYMMETRY_ALLOWED,
+    SDP_MAX_AVERAGE_BIT_RATE,
+    SDP_USED_TX,
+    SDP_STEREO,
+    SDP_USE_IN_BAND_FEC,
+    SDP_MAX_CODED_AUDIO_BW,
+    SDP_CBR,
+    SDP_STREAMS,
+    SDP_PROTOCOL,
+    SDP_MAX_FMTP_PARAM,
+    SDP_FMTP_PARAM_UNKNOWN
+} sdp_fmtp_codec_param_e;
+
+/* Fmtp attribute parameters values for
+   fmtp attribute parameters which convey codec
+   information */
+
+typedef enum {
+    SDP_YES,
+    SDP_NO,
+    SDP_MAX_FMTP_PARAM_VAL,
+    SDP_FMTP_PARAM_UNKNOWN_VAL
+} sdp_fmtp_codec_param_val_e;
+
+/* silenceSupp suppPref */
+typedef enum {
+    SDP_SILENCESUPP_PREF_STANDARD,
+    SDP_SILENCESUPP_PREF_CUSTOM,
+    SDP_SILENCESUPP_PREF_NULL, /* "-" */
+    SDP_MAX_SILENCESUPP_PREF,
+    SDP_SILENCESUPP_PREF_UNKNOWN
+} sdp_silencesupp_pref_e;
+
+/* silenceSupp sidUse */
+typedef enum {
+    SDP_SILENCESUPP_SIDUSE_NOSID,
+    SDP_SILENCESUPP_SIDUSE_FIXED,
+    SDP_SILENCESUPP_SIDUSE_SAMPLED,
+    SDP_SILENCESUPP_SIDUSE_NULL, /* "-" */
+    SDP_MAX_SILENCESUPP_SIDUSE,
+    SDP_SILENCESUPP_SIDUSE_UNKNOWN
+} sdp_silencesupp_siduse_e;
+
+typedef enum {
+    SDP_MEDIADIR_ROLE_PASSIVE,
+    SDP_MEDIADIR_ROLE_ACTIVE,
+    SDP_MEDIADIR_ROLE_BOTH,
+    SDP_MEDIADIR_ROLE_REUSE,
+    SDP_MEDIADIR_ROLE_UNKNOWN,
+    SDP_MAX_MEDIADIR_ROLES,
+    SDP_MEDIADIR_ROLE_UNSUPPORTED,
+    SDP_MEDIADIR_ROLE_INVALID
+} sdp_mediadir_role_e;
+
+typedef enum {
+    SDP_GROUP_ATTR_FID,
+    SDP_GROUP_ATTR_LS,
+    SDP_GROUP_ATTR_ANAT,
+    SDP_MAX_GROUP_ATTR_VAL,
+    SDP_GROUP_ATTR_UNSUPPORTED
+} sdp_group_attr_e;
+
+typedef enum {
+    SDP_SRC_FILTER_INCL,
+    SDP_SRC_FILTER_EXCL,
+    SDP_MAX_FILTER_MODE,
+    SDP_FILTER_MODE_NOT_PRESENT
+} sdp_src_filter_mode_e;
+
+typedef enum {
+    SDP_RTCP_UNICAST_MODE_REFLECTION,
+    SDP_RTCP_UNICAST_MODE_RSI,
+    SDP_RTCP_MAX_UNICAST_MODE,
+    SDP_RTCP_UNICAST_MODE_NOT_PRESENT
+} sdp_rtcp_unicast_mode_e;
+
+/*
+ * sdp_srtp_fec_order_t
+ *  This type defines the order in which to perform FEC
+ *  (Forward Error Correction) and SRTP Encryption/Authentication.
+ */
+typedef enum sdp_srtp_fec_order_t_ {
+    SDP_SRTP_THEN_FEC, /* upon sending perform SRTP then FEC */
+    SDP_FEC_THEN_SRTP, /* upon sending perform FEC then SRTP */
+    SDP_SRTP_FEC_SPLIT /* upon sending perform SRTP Encryption,
+                         * then FEC, the SRTP Authentication */
+} sdp_srtp_fec_order_t;
+
+
+/*
+ *  sdp_srtp_crypto_suite_t
+ *   Enumeration of the crypto suites supported for MGCP SRTP
+ *   package.
+ */
+typedef enum sdp_srtp_crypto_suite_t_ {
+    SDP_SRTP_UNKNOWN_CRYPTO_SUITE = 0,
+    SDP_SRTP_AES_CM_128_HMAC_SHA1_32,
+    SDP_SRTP_AES_CM_128_HMAC_SHA1_80,
+    SDP_SRTP_F8_128_HMAC_SHA1_80,
+    SDP_SRTP_MAX_NUM_CRYPTO_SUITES
+} sdp_srtp_crypto_suite_t;
+
+/*
+ * SDP SRTP crypto suite definition parameters
+ *
+ * SDP_SRTP_<crypto_suite>_KEY_BYTES
+ *  The size of a master key for <crypto_suite> in bytes.
+ *
+ * SDP_SRTP_<crypto_suite>_SALT_BYTES
+ *  The size of a master salt for <crypto_suite> in bytes.
+ */
+#define SDP_SRTP_AES_CM_128_HMAC_SHA1_32_KEY_BYTES     16
+#define SDP_SRTP_AES_CM_128_HMAC_SHA1_32_SALT_BYTES    14
+#define SDP_SRTP_AES_CM_128_HMAC_SHA1_80_KEY_BYTES     16
+#define SDP_SRTP_AES_CM_128_HMAC_SHA1_80_SALT_BYTES    14
+#define SDP_SRTP_F8_128_HMAC_SHA1_80_KEY_BYTES         16
+#define SDP_SRTP_F8_128_HMAC_SHA1_80_SALT_BYTES        14
+
+/* SDP Defines */
+
+#define SDP_MAX_STRING_LEN      256  /* Max len for SDP string       */
+#define SDP_MAX_SHORT_STRING_LEN      12  /* Max len for a short SDP string  */
+#define SDP_MAX_PAYLOAD_TYPES   23  /* Max payload types in m= line */
+#define SDP_TOKEN_LEN           2   /* Len of <token>=              */
+#define SDP_CURRENT_VERSION     0   /* Current default SDP version  */
+#define SDP_MAX_PORT_PARAMS     4   /* Max m= port params - x/x/x/x */
+#define SDP_MIN_DYNAMIC_PAYLOAD 96  /* Min dynamic payload */
+#define SDP_MAX_DYNAMIC_PAYLOAD 127 /* Max dynamic payload */
+#define SDP_MIN_CIF_VALUE 1  /* applies to all  QCIF,CIF,CIF4,CIF16,SQCIF */
+#define SDP_MAX_CIF_VALUE 32 /* applies to all  QCIF,CIF,CIF4,CIF16,SQCIF */
+#define SDP_MAX_SRC_ADDR_LIST  1 /* Max source addrs for which filter applies */
+
+
+#define SDP_DEFAULT_PACKETIZATION_MODE_VALUE 0 /* max packetization mode for H.264 */
+#define SDP_MAX_PACKETIZATION_MODE_VALUE 2 /* max packetization mode for H.264 */
+
+#define SDP_MAX_LEVEL_ASYMMETRY_ALLOWED_VALUE 1 /* max level asymmetry allowed value for H.264 */
+#define SDP_DEFAULT_LEVEL_ASYMMETRY_ALLOWED_VALUE 1 /* default level asymmetry allowed value for H.264 */
+#define SDP_INVALID_LEVEL_ASYMMETRY_ALLOWED_VALUE 2 /* invalid value for level-asymmetry-allowed param for H.264 */
+
+
+/* Max number of stream ids that can be grouped together */
+#define SDP_MAX_GROUP_STREAM_ID 10
+
+
+#define SDP_MAGIC_NUM           0xabcdabcd
+
+#define SDP_UNSUPPORTED         "Unsupported"
+#define SDP_MAX_LINE_LEN   256 /* Max len for SDP Line */
+
+#define SDP_MAX_PROFILE_VALUE  10
+#define SDP_MAX_LEVEL_VALUE    100
+#define SDP_MIN_PROFILE_LEVEL_VALUE 0
+#define SDP_MAX_TTL_VALUE  255
+#define SDP_MIN_MCAST_ADDR_HI_BIT_VAL 224
+#define SDP_MAX_MCAST_ADDR_HI_BIT_VAL 239
+
+/* SDP Enum Types */
+
+typedef enum {
+    SDP_ERR_INVALID_CONF_PTR,
+    SDP_ERR_INVALID_SDP_PTR,
+    SDP_ERR_INTERNAL,
+    SDP_MAX_ERR_TYPES
+} sdp_errmsg_e;
+
+/* SDP Structure Definitions */
+
+/* String names of varios tokens */
+typedef struct {
+    char                     *name;
+    u8                        strlen;
+} sdp_namearray_t;
+
+/* c= line info */
+typedef struct {
+    sdp_nettype_e             nettype;
+    sdp_addrtype_e            addrtype;
+    char                      conn_addr[SDP_MAX_STRING_LEN+1];
+    tinybool                  is_multicast;
+    u16                       ttl;
+    u16                       num_of_addresses;
+} sdp_conn_t;
+
+/* t= line info */
+typedef struct sdp_timespec {
+    char                      start_time[SDP_MAX_STRING_LEN+1];
+    char                      stop_time[SDP_MAX_STRING_LEN+1];
+    struct sdp_timespec      *next_p;
+} sdp_timespec_t;
+
+
+/* k= line info */
+typedef struct sdp_encryptspec {
+    sdp_encrypt_type_e        encrypt_type;
+    char              encrypt_key[SDP_MAX_STRING_LEN+1];
+} sdp_encryptspec_t;
+
+
+/* FMTP attribute deals with named events in the range of 0-255 as
+ * defined in RFC 2833 */
+#define SDP_MIN_NE_VALUE      0
+#define SDP_MAX_NE_VALUES     256
+#define SDP_NE_BITS_PER_WORD  ( sizeof(u32) * 8 )
+#define SDP_NE_NUM_BMAP_WORDS ((SDP_MAX_NE_VALUES + SDP_NE_BITS_PER_WORD - 1)/SDP_NE_BITS_PER_WORD )
+#define SDP_NE_BIT_0          ( 0x00000001 )
+#define SDP_NE_ALL_BITS       ( 0xFFFFFFFF )
+
+#define SDP_DEINT_BUF_REQ_FLAG   0x1
+#define SDP_INIT_BUF_TIME_FLAG   0x2
+#define SDP_MAX_RCMD_NALU_SIZE_FLAG   0x4
+#define SDP_DEINT_BUF_CAP_FLAG   0x8
+
+typedef struct sdp_fmtp {
+    u16                       payload_num;
+    u32                       maxval;  /* maxval optimizes bmap search */
+    u32                       bmap[ SDP_NE_NUM_BMAP_WORDS ];
+    sdp_fmtp_format_type_e    fmtp_format; /* Gives the format type
+                                              for FMTP attribute*/
+    tinybool                  annexb_required;
+    tinybool                  annexa_required;
+
+    tinybool                  annexa;
+    tinybool                  annexb;
+    u32                       bitrate;
+    u32                       mode;
+
+    /* some OPUS specific fmtp params */
+    u32                       maxaveragebitrate;
+    u16                       usedtx;
+    u16                       stereo;
+    u16                       useinbandfec;
+    char                      maxcodedaudiobandwidth[SDP_MAX_STRING_LEN+1];
+    u16                       cbr;
+
+    /* some Data Channel specific fmtp params */
+    u16                       streams;   /* Num streams per Data Channel */
+    char                      protocol[SDP_MAX_STRING_LEN+1];
+
+    /* BEGIN - All Video related FMTP parameters */
+    u16                       qcif;
+    u16                       cif;
+    u16                       maxbr;
+    u16                       sqcif;
+    u16                       cif4;
+    u16                       cif16;
+
+    u16                       custom_x;
+    u16                       custom_y;
+    u16                       custom_mpi;
+    /* CUSTOM=360,240,4 implies X-AXIS=360, Y-AXIS=240; MPI=4 */
+    u16                       par_width;
+    u16                       par_height;
+    /* PAR=12:11 implies par_width=12, par_height=11 */
+
+    /* CPCF should be a float. IOS does not support float and so it is u16 */
+    /* For portable stack, CPCF should be defined as float and the parsing should
+     * be modified accordingly */
+    u16                       cpcf;
+    u16                       bpp;
+    u16                       hrd;
+
+    int16                     profile;
+    int16                     level;
+    tinybool                  is_interlace;
+
+    /* some more H.264 specific fmtp params */
+    char              profile_level_id[SDP_MAX_STRING_LEN+1];
+    char                      parameter_sets[SDP_MAX_STRING_LEN+1];
+    u16                       packetization_mode;
+    u16                       level_asymmetry_allowed;
+    u16                       interleaving_depth;
+    u32                       deint_buf_req;
+    u32                       max_don_diff;
+    u32                       init_buf_time;
+
+    u32                       max_mbps;
+    u32                       max_fs;
+    u32                       max_cpb;
+    u32                       max_dpb;
+    u32                       max_br;
+    tinybool                  redundant_pic_cap;
+    u32                       deint_buf_cap;
+    u32                       max_rcmd_nalu_size;
+    tinybool                  parameter_add;
+
+    tinybool                  annex_d;
+
+    tinybool                  annex_f;
+    tinybool                  annex_i;
+    tinybool                  annex_j;
+    tinybool                  annex_t;
+
+    /* H.263 codec requires annex K,N and P to have values */
+    u16                       annex_k_val;
+    u16                       annex_n_val;
+
+    /* Annex P can take one or more values in the range 1-4 . e.g P=1,3 */
+    u16                       annex_p_val_picture_resize; /* 1 = four; 2 = sixteenth */
+    u16                       annex_p_val_warp; /* 3 = half; 4=sixteenth */
+
+    u8                        flag;
+
+  /* END - All Video related FMTP parameters */
+
+} sdp_fmtp_t;
+
+/* a=qos|secure|X-pc-qos|X-qos info */
+typedef struct sdp_qos {
+    sdp_qos_strength_e        strength;
+    sdp_qos_dir_e             direction;
+    tinybool                  confirm;
+    sdp_qos_status_types_e    status_type;
+} sdp_qos_t;
+
+/* a=curr:qos status_type direction */
+typedef struct sdp_curr {
+    sdp_curr_type_e           type;
+    sdp_qos_status_types_e    status_type;
+    sdp_qos_dir_e             direction;
+} sdp_curr_t;
+
+/* a=des:qos strength status_type direction */
+typedef struct sdp_des {
+    sdp_des_type_e            type;
+    sdp_qos_strength_e        strength;
+    sdp_qos_status_types_e    status_type;
+    sdp_qos_dir_e             direction;
+} sdp_des_t;
+
+/* a=conf:qos status_type direction */
+typedef struct sdp_conf {
+    sdp_conf_type_e           type;
+    sdp_qos_status_types_e    status_type;
+    sdp_qos_dir_e             direction;
+} sdp_conf_t;
+
+
+/* a=rtpmap or a=sprtmap info */
+typedef struct sdp_transport_map {
+    u16                       payload_num;
+    char                      encname[SDP_MAX_STRING_LEN+1];
+    u32                       clockrate;
+    u16                       num_chan;
+} sdp_transport_map_t;
+
+
+/* a=rtr info */
+typedef struct sdp_rtr {
+    tinybool                  confirm;
+} sdp_rtr_t;
+
+/* a=subnet info */
+typedef struct sdp_subnet {
+    sdp_nettype_e             nettype;
+    sdp_addrtype_e            addrtype;
+    char                      addr[SDP_MAX_STRING_LEN+1];
+    int32                     prefix;
+} sdp_subnet_t;
+
+
+/* a=X-pc-codec info */
+typedef struct sdp_pccodec {
+    u16                       num_payloads;
+    ushort                    payload_type[SDP_MAX_PAYLOAD_TYPES];
+} sdp_pccodec_t;
+
+/* a=direction info */
+typedef struct sdp_comediadir {
+    sdp_mediadir_role_e      role;
+    tinybool                 conn_info_present;
+    sdp_conn_t               conn_info;
+    u32                      src_port;
+} sdp_comediadir_t;
+
+
+
+/* a=silenceSupp info */
+typedef struct sdp_silencesupp {
+    tinybool                  enabled;
+    tinybool                  timer_null;
+    u16                       timer;
+    sdp_silencesupp_pref_e    pref;
+    sdp_silencesupp_siduse_e  siduse;
+    tinybool                  fxnslevel_null;
+    u8                        fxnslevel;
+} sdp_silencesupp_t;
+
+
+/*
+ * a=mptime info */
+/* Note that an interval value of zero corresponds to
+ * the "-" syntax on the a= line.
+ */
+typedef struct sdp_mptime {
+    u16                       num_intervals;
+    ushort                    intervals[SDP_MAX_PAYLOAD_TYPES];
+} sdp_mptime_t;
+
+/*
+ * a=X-sidin:<val>, a=X-sidout:< val> and a=X-confid: <val>
+ * Stream Id,ConfID related attributes to be used for audio/video conferencing
+ *
+*/
+
+typedef struct sdp_stream_data {
+    char                      x_sidin[SDP_MAX_STRING_LEN+1];
+    char                      x_sidout[SDP_MAX_STRING_LEN+1];
+    char                      x_confid[SDP_MAX_STRING_LEN+1];
+    sdp_group_attr_e          group_attr; /* FID or LS */
+    u16                       num_group_id;
+    u16                       group_id_arr[SDP_MAX_GROUP_STREAM_ID];
+} sdp_stream_data_t;
+
+/*
+ * a=source-filter:<filter-mode> <filter-spec>
+ * <filter-spec> = <nettype> <addrtype> <dest-addr> <src_addr><src_addr>...
+ * One or more source addresses to apply filter, for one or more connection
+ * address in unicast/multicast environments
+ */
+typedef struct sdp_source_filter {
+   sdp_src_filter_mode_e  mode;
+   sdp_nettype_e     nettype;
+   sdp_addrtype_e    addrtype;
+   char              dest_addr[SDP_MAX_STRING_LEN+1];
+   u16               num_src_addr;
+   char              src_list[SDP_MAX_SRC_ADDR_LIST+1][SDP_MAX_STRING_LEN+1];
+} sdp_source_filter_t;
+
+/*
+ * b=<bw-modifier>:<val>
+ *
+*/
+typedef struct sdp_bw_data {
+    struct sdp_bw_data       *next_p;
+    sdp_bw_modifier_e        bw_modifier;
+    int                      bw_val;
+} sdp_bw_data_t;
+
+/*
+ * This structure houses a linked list of sdp_bw_data_t instances. Each
+ * sdp_bw_data_t instance represents one b= line.
+ */
+typedef struct sdp_bw {
+    u16                      bw_data_count;
+    sdp_bw_data_t            *bw_data_list;
+} sdp_bw_t;
+
+/* Media lines for AAL2 may have more than one transport type defined
+ * each with its own payload type list.  These are referred to as
+ * profile types instead of transport types.  This structure is used
+ * to handle these multiple profile types. Note: One additional profile
+ * field is needed because of the way parsing is done.  This is not an
+ * error. */
+typedef struct sdp_media_profiles {
+    u16             num_profiles;
+    sdp_transport_e profile[SDP_MAX_PROFILES+1];
+    u16             num_payloads[SDP_MAX_PROFILES];
+    sdp_payload_ind_e payload_indicator[SDP_MAX_PROFILES][SDP_MAX_PAYLOAD_TYPES];
+    u16             payload_type[SDP_MAX_PROFILES][SDP_MAX_PAYLOAD_TYPES];
+} sdp_media_profiles_t;
+
+
+/*
+ * sdp_srtp_crypto_context_t
+ *  This type is used to hold cryptographic context information.
+ *
+ */
+
+typedef struct sdp_srtp_crypto_context_t_ {
+    int32                   tag;
+    unsigned long           selection_flags;
+    sdp_srtp_crypto_suite_t suite;
+    unsigned char           master_key[SDP_SRTP_MAX_KEY_SIZE_BYTES];
+    unsigned char           master_salt[SDP_SRTP_MAX_SALT_SIZE_BYTES];
+    unsigned char           master_key_size_bytes;
+    unsigned char           master_salt_size_bytes;
+    unsigned long           ssrc; /* not used */
+    unsigned long           roc;  /* not used */
+    unsigned long           kdr;  /* not used */
+    unsigned short          seq;  /* not used */
+    sdp_srtp_fec_order_t    fec_order; /* not used */
+    unsigned char           master_key_lifetime[SDP_SRTP_MAX_LIFETIME_BYTES];
+    unsigned char           mki[SDP_SRTP_MAX_MKI_SIZE_BYTES];
+    u16                     mki_size_bytes;
+    char*                   session_parameters;
+} sdp_srtp_crypto_context_t;
+
+
+/* m= line info and associated attribute list */
+/* Note: Most of the port parameter values are 16-bit values.  We set
+ * the type to int32 so we can return either a 16-bit value or the
+ * choose value. */
+typedef struct sdp_mca {
+    sdp_media_e               media;
+    sdp_conn_t                conn;
+    sdp_transport_e           transport;
+    sdp_port_format_e         port_format;
+    int32                     port;
+    int32                     sctpport;
+    int32                     num_ports;
+    int32                     vpi;
+    u32                       vci;  /* VCI needs to be 32-bit */
+    int32                     vcci;
+    int32                     cid;
+    u16                       num_payloads;
+    sdp_payload_ind_e         payload_indicator[SDP_MAX_PAYLOAD_TYPES];
+    u16                       payload_type[SDP_MAX_PAYLOAD_TYPES];
+    sdp_media_profiles_t     *media_profiles_p;
+    tinybool                  sessinfo_found;
+    sdp_encryptspec_t         encrypt;
+    sdp_bw_t                  bw;
+    sdp_attr_e                media_direction; /* Either INACTIVE, SENDONLY,
+                                                  RECVONLY, or SENDRECV */
+    u32                       mid;
+    struct sdp_attr          *media_attrs_p;
+    struct sdp_mca           *next_p;
+} sdp_mca_t;
+
+
+/* generic a= line info */
+typedef struct sdp_attr {
+    sdp_attr_e                type;
+    union {
+        tinybool              boolean_val;
+        u32                   u32_val;
+        char                  string_val[SDP_MAX_STRING_LEN+1];
+        char                  ice_attr[SDP_MAX_STRING_LEN+1];
+        sdp_fmtp_t            fmtp;
+        sdp_qos_t             qos;
+        sdp_curr_t            curr;
+        sdp_des_t             des;
+        sdp_conf_t            conf;
+        sdp_transport_map_t   transport_map;    /* A rtpmap or sprtmap */
+        sdp_subnet_t          subnet;
+        sdp_t38_ratemgmt_e    t38ratemgmt;
+        sdp_t38_udpec_e       t38udpec;
+        sdp_pccodec_t         pccodec;
+        sdp_silencesupp_t     silencesupp;
+        sdp_mca_t            *cap_p;    /* A X-CAP or CDSC attribute */
+        sdp_rtr_t             rtr;
+    sdp_comediadir_t      comediadir;
+    sdp_srtp_crypto_context_t srtp_context;
+        sdp_mptime_t          mptime;
+        sdp_stream_data_t     stream_data;
+        char                  unknown[SDP_MAX_STRING_LEN+1];
+        sdp_source_filter_t   source_filter;
+    } attr;
+    struct sdp_attr          *next_p;
+} sdp_attr_t;
+
+typedef struct sdp_srtp_crypto_suite_list_ {
+    sdp_srtp_crypto_suite_t crypto_suite_val;
+    char * crypto_suite_str;
+    unsigned char key_size_bytes;
+    unsigned char salt_size_bytes;
+} sdp_srtp_crypto_suite_list;
+
+/* Application configuration options */
+typedef struct sdp_conf_options {
+    u32                       magic_num;
+    tinybool                  debug_flag[SDP_MAX_DEBUG_TYPES];
+    tinybool                  version_reqd;
+    tinybool                  owner_reqd;
+    tinybool                  session_name_reqd;
+    tinybool                  timespec_reqd;
+    tinybool                  media_supported[SDP_MAX_MEDIA_TYPES];
+    tinybool                  nettype_supported[SDP_MAX_NETWORK_TYPES];
+    tinybool                  addrtype_supported[SDP_MAX_ADDR_TYPES];
+    tinybool                  transport_supported[SDP_MAX_TRANSPORT_TYPES];
+    tinybool                  allow_choose[SDP_MAX_CHOOSE_PARAMS];
+    /* Statistics counts */
+    u32                       num_builds;
+    u32                       num_parses;
+    u32                       num_not_sdp_desc;
+    u32                       num_invalid_token_order;
+    u32                       num_invalid_param;
+    u32                       num_no_resource;
+    struct sdp_conf_options  *next_p;
+} sdp_conf_options_t;
+
+
+/* Session level SDP info with pointers to media line info. */
+/* Elements here that can only be one of are included directly. Elements */
+/* that can be more than one are pointers.                               */
+typedef struct {
+    char                      peerconnection[PC_HANDLE_SIZE];
+    u32                       magic_num;
+    sdp_conf_options_t       *conf_p;
+    tinybool                  debug_flag[SDP_MAX_DEBUG_TYPES];
+    char                      debug_str[SDP_MAX_STRING_LEN+1];
+    u32                       debug_id;
+    int32                     version; /* version is really a u16 */
+    char                      owner_name[SDP_MAX_STRING_LEN+1];
+    char                      owner_sessid[SDP_MAX_STRING_LEN+1];
+    char                      owner_version[SDP_MAX_STRING_LEN+1];
+    sdp_nettype_e             owner_network_type;
+    sdp_addrtype_e            owner_addr_type;
+    char                      owner_addr[SDP_MAX_STRING_LEN+1];
+    char                      sessname[SDP_MAX_STRING_LEN+1];
+    tinybool                  sessinfo_found;
+    tinybool                  uri_found;
+    sdp_conn_t                default_conn;
+    sdp_timespec_t           *timespec_p;
+    sdp_encryptspec_t         encrypt;
+    sdp_bw_t                  bw;
+    sdp_attr_t               *sess_attrs_p;
+
+    /* Info to help with building capability attributes. */
+    u16                       cur_cap_num;
+    sdp_mca_t                *cur_cap_p;
+    /* Info to help parsing X-cpar attrs. */
+    u16                       cap_valid;
+    u16                       last_cap_inst;
+    /* Info to help building X-cpar/cpar attrs. */
+    sdp_attr_e            last_cap_type;
+
+    /* MCA - Media, connection, and attributes */
+    sdp_mca_t                *mca_p;
+    ushort                    mca_count;
+} sdp_t;
+
+
+/* Token processing table. */
+typedef struct {
+    char *name;
+    sdp_result_e (*parse_func)(sdp_t *sdp_p, u16 level, const char *ptr);
+    sdp_result_e (*build_func)(sdp_t *sdp_p, u16 level, flex_string *fs);
+} sdp_tokenarray_t;
+
+
+/* Attribute processing table. */
+typedef struct {
+    char *name;
+    u16 strlen;
+    sdp_result_e (*parse_func)(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                               const char *ptr);
+    sdp_result_e (*build_func)(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                               flex_string *fs);
+} sdp_attrarray_t;
+
+
+/* Prototypes */
+
+/* sdp_config.c */
+extern void *sdp_init_config(void);
+extern void sdp_appl_debug(void *config_p, sdp_debug_e debug_type,
+                           tinybool debug_flag);
+extern void sdp_require_version(void *config_p, tinybool version_required);
+extern void sdp_require_owner(void *config_p, tinybool owner_required);
+extern void sdp_require_session_name(void *config_p,
+                                     tinybool sess_name_required);
+extern void sdp_require_timespec(void *config_p, tinybool timespec_required);
+extern void sdp_media_supported(void *config_p, sdp_media_e media_type,
+                        tinybool media_supported);
+extern void sdp_nettype_supported(void *config_p, sdp_nettype_e nettype,
+                          tinybool nettype_supported);
+extern void sdp_addrtype_supported(void *config_p, sdp_addrtype_e addrtype,
+                           tinybool addrtype_supported);
+extern void sdp_transport_supported(void *config_p, sdp_transport_e transport,
+                            tinybool transport_supported);
+extern void sdp_allow_choose(void *config_p, sdp_choose_param_e param,
+                             tinybool choose_allowed);
+
+/* sdp_main.c */
+extern sdp_t *sdp_init_description(const char *peerconnection, void *config_p);
+extern void sdp_debug(sdp_t *sdp_ptr, sdp_debug_e debug_type, tinybool debug_flag);
+extern void sdp_set_string_debug(sdp_t *sdp_ptr, const char *debug_str);
+extern sdp_result_e sdp_parse(sdp_t *sdp_ptr, char **bufp, u16 len);
+extern sdp_result_e sdp_build(sdp_t *sdp_ptr, flex_string *fs);
+extern sdp_t *sdp_copy(sdp_t *sdp_ptr);
+extern sdp_result_e sdp_free_description(sdp_t *sdp_ptr);
+extern void sdp_parse_error(const char *peerconnection, const char *format, ...);
+
+extern const char *sdp_get_result_name(sdp_result_e rc);
+
+
+/* sdp_access.c */
+extern tinybool sdp_version_valid(void *sdp_p);
+extern int32 sdp_get_version(void *sdp_p);
+extern sdp_result_e sdp_set_version(void *sdp_p, int32 version);
+
+extern tinybool sdp_owner_valid(void *sdp_p);
+extern const char *sdp_get_owner_username(void *sdp_p);
+extern const char *sdp_get_owner_sessionid(void *sdp_p);
+extern const char *sdp_get_owner_version(void *sdp_p);
+extern sdp_nettype_e sdp_get_owner_network_type(void *sdp_p);
+extern sdp_addrtype_e sdp_get_owner_address_type(void *sdp_p);
+extern const char *sdp_get_owner_address(void *sdp_p);
+extern sdp_result_e sdp_set_owner_username(void *sdp_p, const char *username);
+extern sdp_result_e sdp_set_owner_sessionid(void *sdp_p, const char *sessid);
+extern sdp_result_e sdp_set_owner_version(void *sdp_p, const char *version);
+extern sdp_result_e sdp_set_owner_network_type(void *sdp_p,
+                                               sdp_nettype_e network_type);
+extern sdp_result_e sdp_set_owner_address_type(void *sdp_p,
+                                               sdp_addrtype_e address_type);
+extern sdp_result_e sdp_set_owner_address(void *sdp_p, const char *address);
+
+extern tinybool sdp_session_name_valid(void *sdp_p);
+extern const char *sdp_get_session_name(void *sdp_p);
+extern sdp_result_e sdp_set_session_name(void *sdp_p, const char *sessname);
+
+extern tinybool sdp_timespec_valid(void *sdp_ptr);
+extern const char *sdp_get_time_start(void *sdp_ptr);
+extern const char *sdp_get_time_stop(void *sdp_ptr);
+sdp_result_e sdp_set_time_start(void *sdp_ptr, const char *start_time);
+sdp_result_e sdp_set_time_stop(void *sdp_ptr, const char *stop_time);
+
+extern tinybool sdp_encryption_valid(void *sdp_ptr, u16 level);
+extern sdp_encrypt_type_e sdp_get_encryption_method(void *sdp_ptr, u16 level);
+extern const char *sdp_get_encryption_key(void *sdp_ptr, u16 level);
+extern sdp_result_e sdp_set_encryption_method(void *sdp_ptr, u16 level,
+                                              sdp_encrypt_type_e method);
+extern sdp_result_e sdp_set_encryption_key(void *sdp_ptr, u16 level,
+                                           const char *key);
+
+extern tinybool sdp_connection_valid(void *sdp_p, u16 level);
+extern tinybool sdp_bw_line_exists(void *sdp_ptr, u16 level, u16 inst_num);
+extern tinybool sdp_bandwidth_valid(void *sdp_p, u16 level, u16 inst_num);
+extern sdp_nettype_e sdp_get_conn_nettype(void *sdp_p, u16 level);
+extern sdp_addrtype_e sdp_get_conn_addrtype(void *sdp_p, u16 level);
+extern const char *sdp_get_conn_address(void *sdp_p, u16 level);
+
+extern tinybool sdp_is_mcast_addr (void *sdp_ptr, u16 level);
+extern int32 sdp_get_mcast_ttl(void *sdp_ptr, u16 level);
+extern int32 sdp_get_mcast_num_of_addresses(void *sdp_ptr, u16 level);
+
+extern sdp_result_e sdp_set_conn_nettype(void *sdp_p, u16 level,
+                                  sdp_nettype_e nettype);
+extern sdp_result_e sdp_set_conn_addrtype(void *sdp_p, u16 level,
+                                   sdp_addrtype_e addrtype);
+extern sdp_result_e sdp_set_conn_address(void *sdp_p, u16 level,
+                                         const char *address);
+extern sdp_result_e sdp_set_mcast_addr_fields(void *sdp_ptr, u16 level,
+                                            u16 ttl, u16 num_addr);
+
+extern tinybool sdp_media_line_valid(void *sdp_ptr, u16 level);
+extern u16 sdp_get_num_media_lines(void *sdp_ptr);
+extern sdp_media_e sdp_get_media_type(void *sdp_ptr, u16 level);
+extern sdp_port_format_e sdp_get_media_port_format(void *sdp_ptr, u16 level);
+extern int32 sdp_get_media_portnum(void *sdp_ptr, u16 level);
+extern int32 sdp_get_media_portcount(void *sdp_ptr, u16 level);
+extern int32 sdp_get_media_vpi(void *sdp_ptr, u16 level);
+extern u32 sdp_get_media_vci(void *sdp_ptr, u16 level);
+extern int32 sdp_get_media_vcci(void *sdp_ptr, u16 level);
+extern int32 sdp_get_media_cid(void *sdp_ptr, u16 level);
+extern sdp_transport_e sdp_get_media_transport(void *sdp_ptr, u16 level);
+extern u16 sdp_get_media_num_profiles(void *sdp_ptr, u16 level);
+extern sdp_transport_e sdp_get_media_profile(void *sdp_ptr, u16 level,
+                                              u16 profile_num);
+extern u16 sdp_get_media_num_payload_types(void *sdp_ptr, u16 level);
+extern u16 sdp_get_media_profile_num_payload_types(void *sdp_ptr, u16 level,
+                                                    u16 profile_num);
+extern u32 sdp_get_media_payload_type(void *sdp_ptr, u16 level,
+                                u16 payload_num, sdp_payload_ind_e *indicator);
+extern u32 sdp_get_media_profile_payload_type(void *sdp_ptr, u16 level,
+                  u16 prof_num, u16 payload_num, sdp_payload_ind_e *indicator);
+extern sdp_result_e sdp_insert_media_line(void *sdp_ptr, u16 level);
+extern void sdp_delete_media_line(void *sdp_ptr, u16 level);
+extern sdp_result_e sdp_set_media_type(void *sdp_ptr, u16 level,
+                                       sdp_media_e media);
+extern sdp_result_e sdp_set_media_port_format(void *sdp_ptr, u16 level,
+                                       sdp_port_format_e port_format);
+extern sdp_result_e sdp_set_media_portnum(void *sdp_ptr, u16 level,
+                                          int32 portnum, int32 sctpport);
+extern sdp_result_e sdp_set_media_portcount(void *sdp_ptr, u16 level,
+                                            int32 num_ports);
+extern sdp_result_e sdp_set_media_vpi(void *sdp_ptr, u16 level, int32 vpi);
+extern sdp_result_e sdp_set_media_vci(void *sdp_ptr, u16 level, u32 vci);
+extern sdp_result_e sdp_set_media_vcci(void *sdp_ptr, u16 level, int32 vcci);
+extern sdp_result_e sdp_set_media_cid(void *sdp_ptr, u16 level, int32 cid);
+extern sdp_result_e sdp_set_media_transport(void *sdp_ptr, u16 level,
+                                            sdp_transport_e transport);
+extern sdp_result_e sdp_add_media_profile(void *sdp_ptr, u16 level,
+                                           sdp_transport_e profile);
+extern sdp_result_e sdp_add_media_payload_type(void *sdp_ptr, u16 level,
+                               u16 payload_type, sdp_payload_ind_e indicator);
+extern sdp_result_e sdp_add_media_profile_payload_type(void *sdp_ptr,
+                               u16 level, u16 prof_num, u16 payload_type,
+                               sdp_payload_ind_e indicator);
+
+/* sdp_attr_access.c */
+extern sdp_result_e sdp_add_new_attr(void *sdp_ptr, u16 level, u8 cap_num,
+                                     sdp_attr_e attr_type, u16 *inst_num);
+extern sdp_result_e sdp_copy_attr (void *src_sdp_ptr, void *dst_sdp_ptr,
+                                   u16 src_level, u16 dst_level,
+                                   u8 src_cap_num, u8 dst_cap_num,
+                                   sdp_attr_e src_attr_type, u16 src_inst_num);
+extern sdp_result_e sdp_copy_all_attrs(void *src_sdp_ptr, void *dst_sdp_ptr,
+                                       u16 src_level, u16 dst_level);
+extern sdp_result_e sdp_attr_num_instances(void *sdp_ptr, u16 level,
+                         u8 cap_num, sdp_attr_e attr_type, u16 *num_attr_inst);
+extern sdp_result_e sdp_get_total_attrs(void *sdp_ptr, u16 level, u8 cap_num,
+                                        u16 *num_attrs);
+extern sdp_result_e sdp_get_attr_type(void *sdp_ptr, u16 level, u8 cap_num,
+                          u16 attr_num, sdp_attr_e *attr_type, u16 *inst_num);
+extern sdp_result_e sdp_delete_attr(void *sdp_ptr, u16 level, u8 cap_num,
+                             sdp_attr_e attr_type, u16 inst_num);
+extern sdp_result_e sdp_delete_all_attrs(void *sdp_ptr, u16 level, u8 cap_num);
+extern tinybool sdp_attr_valid(void *sdp_ptr, sdp_attr_e attr_type,
+                                u16 level, u8 cap_num, u16 inst_num);
+extern const char *sdp_attr_get_simple_string(void *sdp_ptr,
+                   sdp_attr_e attr_type, u16 level, u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_set_simple_string(void *sdp_ptr,
+                   sdp_attr_e attr_type, u16 level,
+                   u8 cap_num, u16 inst_num, const char *string_parm);
+extern u32 sdp_attr_get_simple_u32(void *sdp_ptr, sdp_attr_e attr_type,
+                                    u16 level, u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_set_simple_u32(void *sdp_ptr,
+                                      sdp_attr_e attr_type, u16 level,
+                                      u8 cap_num, u16 inst_num, u32 num_parm);
+extern tinybool sdp_attr_get_simple_boolean(void *sdp_ptr,
+                   sdp_attr_e attr_type, u16 level, u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_set_simple_boolean(void *sdp_ptr,
+                   sdp_attr_e attr_type, u16 level, u8 cap_num,
+                   u16 inst_num, u32 bool_parm);
+extern const char* sdp_attr_get_maxprate(void *sdp_ptr, u16 level,
+                                         u16 inst_num);
+extern sdp_result_e sdp_attr_set_maxprate(void *sdp_ptr, u16 level,
+                                       u16 inst_num, const char *string_parm);
+extern sdp_t38_ratemgmt_e sdp_attr_get_t38ratemgmt(void *sdp_ptr, u16 level,
+                                                   u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_set_t38ratemgmt(void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             sdp_t38_ratemgmt_e t38ratemgmt);
+extern sdp_t38_udpec_e sdp_attr_get_t38udpec(void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_set_t38udpec(void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num,
+                                          sdp_t38_udpec_e t38udpec);
+extern sdp_direction_e sdp_get_media_direction(void *sdp_ptr, u16 level,
+                                               u8 cap_num);
+extern sdp_qos_strength_e sdp_attr_get_qos_strength(void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num);
+extern sdp_qos_status_types_e sdp_attr_get_qos_status_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num);
+extern sdp_qos_dir_e sdp_attr_get_qos_direction(void *sdp_ptr, u16 level,
+                               u8 cap_num, sdp_attr_e qos_attr, u16 inst_num);
+extern tinybool sdp_attr_get_qos_confirm(void *sdp_ptr, u16 level,
+                               u8 cap_num, sdp_attr_e qos_attr, u16 inst_num);
+extern sdp_curr_type_e sdp_attr_get_curr_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num);
+extern sdp_des_type_e sdp_attr_get_des_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num);
+extern sdp_conf_type_e sdp_attr_get_conf_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num);
+extern sdp_result_e sdp_attr_set_conf_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                sdp_conf_type_e conf_type);
+extern sdp_result_e sdp_attr_set_des_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                sdp_des_type_e des_type);
+extern sdp_result_e sdp_attr_set_curr_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                sdp_curr_type_e curr_type);
+extern sdp_result_e sdp_attr_set_qos_strength(void *sdp_ptr, u16 level,
+                                 u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                 sdp_qos_strength_e strength);
+extern sdp_result_e sdp_attr_set_qos_direction(void *sdp_ptr, u16 level,
+                                 u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                 sdp_qos_dir_e direction);
+extern sdp_result_e sdp_attr_set_qos_status_type(void *sdp_ptr, u16 level,
+                                 u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                 sdp_qos_status_types_e status_type);
+extern sdp_result_e sdp_attr_set_qos_confirm(void *sdp_ptr, u16 level,
+                                 u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                 tinybool confirm);
+extern sdp_nettype_e sdp_attr_get_subnet_nettype(void *sdp_ptr, u16 level,
+                                                 u8 cap_num, u16 inst_num);
+extern sdp_addrtype_e sdp_attr_get_subnet_addrtype(void *sdp_ptr, u16 level,
+                                                   u8 cap_num, u16 inst_num);
+extern const char *sdp_attr_get_subnet_addr(void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_subnet_prefix(void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_set_subnet_nettype(void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                sdp_nettype_e nettype);
+extern sdp_result_e sdp_attr_set_subnet_addrtype(void *sdp_ptr, u16 level,
+                                                 u8 cap_num, u16 inst_num,
+                                                 sdp_addrtype_e addrtype);
+extern sdp_result_e sdp_attr_set_subnet_addr(void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             const char *addr);
+extern sdp_result_e sdp_attr_set_subnet_prefix(void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                               int32 prefix);
+extern tinybool sdp_attr_rtpmap_payload_valid(void *sdp_ptr, u16 level,
+                                  u8 cap_num, u16 *inst_num, u16 payload_type);
+extern u16 sdp_attr_get_rtpmap_payload_type(void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num);
+extern const char *sdp_attr_get_rtpmap_encname(void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num);
+extern u32 sdp_attr_get_rtpmap_clockrate(void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num);
+extern u16 sdp_attr_get_rtpmap_num_chan(void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_set_rtpmap_payload_type(void *sdp_ptr, u16 level,
+                                                     u8 cap_num, u16 inst_num,
+                                                     u16 payload_num);
+extern sdp_result_e sdp_attr_set_rtpmap_encname(void *sdp_ptr, u16 level,
+                              u8 cap_num, u16 inst_num, const char *encname);
+extern sdp_result_e sdp_attr_set_rtpmap_clockrate(void *sdp_ptr, u16 level,
+                                                  u8 cap_num, u16 inst_num,
+                                                  u32 clockrate);
+extern sdp_result_e sdp_attr_set_rtpmap_num_chan(void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num, u16 num_chan);
+extern tinybool sdp_attr_sprtmap_payload_valid(void *sdp_ptr, u16 level,
+                                  u8 cap_num, u16 *inst_num, u16 payload_type);
+extern u16 sdp_attr_get_sprtmap_payload_type(void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num);
+extern const char *sdp_attr_get_sprtmap_encname(void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num);
+extern u32 sdp_attr_get_sprtmap_clockrate(void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num);
+extern u16 sdp_attr_get_sprtmap_num_chan(void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_set_sprtmap_payload_type(void *sdp_ptr, u16 level,
+                                                     u8 cap_num, u16 inst_num,
+                                                     u16 payload_num);
+extern sdp_result_e sdp_attr_set_sprtmap_encname(void *sdp_ptr, u16 level,
+                              u8 cap_num, u16 inst_num, const char *encname);
+extern sdp_result_e sdp_attr_set_sprtmap_clockrate(void *sdp_ptr, u16 level,
+                                                  u8 cap_num, u16 inst_num,
+                                                  u16 clockrate);
+extern sdp_result_e sdp_attr_set_sprtmap_num_chan(void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num, u16 num_chan);
+extern tinybool sdp_attr_fmtp_payload_valid(void *sdp_ptr, u16 level,
+                                  u8 cap_num, u16 *inst_num, u16 payload_type);
+extern u16 sdp_attr_get_fmtp_payload_type(void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num);
+extern sdp_ne_res_e sdp_attr_fmtp_is_range_set(void *sdp_ptr, u16 level,
+                           u8 cap_num, u16 inst_num, u8 low_val, u8 high_val);
+extern tinybool sdp_attr_fmtp_valid(void *sdp_ptr, u16 level, u8 cap_num,
+                                   u16 inst_num, u16 appl_maxval, u32* evt_array);
+extern sdp_result_e sdp_attr_set_fmtp_payload_type(void *sdp_ptr, u16 level,
+                                                   u8 cap_num, u16 inst_num,
+                                                   u16 payload_num);
+extern sdp_result_e sdp_attr_get_fmtp_range(void *sdp_ptr, u16 level,
+                           u8 cap_num, u16 inst_num, u32 *bmap);
+extern sdp_result_e sdp_attr_set_fmtp_range(void *sdp_ptr, u16 level,
+                           u8 cap_num, u16 inst_num, u8 low_val, u8 high_val);
+extern sdp_result_e sdp_attr_set_fmtp_bitmap(void *sdp_ptr, u16 level,
+                           u8 cap_num, u16 inst_num, u32 *bmap, u32 maxval);
+extern sdp_result_e sdp_attr_clear_fmtp_range(void *sdp_ptr, u16 level,
+                           u8 cap_num, u16 inst_num, u8 low_val, u8 high_val);
+extern sdp_ne_res_e sdp_attr_compare_fmtp_ranges(void *src_sdp_ptr,
+                           void *dst_sdp_ptr, u16 src_level, u16 dst_level,
+                           u8 src_cap_num, u8 dst_cap_num, u16 src_inst_num,
+                           u16 dst_inst_num);
+extern sdp_result_e sdp_attr_copy_fmtp_ranges(void *src_sdp_ptr,
+                           void *dst_sdp_ptr, u16 src_level, u16 dst_level,
+                           u8 src_cap_num, u8 dst_cap_num, u16 src_inst_num,
+                           u16 dst_inst_num);
+extern sdp_result_e sdp_attr_set_fmtp_annexa (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              tinybool annexa);
+extern sdp_result_e sdp_attr_set_fmtp_mode(void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num,
+                                           u32 mode);
+extern u32 sdp_attr_get_fmtp_mode_for_payload_type (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u32 payload_type);
+
+extern sdp_result_e sdp_attr_set_fmtp_annexb (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              tinybool annexb);
+
+extern sdp_result_e sdp_attr_set_fmtp_bitrate_type  (void *sdp_ptr, u16 level,
+                                                     u8 cap_num, u16 inst_num,
+                                                     u32 bitrate);
+extern sdp_result_e sdp_attr_set_fmtp_cif  (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num,
+                                            u16 cif);
+extern sdp_result_e sdp_attr_set_fmtp_qcif  (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             u16 qcif);
+extern sdp_result_e sdp_attr_set_fmtp_sqcif  (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              u16 sqcif);
+extern sdp_result_e sdp_attr_set_fmtp_cif4  (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             u16 cif4);
+extern sdp_result_e sdp_attr_set_fmtp_cif16  (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              u16 cif16);
+extern sdp_result_e sdp_attr_set_fmtp_maxbr  (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              u16 maxbr);
+extern sdp_result_e sdp_attr_set_fmtp_max_average_bitrate (void *sdp_ptr, u16 level,
+                                                           u8 cap_num, u16 inst_num,
+                                                           u32 maxaveragebitrate);
+extern sdp_result_e sdp_attr_set_fmtp_usedtx (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              tinybool usedtx);
+extern sdp_result_e sdp_attr_set_fmtp_stereo (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              tinybool stereo);
+extern sdp_result_e sdp_attr_set_fmtp_useinbandfec (void *sdp_ptr, u16 level,
+                                                    u8 cap_num, u16 inst_num,
+                                                    tinybool useinbandfec);
+extern sdp_result_e sdp_attr_set_fmtp_maxcodedaudiobandwidth (void *sdp_ptr, u16 level,
+                                                              u8 cap_num, u16 inst_num,
+                                                              const char *maxcodedaudiobandwidth);
+extern sdp_result_e sdp_attr_set_fmtp_cbr (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num,
+                                           tinybool cbr);
+extern sdp_result_e sdp_attr_set_fmtp_custom (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              u16 custom_x, u16 custom_y,
+                                              u16 custom_mpi);
+extern sdp_result_e sdp_attr_set_fmtp_par (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num,
+                                           u16 par_width, u16 par_height);
+extern sdp_result_e sdp_attr_set_fmtp_bpp  (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num,
+                                            u16 bpp);
+extern sdp_result_e sdp_attr_set_fmtp_hrd  (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num,
+                                            u16 hrd);
+
+extern sdp_result_e sdp_attr_set_fmtp_h263_num_params (void *sdp_ptr,
+                                                       int16 level,
+                                                       u8 cap_num,
+                                                       u16 inst_num,
+                                                       int16 profile,
+                                                       u16 h263_level,
+                                                       tinybool interlace);
+
+extern sdp_result_e sdp_attr_set_fmtp_profile_level_id (void *sdp_ptr,
+                                                       u16 level,
+                                                       u8 cap_num,
+                                                       u16 inst_num,
+                                                       const char *prof_id);
+
+extern sdp_result_e sdp_attr_set_fmtp_parameter_sets (void *sdp_ptr,
+                                                      u16 level,
+                                                      u8 cap_num,
+                                                      u16 inst_num,
+                                                      const char *parameter_sets);
+
+extern sdp_result_e sdp_attr_set_fmtp_deint_buf_req (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                u32 deint_buf_req);
+
+extern sdp_result_e sdp_attr_set_fmtp_init_buf_time (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                u32 init_buf_time);
+
+extern sdp_result_e sdp_attr_set_fmtp_max_don_diff (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                u32 max_don_diff);
+
+extern sdp_result_e sdp_attr_set_fmtp_interleaving_depth (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                u16 interleaving_depth);
+
+extern sdp_result_e sdp_attr_set_fmtp_pack_mode (void *sdp_ptr,
+                                                u16 level,
+                                                u8 cap_num,
+                                                u16 inst_num,
+                                                u16 pack_mode);
+
+extern sdp_result_e sdp_attr_set_fmtp_level_asymmetry_allowed (void *sdp_ptr,
+                                                u16 level,
+                                                u8 cap_num,
+                                                u16 inst_num,
+                                                u16 level_asymmetry_allowed);
+
+extern sdp_result_e sdp_attr_set_fmtp_redundant_pic_cap (void *sdp_ptr,
+                                                  u16 level,
+                                                  u8 cap_num,
+                                                  u16 inst_num,
+                                                  tinybool redundant_pic_cap);
+
+extern sdp_result_e sdp_attr_set_fmtp_max_mbps (void *sdp_ptr,
+                                                  u16 level,
+                                                  u8 cap_num,
+                                                  u16 inst_num,
+                                                  u32 max_mbps);
+
+extern sdp_result_e sdp_attr_set_fmtp_max_fs (void *sdp_ptr,
+                                             u16 level,
+                                             u8 cap_num,
+                                             u16 inst_num,
+                                             u32 max_fs);
+
+extern sdp_result_e sdp_attr_set_fmtp_max_cpb (void *sdp_ptr,
+                                             u16 level,
+                                             u8 cap_num,
+                                             u16 inst_num,
+                                             u32 max_cpb);
+
+extern sdp_result_e sdp_attr_set_fmtp_max_dpb (void *sdp_ptr,
+                                             u16 level,
+                                             u8 cap_num,
+                                             u16 inst_num,
+                                             u32 max_dpb);
+
+extern sdp_result_e sdp_attr_set_fmtp_max_br (void *sdp_ptr,
+                                             u16 level,
+                                             u8 cap_num,
+                                             u16 inst_num,
+                                             u32 max_br);
+
+extern sdp_result_e sdp_attr_set_fmtp_max_rcmd_nalu_size (void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                              u32 max_rcmd_nalu_size);
+
+extern sdp_result_e sdp_attr_set_fmtp_deint_buf_cap (void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                              u32 deint_buf_cap);
+
+extern sdp_result_e sdp_attr_set_fmtp_h264_parameter_add (void *sdp_ptr,
+                                                          u16 level,
+                                                          u8 cap_num,
+                                                          u16 inst_num,
+                                                          tinybool parameter_add);
+
+extern sdp_result_e sdp_attr_set_fmtp_h261_annex_params (void *sdp_ptr,
+                                                         u16 level,
+                                                         u8 cap_num,
+                                                         u16 inst_num,
+                                                         tinybool annex_d);
+
+extern sdp_result_e sdp_attr_set_fmtp_h263_annex_params (void *sdp_ptr,
+                                                         u16 level,
+                                                         u8 cap_num,
+                                                         u16 inst_num,
+                                                         tinybool annex_f,
+                                                         tinybool annex_i,
+                                                         tinybool annex_j,
+                                                         tinybool annex_t,
+                                                         u16 annex_k_val,
+                                                         u16 annex_n_val,
+                                               u16 annex_p_val_picture_resize,
+                                               u16 annex_p_val_warp);
+
+
+/* get routines */
+extern int32 sdp_attr_get_fmtp_bitrate_type (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num);
+
+extern int32 sdp_attr_get_fmtp_cif (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_qcif (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_sqcif (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_cif4 (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_cif16 (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_maxbr (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_get_fmtp_max_average_bitrate (void *sdp_ptr, u16 level,
+                                                           u8 cap_num, u16 inst_num, u32* val);
+extern sdp_result_e sdp_attr_get_fmtp_usedtx (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num, tinybool* val);
+extern sdp_result_e sdp_attr_get_fmtp_stereo (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num, tinybool* val);
+extern sdp_result_e sdp_attr_get_fmtp_useinbandfec (void *sdp_ptr, u16 level,
+                                                    u8 cap_num, u16 inst_num, tinybool* val);
+extern char* sdp_attr_get_fmtp_maxcodedaudiobandwidth (void *sdp_ptr, u16 level,
+                                                             u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_get_fmtp_cbr (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, tinybool* val);
+extern sdp_result_e sdp_attr_set_fmtp_streams (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, u32 streams);
+extern sdp_result_e sdp_attr_get_fmtp_streams (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, u32* val);
+extern sdp_result_e sdp_attr_get_fmtp_data_channel_protocol (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, char* protocol);
+extern sdp_result_e sdp_attr_set_fmtp_data_channel_protocol (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num,
+                             const char *protocol);
+extern int32 sdp_attr_get_fmtp_custom_x (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_custom_y (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_custom_mpi (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_par_width (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_par_height (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_bpp (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_hrd (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_profile (void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_level (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num);
+extern tinybool sdp_attr_get_fmtp_interlace (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num);
+extern tinybool sdp_attr_get_fmtp_annex_d (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num);
+extern tinybool sdp_attr_get_fmtp_annex_f (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num);
+extern tinybool sdp_attr_get_fmtp_annex_i (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num);
+extern tinybool sdp_attr_get_fmtp_annex_j (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num);
+extern tinybool sdp_attr_get_fmtp_annex_t (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_annex_k_val (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_annex_n_val (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num);
+
+extern int32 sdp_attr_get_fmtp_annex_p_picture_resize (void *sdp_ptr, u16 level,
+                                                       u8 cap_num, u16 inst_num);
+extern int32 sdp_attr_get_fmtp_annex_p_warp (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num);
+
+/* H.264 codec specific params */
+
+extern const char *sdp_attr_get_fmtp_profile_id(void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num);
+extern const char *sdp_attr_get_fmtp_param_sets(void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_attr_get_fmtp_pack_mode (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num, u16 *val);
+
+extern sdp_result_e sdp_attr_get_fmtp_level_asymmetry_allowed (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num, u16 *val);
+
+extern sdp_result_e sdp_attr_get_fmtp_interleaving_depth (void *sdp_ptr, u16 level,
+                                                   u8 cap_num, u16 inst_num,
+                                                   u16 *val);
+extern sdp_result_e sdp_attr_get_fmtp_max_don_diff (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             u32 *val);
+
+/* The following four H.264 parameters that require special handling as
+ * the values range from 0 - 4294967295
+ */
+extern sdp_result_e sdp_attr_get_fmtp_deint_buf_req (void *sdp_ptr, u16 level,
+                                                    u8 cap_num, u16 inst_num,
+                                                    u32 *val);
+extern sdp_result_e sdp_attr_get_fmtp_deint_buf_cap (void *sdp_ptr, u16 level,
+                                                    u8 cap_num, u16 inst_num,
+                                                    u32 *val);
+extern sdp_result_e sdp_attr_get_fmtp_init_buf_time (void *sdp_ptr, u16 level,
+                                                    u8 cap_num, u16 inst_num,
+                                                    u32 *val);
+extern sdp_result_e sdp_attr_get_fmtp_max_rcmd_nalu_size (void *sdp_ptr,
+                                                   u16 level, u8 cap_num,
+                                                   u16 inst_num, u32 *val);
+
+
+extern sdp_result_e sdp_attr_get_fmtp_max_mbps (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num, u32 *val);
+extern sdp_result_e sdp_attr_get_fmtp_max_fs (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num, u32 *val);
+extern sdp_result_e sdp_attr_get_fmtp_max_cpb (void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num, u32 *val);
+extern sdp_result_e sdp_attr_get_fmtp_max_dpb (void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num, u32 *val);
+extern sdp_result_e sdp_attr_get_fmtp_max_br (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num, u32 *val);
+extern tinybool sdp_attr_fmtp_is_redundant_pic_cap (void *sdp_ptr, u16 level,
+                                                    u8 cap_num,
+                                                    u16 inst_num);
+extern tinybool sdp_attr_fmtp_is_parameter_add (void *sdp_ptr, u16 level,
+                                                u8 cap_num,
+                                                u16 inst_num);
+extern tinybool sdp_attr_fmtp_is_annexa_set (void *sdp_ptr, u16 level,
+                                             u8 cap_num,
+                                             u16 inst_num);
+
+extern tinybool sdp_attr_fmtp_is_annexb_set (void *sdp_ptr, u16 level,
+                                             u8 cap_num,
+                                             u16 inst_num);
+
+extern sdp_fmtp_format_type_e  sdp_attr_fmtp_get_fmtp_format (void *sdp_ptr, u16 level, u8 cap_num,
+                                                              u16 inst_num);
+
+extern u16 sdp_attr_get_pccodec_num_payload_types(void *sdp_ptr, u16 level,
+                                                  u8 cap_num, u16 inst_num);
+extern u16 sdp_attr_get_pccodec_payload_type(void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num, u16 payload_num);
+extern sdp_result_e sdp_attr_add_pccodec_payload_type(void *sdp_ptr,
+                                               u16 level, u8 cap_num,
+                                               u16 inst_num, u16 payload_type);
+extern u16 sdp_attr_get_xcap_first_cap_num(void *sdp_ptr, u16 level,
+                                            u16 inst_num);
+extern sdp_media_e sdp_attr_get_xcap_media_type(void *sdp_ptr, u16 level,
+                                                u16 inst_num);
+extern sdp_transport_e sdp_attr_get_xcap_transport_type(void *sdp_ptr,
+                                         u16 level, u16 inst_num);
+extern u16 sdp_attr_get_xcap_num_payload_types(void *sdp_ptr, u16 level,
+                                               u16 inst_num);
+extern u16 sdp_attr_get_xcap_payload_type(void *sdp_ptr, u16 level,
+                                          u16 inst_num, u16 payload_num,
+                                          sdp_payload_ind_e *indicator);
+extern sdp_result_e sdp_attr_set_xcap_media_type(void *sdp_ptr, u16 level,
+                                          u16 inst_num, sdp_media_e media);
+extern sdp_result_e sdp_attr_set_xcap_transport_type(void *sdp_ptr, u16 level,
+                                      u16 inst_num, sdp_transport_e transport);
+extern sdp_result_e sdp_attr_add_xcap_payload_type(void *sdp_ptr, u16 level,
+                                      u16 inst_num, u16 payload_type,
+                                      sdp_payload_ind_e indicator);
+extern u16 sdp_attr_get_cdsc_first_cap_num(void *sdp_ptr, u16 level,
+                                            u16 inst_num);
+extern sdp_media_e sdp_attr_get_cdsc_media_type(void *sdp_ptr, u16 level,
+                                                u16 inst_num);
+extern sdp_transport_e sdp_attr_get_cdsc_transport_type(void *sdp_ptr,
+                                         u16 level, u16 inst_num);
+extern u16 sdp_attr_get_cdsc_num_payload_types(void *sdp_ptr, u16 level,
+                                               u16 inst_num);
+extern u16 sdp_attr_get_cdsc_payload_type(void *sdp_ptr, u16 level,
+                                          u16 inst_num, u16 payload_num,
+                                          sdp_payload_ind_e *indicator);
+extern sdp_result_e sdp_attr_set_cdsc_media_type(void *sdp_ptr, u16 level,
+                                          u16 inst_num, sdp_media_e media);
+extern sdp_result_e sdp_attr_set_cdsc_transport_type(void *sdp_ptr, u16 level,
+                                      u16 inst_num, sdp_transport_e transport);
+extern sdp_result_e sdp_attr_add_cdsc_payload_type(void *sdp_ptr, u16 level,
+                                      u16 inst_num, u16 payload_type,
+                                      sdp_payload_ind_e indicator);
+extern tinybool sdp_media_dynamic_payload_valid (void *sdp_ptr, u16 payload_type,
+                                                 u16 m_line);
+
+extern sdp_result_e sdp_attr_set_rtr_confirm (void *, u16 , \
+                                              u8 ,u16 ,tinybool );
+extern tinybool sdp_attr_get_rtr_confirm (void *, u16, u8, u16);
+
+extern tinybool sdp_attr_get_silencesupp_enabled(void *sdp_ptr, u16 level,
+                                                 u8 cap_num, u16 inst_num);
+extern u16 sdp_attr_get_silencesupp_timer(void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num,
+                                          tinybool *null_ind);
+extern sdp_silencesupp_pref_e sdp_attr_get_silencesupp_pref(void *sdp_ptr,
+                                                            u16 level,
+                                                            u8 cap_num,
+                                                            u16 inst_num);
+extern sdp_silencesupp_siduse_e sdp_attr_get_silencesupp_siduse(void *sdp_ptr,
+                                                                u16 level,
+                                                                u8 cap_num,
+                                                                u16 inst_num);
+extern u8 sdp_attr_get_silencesupp_fxnslevel(void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             tinybool *null_ind);
+extern sdp_result_e sdp_attr_set_silencesupp_enabled(void *sdp_ptr, u16 level,
+                                                     u8 cap_num, u16 inst_num,
+                                                     tinybool enable);
+extern sdp_result_e sdp_attr_set_silencesupp_timer(void *sdp_ptr, u16 level,
+                                                   u8 cap_num, u16 inst_num,
+                                                   u16 value,
+                                                   tinybool null_ind);
+extern sdp_result_e sdp_attr_set_silencesupp_pref(void *sdp_ptr, u16 level,
+                                                  u8 cap_num, u16 inst_num,
+                                                  sdp_silencesupp_pref_e pref);
+extern sdp_result_e sdp_attr_set_silencesupp_siduse(void *sdp_ptr, u16 level,
+                                                    u8 cap_num, u16 inst_num,
+                                                    sdp_silencesupp_siduse_e siduse);
+extern sdp_result_e sdp_attr_set_silencesupp_fxnslevel(void *sdp_ptr,
+                                                       u16 level,
+                                                       u8 cap_num,
+                                                       u16 inst_num,
+                                                       u16 value,
+                                                       tinybool null_ind);
+
+extern sdp_mediadir_role_e sdp_attr_get_comediadir_role(void *sdp_ptr,
+                                                        u16 level, u8 cap_num,
+                                                        u16 inst_num);
+extern sdp_result_e sdp_attr_set_comediadir_role(void *sdp_ptr, u16 level,
+                                                 u8 cap_num, u16 inst_num,
+                                                sdp_mediadir_role_e role);
+
+extern sdp_result_e sdp_delete_all_media_direction_attrs (void *sdp_ptr,
+                                                          u16 level);
+
+extern u16 sdp_attr_get_mptime_num_intervals(
+    void *sdp_ptr, u16 level, u8 cap_num, u16 inst_num);
+extern u16 sdp_attr_get_mptime_interval(
+    void *sdp_ptr, u16 level, u8 cap_num, u16 inst_num, u16 interval_num);
+extern sdp_result_e sdp_attr_add_mptime_interval(
+    void *sdp_ptr, u16 level, u8 cap_num, u16 inst_num, u16 interval);
+
+
+extern sdp_result_e sdp_delete_bw_line (void *sdp_ptr, u16 level, u16 inst_num);
+extern sdp_result_e sdp_copy_all_bw_lines(void *src_sdp_ptr, void *dst_sdp_ptr,
+                                          u16 src_level, u16 dst_level);
+extern sdp_bw_modifier_e sdp_get_bw_modifier(void *sdp_ptr, u16 level,
+                                             u16 inst_num);
+extern int32 sdp_get_bw_value(void *sdp_ptr, u16 level, u16 inst_num);
+extern int32 sdp_get_num_bw_lines (void *sdp_ptr, u16 level);
+
+extern sdp_result_e sdp_add_new_bw_line(void *sdp_ptr, u16 level,
+                                         sdp_bw_modifier_e bw_modifier, u16 *inst_num);
+extern sdp_result_e sdp_set_bw(void *sdp_ptr, u16 level, u16 inst_num,
+                               sdp_bw_modifier_e value, u32 bw_val);
+
+extern sdp_group_attr_e sdp_get_group_attr(void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_set_group_attr(void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       sdp_group_attr_e value);
+
+extern const char* sdp_attr_get_x_sidout (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num);
+
+extern sdp_result_e sdp_attr_set_x_sidout (void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num,
+                                   const char *sidout);
+
+extern const char* sdp_attr_get_x_sidin (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num);
+
+extern sdp_result_e sdp_attr_set_x_sidin (void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num,
+                                   const char *sidin);
+
+extern const char* sdp_attr_get_x_confid (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num);
+
+extern sdp_result_e sdp_attr_set_x_confid (void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num,
+                                   const char *confid);
+
+extern sdp_result_e sdp_attr_set_ice_candidate(void *sdp_ptr, u16 level,
+                              u8 cap_num, u16 inst_num, const char *ice_candidate);
+
+extern u16 sdp_get_group_num_id(void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_set_group_num_id(void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num,
+                                         u16 group_num_id);
+
+extern int32 sdp_get_group_id(void *sdp_ptr, u16 level,
+                              u8 cap_num, u16 inst_num, u16 id_num);
+extern sdp_result_e sdp_set_group_id (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num, u16 group_id);
+
+
+extern int32 sdp_get_mid_value(void *sdp_ptr, u16 level);
+extern sdp_result_e sdp_set_mid_value(void *sdp_ptr, u16 level, u32 mid_val);
+
+extern sdp_result_e sdp_set_source_filter(void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num,
+                                          sdp_src_filter_mode_e mode,
+                                          sdp_nettype_e nettype,
+                                          sdp_addrtype_e addrtype,
+                                          const char *dest_addr,
+                                          const char *src_addr);
+extern sdp_result_e sdp_include_new_filter_src_addr(void *sdp_ptr, u16 level,
+                                                    u8 cap_num, u16 inst_num,
+                                                    const char *src_addr);
+extern sdp_src_filter_mode_e sdp_get_source_filter_mode(void *sdp_ptr,
+                                                  u16 level, u8 cap_num,
+                                                  u16 inst_num);
+extern sdp_result_e sdp_get_filter_destination_attributes(void *sdp_ptr,
+                                                  u16 level, u8 cap_num,
+                                                  u16 inst_num,
+                                                  sdp_nettype_e *nettype,
+                                                  sdp_addrtype_e *addrtype,
+                                                  char *dest_addr);
+extern int32 sdp_get_filter_source_address_count(void *sdp_ptr, u16 level,
+                                                 u8 cap_num, u16 inst_num);
+extern sdp_result_e sdp_get_filter_source_address (void *sdp_ptr, u16 level,
+                                                   u8 cap_num, u16 inst_num,
+                                                   u16 src_addr_id,
+                                                   char *src_addr);
+
+extern sdp_result_e sdp_set_rtcp_unicast_mode(void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              sdp_rtcp_unicast_mode_e mode);
+extern sdp_rtcp_unicast_mode_e sdp_get_rtcp_unicast_mode(void *sdp_ptr,
+                                              u16 level, u8 cap_num,
+                                              u16 inst_num);
+
+void sdp_crypto_debug(char *buffer, ulong length_bytes);
+char * sdp_debug_msg_filter(char *buffer, ulong length_bytes);
+
+extern int32
+sdp_attr_get_sdescriptions_tag(void *sdp_ptr,
+                               u16 level,
+                               u8 cap_num,
+                              u16 inst_num);
+
+extern sdp_srtp_crypto_suite_t
+sdp_attr_get_sdescriptions_crypto_suite(void *sdp_ptr,
+                                        u16 level,
+                                        u8 cap_num,
+                                       u16 inst_num);
+
+extern const char*
+sdp_attr_get_sdescriptions_key(void *sdp_ptr,
+                               u16 level,
+                               u8 cap_num,
+                              u16 inst_num);
+
+extern const char*
+sdp_attr_get_sdescriptions_salt(void *sdp_ptr,
+                                u16 level,
+                                u8 cap_num,
+                               u16 inst_num);
+
+extern const char*
+sdp_attr_get_sdescriptions_lifetime(void *sdp_ptr,
+                                    u16 level,
+                                    u8 cap_num,
+                                   u16 inst_num);
+
+extern sdp_result_e
+sdp_attr_get_sdescriptions_mki(void *sdp_ptr,
+                               u16 level,
+                               u8 cap_num,
+                              u16 inst_num,
+                              const char **mki_value,
+                              u16 *mki_length);
+
+extern const char*
+sdp_attr_get_sdescriptions_session_params(void *sdp_ptr,
+                                          u16 level,
+                                          u8 cap_num,
+                                         u16 inst_num);
+
+extern unsigned char
+sdp_attr_get_sdescriptions_key_size(void *sdp_ptr,
+                                    u16 level,
+                                   u8 cap_num,
+                                   u16 inst_num);
+
+extern unsigned char
+sdp_attr_get_sdescriptions_salt_size(void *sdp_ptr,
+                                     u16 level,
+                                    u8 cap_num,
+                                    u16 inst_num);
+
+extern unsigned long
+sdp_attr_get_srtp_crypto_selection_flags(void *sdp_ptr,
+                                         u16 level,
+                                        u8 cap_num,
+                                        u16 inst_num);
+
+extern sdp_result_e
+sdp_attr_set_sdescriptions_tag(void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num,
+                               int32 tag_num);
+
+extern sdp_result_e
+sdp_attr_set_sdescriptions_crypto_suite(void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num,
+                                        sdp_srtp_crypto_suite_t crypto_suite);
+
+extern sdp_result_e
+sdp_attr_set_sdescriptions_key(void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num,
+                               char *key);
+
+extern sdp_result_e
+sdp_attr_set_sdescriptions_salt(void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num,
+                                char *salt);
+
+extern sdp_result_e
+sdp_attr_set_sdescriptions_lifetime(void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num,
+                                    char *lifetime);
+
+extern sdp_result_e
+sdp_attr_set_sdescriptions_mki(void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num,
+                               char *mki_value,
+                              u16 mki_length);
+
+extern sdp_result_e
+sdp_attr_set_sdescriptions_key_size(void *sdp_ptr,
+                                     u16 level,
+                                    u8 cap_num,
+                                    u16 inst_num,
+                                    unsigned char key_size);
+
+extern sdp_result_e
+sdp_attr_set_sdescriptions_salt_size(void *sdp_ptr,
+                                     u16 level,
+                                    u8 cap_num,
+                                    u16 inst_num,
+                                    unsigned char salt_size);
+
+sdp_result_e
+sdp_attr_get_ice_attribute (void *sdp_ptr, u16 level,
+                           u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num,
+                           char **out);
+sdp_result_e
+sdp_attr_set_ice_attribute(void *sdp_ptr, u16 level,
+                           u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num, const char *ice_attrib);
+
+sdp_result_e
+sdp_attr_get_rtcp_mux_attribute (void *sdp_ptr, u16 level,
+                                  u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num,
+                                  tinybool *rtcp_mux);
+
+sdp_result_e
+sdp_attr_set_rtcp_mux_attribute(void *sdp_ptr, u16 level,
+                              u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num, const tinybool rtcp_mux);
+
+sdp_result_e
+sdp_attr_get_dtls_fingerprint_attribute (void *sdp_ptr, u16 level,
+                                  u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num,
+                                  char **out);
+
+sdp_result_e
+sdp_attr_set_dtls_fingerprint_attribute(void *sdp_ptr, u16 level,
+                              u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num, const char *dtls_fingerprint);
+
+#endif /* _SDP_H_ */
diff --git a/libs/sipcc/core/sdp/sdp_access.c b/libs/sipcc/core/sdp/sdp_access.c
new file mode 100644 (file)
index 0000000..dac9069
--- /dev/null
@@ -0,0 +1,2847 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp_os_defs.h"
+#include "sdp.h"
+#include "sdp_private.h"
+#include "ccsip_sdp.h"
+#include "rtp_defs.h"
+#include "CSFLog.h"
+
+static const char* logTag = "sdp_access";
+
+/* Function:    sdp_find_media_level
+ * Description: Find and return a pointer to the specified media level,
+ *              if it exists.
+ *              Note: This is not an API for the application but an internal
+ *              routine used by the SDP library.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to find.
+ * Returns:     Pointer to the media level or NULL if not found.
+ */
+sdp_mca_t *sdp_find_media_level (sdp_t *sdp_p, u16 level)
+{
+    int i;
+    sdp_mca_t *mca_p = NULL;
+
+    if ((level >= 1) && (level <= sdp_p->mca_count)) {
+        for (i=1, mca_p = sdp_p->mca_p;
+             ((i < level) && (mca_p != NULL));
+             mca_p = mca_p->next_p, i++) {
+
+             /*sa_ignore EMPTYLOOP*/
+             ; /* Do nothing. */
+        }
+    }
+
+    return (mca_p);
+}
+
+/* Function:    sdp_version_valid
+ * Description: Returns true or false depending on whether the version
+ *              set for this SDP is valid.  Currently the only valid
+ *              version is 0.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_version_valid (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (sdp_p->version == SDP_INVALID_VALUE) {
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/* Function:    sdp_get_version
+ * Description: Returns the version value set for the given SDP.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Version value.
+ */
+int32 sdp_get_version (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    return (sdp_p->version);
+}
+
+/* Function:    sdp_set_version
+ * Description: Sets the value of the version parameter for the v= version
+ *              token line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              version     Version to set.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_version (void *sdp_ptr, int32 version)
+{
+    sdp_t *sdp_p = (sdp_t*)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    sdp_p->version = version;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_owner_valid
+ * Description: Returns true or false depending on whether the owner
+ *              token line has been defined for this SDP.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_owner_valid (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if ((sdp_p->owner_name[0] == '\0') ||
+        (sdp_p->owner_network_type == SDP_NT_INVALID) ||
+        (sdp_p->owner_addr_type == SDP_AT_INVALID) ||
+        (sdp_p->owner_addr[0] == '\0')) {
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/* Function:    sdp_get_owner_username
+ * Description: Returns a pointer to the value of the username parameter
+ *              from the o= owner token line.  Value is returned as a
+ *              const ptr and so cannot be modified by the application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Version value.
+ */
+const char *sdp_get_owner_username (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    return (sdp_p->owner_name);
+}
+
+/* Function:    sdp_get_owner_sessionid
+ * Description: Returns the session id parameter from the o= owner token
+ *              line.  Because the value may be larger than 32 bits, this
+ *              parameter is returned as a string, though has been verified
+ *              to be numeric.  Value is returned as a const ptr and so
+ *              cannot be modified by the application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Ptr to owner session id or NULL.
+ */
+const char *sdp_get_owner_sessionid (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    return (sdp_p->owner_sessid);
+}
+
+/* Function:    sdp_get_owner_version
+ * Description: Returns the version parameter from the o= owner token
+ *              line.  Because the value may be larger than 32 bits, this
+ *              parameter is returned as a string, though has been verified
+ *              to be numeric.  Value is returned as a const ptr and so
+ *              cannot be modified by the application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Ptr to owner version or NULL.
+ */
+const char *sdp_get_owner_version (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    return (sdp_p->owner_version);
+}
+
+/* Function:    sdp_get_owner_network_type
+ * Description: Returns the network type parameter from the o= owner token
+ *              line.  If network type has not been set SDP_NT_INVALID will
+ *              be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Network type or SDP_NT_INVALID.
+ */
+sdp_nettype_e sdp_get_owner_network_type (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_NT_INVALID);
+    }
+
+    return (sdp_p->owner_network_type);
+}
+
+/* Function:    sdp_get_owner_address_type
+ * Description: Returns the address type parameter from the o= owner token
+ *              line.  If address type has not been set SDP_AT_INVALID will
+ *              be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Address type or SDP_AT_INVALID.
+ */
+sdp_addrtype_e sdp_get_owner_address_type (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_AT_INVALID);
+    }
+
+    return (sdp_p->owner_addr_type);
+}
+
+/* Function:    sdp_get_owner_address
+ * Description: Returns the address parameter from the o= owner token
+ *              line.  Value is returned as a const ptr and so
+ *              cannot be modified by the application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Ptr to address or NULL.
+ */
+const char *sdp_get_owner_address (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    return (sdp_p->owner_addr);
+}
+
+/* Function:    sdp_set_owner_username
+ * Description: Sets the value of the username parameter for the o= owner
+ *              token line.  The string is copied into the SDP structure
+ *              so application memory will not be referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              username    Ptr to the username string.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_owner_username (void *sdp_ptr, const char *username)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    sstrncpy(sdp_p->owner_name, username, sizeof(sdp_p->owner_name));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_owner_username
+ * Description: Sets the value of the session id parameter for the o= owner
+ *              token line.  The string is copied into the SDP structure
+ *              so application memory will not be referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              sessionid   Ptr to the sessionid string.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_owner_sessionid (void *sdp_ptr, const char *sessionid)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    sstrncpy(sdp_p->owner_sessid, sessionid, sizeof(sdp_p->owner_sessid));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_owner_version
+ * Description: Sets the value of the version parameter for the o= owner
+ *              token line.  The string is copied into the SDP structure
+ *              so application memory will not be referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              version     Ptr to the version string.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_owner_version (void *sdp_ptr, const char *version)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    sstrncpy(sdp_p->owner_version, version, sizeof(sdp_p->owner_version));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_owner_network_type
+ * Description: Sets the value of the network type parameter for the o= owner
+ *              token line.
+ * Parameters:  sdp_ptr       The SDP handle returned by sdp_init_description.
+ *              network_type  Network type for the owner line.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_owner_network_type (void *sdp_ptr,
+                                         sdp_nettype_e network_type)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    sdp_p->owner_network_type = network_type;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_owner_address_type
+ * Description: Sets the value of the address type parameter for the o= owner
+ *              token line.
+ * Parameters:  sdp_ptr       The SDP handle returned by sdp_init_description.
+ *              address_type  Address type for the owner line.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_owner_address_type (void *sdp_ptr,
+                                         sdp_addrtype_e address_type)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    sdp_p->owner_addr_type = address_type;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_owner_address
+ * Description: Sets the value of the address parameter for the o= owner
+ *              token line.  The string is copied into the SDP structure
+ *              so application memory will not be referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              version     Ptr to the version string.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_owner_address (void *sdp_ptr, const char *address)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    sstrncpy(sdp_p->owner_addr, address, sizeof(sdp_p->owner_addr));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_session_name_valid
+ * Description: Returns true or false depending on whether the session name
+ *              s= token line has been defined for this SDP.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_session_name_valid (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (sdp_p->sessname[0] == '\0') {
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/* Function:    sdp_get_session_name
+ * Description: Returns the session name parameter from the s= session
+ *              name token line.  Value is returned as a const ptr and so
+ *              cannot be modified by the application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Ptr to session name or NULL.
+ */
+const char *sdp_get_session_name (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    return (sdp_p->sessname);
+}
+
+/* Function:    sdp_set_session_name
+ * Description: Sets the value of the session name parameter for the s=
+ *              session name token line.  The string is copied into the
+ *              SDP structure so application memory will not be
+ *              referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              sessname    Ptr to the session name string.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_session_name (void *sdp_ptr, const char *sessname)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    sstrncpy(sdp_p->sessname, sessname, sizeof(sdp_p->sessname));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_timespec_valid
+ * Description: Returns true or false depending on whether the timespec t=
+ *              token line has been defined for this SDP.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_timespec_valid (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if ((sdp_p->timespec_p == NULL) ||
+        (sdp_p->timespec_p->start_time == '\0') ||
+        (sdp_p->timespec_p->stop_time == '\0')) {
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/* Function:    sdp_get_time_start
+ * Description: Returns the start time parameter from the t= timespec token
+ *              line.  Because the value may be larger than 32 bits, this
+ *              parameter is returned as a string, though has been verified
+ *              to be numeric.  Value is returned as a const ptr and so
+ *              cannot be modified by the application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Ptr to start time or NULL.
+ */
+const char *sdp_get_time_start (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    if (sdp_p->timespec_p != NULL) {
+        return (sdp_p->timespec_p->start_time);
+    } else {
+        return (NULL);
+    }
+}
+
+/* Function:    sdp_get_time_stop
+ * Description: Returns the stop time parameter from the t= timespec token
+ *              line.  Because the value may be larger than 32 bits, this
+ *              parameter is returned as a string, though has been verified
+ *              to be numeric.  Value is returned as a const ptr and so
+ *              cannot be modified by the application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Ptr to stop time or NULL.
+ */
+const char *sdp_get_time_stop (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    if (sdp_p->timespec_p != NULL) {
+        return (sdp_p->timespec_p->stop_time);
+    } else {
+        return (NULL);
+    }
+}
+
+/* Function:    sdp_set_time_start
+ * Description: Sets the value of the start time parameter for the t=
+ *              timespec token line.  The string is copied into the
+ *              SDP structure so application memory will not be
+ *              referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              start_time  Ptr to the start time string.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_time_start (void *sdp_ptr, const char *start_time)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_p->timespec_p == NULL) {
+        sdp_p->timespec_p = (sdp_timespec_t *)SDP_MALLOC(sizeof(sdp_timespec_t));
+        if (sdp_p->timespec_p == NULL) {
+            sdp_p->conf_p->num_no_resource++;
+            return (SDP_NO_RESOURCE);
+        }
+        sdp_p->timespec_p->start_time[0] = '\0';
+        sdp_p->timespec_p->stop_time[0] = '\0';
+    }
+    sstrncpy(sdp_p->timespec_p->start_time, start_time,
+             sizeof(sdp_p->timespec_p->start_time));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_time_stop
+ * Description: Sets the value of the stop time parameter for the t=
+ *              timespec token line.  The string is copied into the
+ *              SDP structure so application memory will not be
+ *              referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              stop_time  Ptr to the stop time string.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_time_stop (void *sdp_ptr, const char *stop_time)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_p->timespec_p == NULL) {
+        sdp_p->timespec_p = (sdp_timespec_t *)SDP_MALLOC(sizeof(sdp_timespec_t));
+        if (sdp_p->timespec_p == NULL) {
+            sdp_p->conf_p->num_no_resource++;
+            return (SDP_NO_RESOURCE);
+        }
+        sdp_p->timespec_p->start_time[0] = '\0';
+        sdp_p->timespec_p->stop_time[0] = '\0';
+    }
+    sstrncpy(sdp_p->timespec_p->stop_time, stop_time,
+             sizeof(sdp_p->timespec_p->stop_time));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_encryption_valid
+ * Description: Returns true or false depending on whether the encryption k=
+ *              token line has been defined for this SDP at the given level.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the k= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_encryption_valid (void *sdp_ptr, u16 level)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_encryptspec_t   *encrypt_p;
+    sdp_mca_t           *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        encrypt_p = &(sdp_p->encrypt);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (FALSE);
+        }
+        encrypt_p = &(mca_p->encrypt);
+    }
+
+    if ((encrypt_p->encrypt_type == SDP_ENCRYPT_INVALID) ||
+        ((encrypt_p->encrypt_type != SDP_ENCRYPT_PROMPT) &&
+         (encrypt_p->encrypt_key[0] == '\0'))) {
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/* Function:    sdp_get_encryption_method
+ * Description: Returns the encryption method parameter from the k=
+ *              encryption token line.  If encryption method has not been
+ *              set SDP_ENCRYPT_INVALID will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     Encryption method or SDP_ENCRYPT_INVALID.
+ */
+sdp_encrypt_type_e sdp_get_encryption_method (void *sdp_ptr, u16 level)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_encryptspec_t   *encrypt_p;
+    sdp_mca_t           *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_ENCRYPT_INVALID);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        encrypt_p = &(sdp_p->encrypt);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_ENCRYPT_INVALID);
+        }
+        encrypt_p = &(mca_p->encrypt);
+    }
+
+    return (encrypt_p->encrypt_type);
+}
+
+/* Function:    sdp_get_encryption_key
+ * Description: Returns a pointer to the encryption key parameter
+ *              from the k= encryption token line.  Value is returned as a
+ *              const ptr and so cannot be modified by the application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     Ptr to encryption key or NULL.
+ */
+const char *sdp_get_encryption_key (void *sdp_ptr, u16 level)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_encryptspec_t   *encrypt_p;
+    sdp_mca_t           *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        encrypt_p = &(sdp_p->encrypt);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (NULL);
+        }
+        encrypt_p = &(mca_p->encrypt);
+    }
+
+    return (encrypt_p->encrypt_key);
+}
+
+/* Function:    sdp_set_encryption_method
+ * Description: Sets the value of the encryption method param for the k=
+ *              encryption token line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the k= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ *              type        The encryption type.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_encryption_method (void *sdp_ptr, u16 level,
+                                        sdp_encrypt_type_e type)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_encryptspec_t   *encrypt_p;
+    sdp_mca_t           *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        encrypt_p = &(sdp_p->encrypt);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        encrypt_p = &(mca_p->encrypt);
+    }
+
+    encrypt_p->encrypt_type = type;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_encryption_key
+ * Description: Sets the value of the encryption key parameter for the k=
+ *              encryption token line.  The string is copied into the
+ *              SDP structure so application memory will not be
+ *              referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the k= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ *              key         Ptr to the encryption key string.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_encryption_key (void *sdp_ptr, u16 level, const char *key)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_encryptspec_t   *encrypt_p;
+    sdp_mca_t           *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        encrypt_p = &(sdp_p->encrypt);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        encrypt_p = &(mca_p->encrypt);
+    }
+
+    sstrncpy(encrypt_p->encrypt_key, key, sizeof(encrypt_p->encrypt_key));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_connection_valid
+ * Description: Returns true or false depending on whether the connection c=
+ *              token line has been defined for this SDP at the given level.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_connection_valid (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (FALSE);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    /*if network type is ATM . then allow c= line without address type
+     * and address . This is a special case to cover PVC
+     */
+    if (conn_p->nettype == SDP_NT_ATM &&
+        conn_p->addrtype == SDP_AT_INVALID) {
+        return TRUE;
+    }
+
+    if ((conn_p->nettype >= SDP_MAX_NETWORK_TYPES) ||
+        (conn_p->addrtype >= SDP_MAX_ADDR_TYPES) ||
+        (conn_p->conn_addr[0] == '\0')) {
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/* Function:    sdp_bandwidth_valid
+ * Description: Returns true or false depending on whether the bandwidth b=
+ *              token line has been defined for this SDP at the given level.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ *              inst_num    instance number of bw line at that level. The first
+ *                          instance has a inst_num of 1 and so on.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_bandwidth_valid (void *sdp_ptr, u16 level, u16 inst_num)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_bw_data_t *bw_data_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return FALSE;
+    }
+
+    bw_data_p = sdp_find_bw_line(sdp_p, level, inst_num);
+    if (bw_data_p != NULL) {
+        if ((bw_data_p->bw_modifier < SDP_BW_MODIFIER_AS) ||
+            (bw_data_p->bw_modifier >= SDP_MAX_BW_MODIFIER_VAL)) {
+            return FALSE;
+        } else {
+            return TRUE;
+        }
+    } else {
+        return FALSE;
+    }
+}
+
+/*
+ * sdp_bw_line_exists
+ *
+ * Description: This api retruns true if there exists a bw line at the
+ *              instance and level specified.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ *              inst_num    instance number of bw line at that level. The first
+ *                          instance has a inst_num of 1 and so on.
+ * Returns:     TRUE or FALSE
+ */
+tinybool sdp_bw_line_exists (void *sdp_ptr, u16 level, u16 inst_num)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_bw_data_t *bw_data_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return FALSE;
+    }
+
+    bw_data_p = sdp_find_bw_line(sdp_p, level, inst_num);
+    if (bw_data_p != NULL) {
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
+/* Function:    sdp_get_conn_nettype
+ * Description: Returns the network type parameter from the c=
+ *              connection token line.  If network type has not been
+ *              set SDP_NT_INVALID will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     Network type or SDP_NT_INVALID.
+ */
+sdp_nettype_e sdp_get_conn_nettype (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_NT_INVALID);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_NT_INVALID);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    return (conn_p->nettype);
+}
+
+/* Function:    sdp_get_conn_addrtype
+ * Description: Returns the address type parameter from the c=
+ *              connection token line.  If address type has not been
+ *              set SDP_AT_INVALID will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     Address type or SDP_AT_INVALID.
+ */
+sdp_addrtype_e sdp_get_conn_addrtype (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_AT_INVALID);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_AT_INVALID);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    return (conn_p->addrtype);
+}
+
+/* Function:    sdp_get_conn_address
+ * Description: Returns a pointer to the address parameter
+ *              from the c= connection token line.  Value is returned as a
+ *              const ptr and so cannot be modified by the application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     Ptr to address or NULL.
+ */
+const char *sdp_get_conn_address (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (NULL);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    return (conn_p->conn_addr);
+}
+
+/* Function:    sdp_is_mcast_addr
+ * Description: Returns a boolean to indicate if the addr is multicast in
+ *              the c=line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     TRUE if the addr is multicast, FALSE if not.
+ */
+
+tinybool sdp_is_mcast_addr (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p != NULL) {
+            conn_p = &(mca_p->conn);
+       } else {
+            return (FALSE);
+       }
+    }
+
+    if ((conn_p) && (conn_p->is_multicast)) {
+        return (TRUE);
+    } else {
+        return (FALSE);
+    }
+}
+
+/* Function:    sdp_get_mcast_ttl
+ * Description: Get the time to live(ttl) value for the multicast address
+ *              if present.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     Multicast address - Time to live (ttl) value
+ */
+
+int32 sdp_get_mcast_ttl (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+    u16 ttl=0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) != FALSE) {
+        if (level == SDP_SESSION_LEVEL) {
+            conn_p = &(sdp_p->default_conn);
+        } else {
+            mca_p = sdp_find_media_level(sdp_p, level);
+            if (mca_p != NULL) {
+                conn_p = &(mca_p->conn);
+            } else {
+                return SDP_INVALID_VALUE;
+            }
+        }
+    } else {
+        return SDP_INVALID_VALUE;
+    }
+
+    if (conn_p) {
+       ttl = conn_p->ttl;
+    }
+    return ttl;
+}
+
+/* Function:    sdp_get_mcast_num_of_addresses
+ * Description: Get the number of addresses value for the multicast address
+ *              if present.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     Multicast address - number of addresses value
+ */
+
+int32 sdp_get_mcast_num_of_addresses (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+    u16 num_addr = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    } else {
+        if (level == SDP_SESSION_LEVEL) {
+            conn_p = &(sdp_p->default_conn);
+        } else {
+            mca_p = sdp_find_media_level(sdp_p, level);
+            if (mca_p != NULL) {
+                conn_p = &(mca_p->conn);
+            } else {
+                return (SDP_INVALID_VALUE);
+           }
+        }
+    }
+
+    if (conn_p) {
+       num_addr = conn_p->num_of_addresses;
+    }
+    return num_addr;
+}
+/* Function:    sdp_set_conn_nettype
+ * Description: Sets the value of the network type parameter for the c=
+ *              connection token line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              nettype     Network type for the connection line.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_conn_nettype (void *sdp_ptr, u16 level,
+                                   sdp_nettype_e nettype)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    conn_p->nettype = nettype;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_conn_addrtype
+ * Description: Sets the value of the address type parameter for the c=
+ *              connection token line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              addrtype    Address type for the connection line.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_conn_addrtype (void *sdp_ptr, u16 level,
+                                    sdp_addrtype_e addrtype)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    conn_p->addrtype = addrtype;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_conn_address
+ * Description: Sets the value of the address parameter for the c=
+ *              connection token line.  The string is copied into the
+ *              SDP structure so application memory will not be
+ *              referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ *              address     Ptr to the address string.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_conn_address (void *sdp_ptr, u16 level,
+                                   const char *address)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    sstrncpy(conn_p->conn_addr, address, sizeof(conn_p->conn_addr));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_mcast_addr_fields
+ * Description: Sets the value of the ttl and num of addresses for
+ *              a multicast address.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          either SDP_SESSION_LEVEL or 1-n specifying a
+ *                          media line level.
+ *              ttl         Time to live (ttl) value.
+ *              num_of_addresses number of addresses
+ .
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_set_mcast_addr_fields(void *sdp_ptr, u16 level,
+                                      u16 ttl, u16 num_of_addresses)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_conn_t *conn_p;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    if (conn_p) {
+       conn_p->is_multicast = TRUE;
+       if ((conn_p->ttl >0) && (conn_p->ttl <= SDP_MAX_TTL_VALUE)) {
+          conn_p->ttl = ttl;
+       }
+       conn_p->num_of_addresses = num_of_addresses;
+    } else {
+       return (SDP_FAILURE);
+    }
+    return (SDP_SUCCESS);
+}
+
+
+/* Function:    sdp_media_line_valid
+ * Description: Returns true or false depending on whether the specified
+ *              media line m= has been defined for this SDP.  The
+ *              SDP_SESSION_LEVEL level is not valid for this check since,
+ *              by definition, this is a media level.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the c= line.  Will be
+ *                          1-n specifying a media line level.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_media_line_valid (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (FALSE);
+    }
+
+    /* Validate params for this media line */
+    if ((mca_p->media >= SDP_MAX_MEDIA_TYPES) ||
+        (mca_p->port_format >= SDP_MAX_PORT_FORMAT_TYPES) ||
+        (mca_p->transport >= SDP_MAX_TRANSPORT_TYPES) ||
+        (mca_p->num_payloads == 0)) {
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/* Function:    sdp_get_num_media_lines
+ * Description: Returns the number of media lines associated with the SDP.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ * Returns:     Number of media lines.
+ */
+u16 sdp_get_num_media_lines (void *sdp_ptr)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    return (sdp_p->mca_count);
+}
+
+/* Function:    sdp_get_media_type
+ * Description: Returns the media type parameter from the m=
+ *              media token line.  If media type has not been
+ *              set SDP_MEDIA_INVALID will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     Media type or SDP_MEDIA_INVALID.
+ */
+sdp_media_e sdp_get_media_type (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_MEDIA_INVALID);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_MEDIA_INVALID);
+    }
+
+    return (mca_p->media);
+}
+
+/* Function:    sdp_get_media_port_format
+ * Description: Returns the port format type associated with the m=
+ *              media token line.  If port format type has not been
+ *              set SDP_PORT_FORMAT_INVALID will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     Port format type or SDP_PORT_FORMAT_INVALID.
+ */
+sdp_port_format_e sdp_get_media_port_format (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_PORT_FORMAT_INVALID);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_PORT_FORMAT_INVALID);
+    }
+
+    return (mca_p->port_format);
+}
+
+/* Function:    sdp_get_media_portnum
+ * Description: Returns the port number associated with the m=
+ *              media token line.  If port number has not been
+ *              set SDP_INVALID_VALUE will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     Port number or SDP_INVALID_VALUE.
+ */
+int32 sdp_get_media_portnum (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    /* Make sure port number is valid for the specified format. */
+    if ((mca_p->port_format != SDP_PORT_NUM_ONLY) &&
+        (mca_p->port_format != SDP_PORT_NUM_COUNT) &&
+        (mca_p->port_format != SDP_PORT_NUM_VPI_VCI) &&
+        (mca_p->port_format != SDP_PORT_NUM_VPI_VCI_CID)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Port num not valid for media line %u",
+                      sdp_p->debug_str, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    }
+
+    return (mca_p->port);
+}
+
+/* Function:    sdp_get_media_portcount
+ * Description: Returns the port count associated with the m=
+ *              media token line.  If port count has not been
+ *              set SDP_INVALID_VALUE will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     Port count or SDP_INVALID_VALUE.
+ */
+int32 sdp_get_media_portcount (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    /* Make sure port number is valid for the specified format. */
+    if (mca_p->port_format != SDP_PORT_NUM_COUNT) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Port count not valid for media line %u",
+                      sdp_p->debug_str, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    }
+
+    return (mca_p->num_ports);
+}
+
+/* Function:    sdp_get_media_vpi
+ * Description: Returns the VPI parameter associated with the m=
+ *              media token line.  If VPI has not been set
+ *              SDP_INVALID_VALUE will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     VPI or SDP_INVALID_VALUE.
+ */
+int32 sdp_get_media_vpi (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    /* Make sure port number is valid for the specified format. */
+    if ((mca_p->port_format != SDP_PORT_VPI_VCI) &&
+        (mca_p->port_format != SDP_PORT_NUM_VPI_VCI) &&
+        (mca_p->port_format != SDP_PORT_NUM_VPI_VCI_CID)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s VPI not valid for media line %u",
+                      sdp_p->debug_str, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    }
+
+    return (mca_p->vpi);
+}
+
+/* Function:    sdp_get_media_vci
+ * Description: Returns the VCI parameter associated with the m=
+ *              media token line.  If VCI has not been set
+ *              SDP_INVALID_VALUE will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     VCI or zero.
+ */
+u32 sdp_get_media_vci (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (0);
+    }
+
+    /* Make sure port number is valid for the specified format. */
+    if ((mca_p->port_format != SDP_PORT_VPI_VCI) &&
+        (mca_p->port_format != SDP_PORT_NUM_VPI_VCI) &&
+        (mca_p->port_format != SDP_PORT_NUM_VPI_VCI_CID)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s VCI not valid for media line %u",
+                      sdp_p->debug_str, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    }
+
+    return (mca_p->vci);
+}
+
+/* Function:    sdp_get_media_vcci
+ * Description: Returns the VCCI parameter associated with the m=
+ *              media token line.  If VCCI has not been set
+ *              SDP_INVALID_VALUE will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     VCCI or SDP_INVALID_VALUE.
+ */
+int32 sdp_get_media_vcci (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    /* Make sure port number is valid for the specified format. */
+    if ((mca_p->port_format != SDP_PORT_VCCI) &&
+        (mca_p->port_format != SDP_PORT_VCCI_CID)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s VCCI not valid for media line %u",
+                      sdp_p->debug_str, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    }
+
+    return (mca_p->vcci);
+}
+
+/* Function:    sdp_get_media_cid
+ * Description: Returns the CID parameter associated with the m=
+ *              media token line.  If CID has not been set
+ *              SDP_INVALID_VALUE will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     CID or SDP_INVALID_VALUE.
+ */
+int32 sdp_get_media_cid (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    /* Make sure port number is valid for the specified format. */
+    if ((mca_p->port_format != SDP_PORT_VCCI_CID) &&
+        (mca_p->port_format != SDP_PORT_NUM_VPI_VCI_CID)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s CID not valid for media line %u",
+                      sdp_p->debug_str, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    }
+
+    return (mca_p->cid);
+}
+
+/* Function:    sdp_get_media_transport
+ * Description: Returns the transport type parameter associated with the m=
+ *              media token line.  If transport type has not been set
+ *              SDP_TRANSPORT_INVALID will be returned.  If the transport
+ *              type is one of the AAL2 variants, the profile routines below
+ *              should be used to access multiple profile types and payload
+ *              lists per m= line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     CID or SDP_TRANSPORT_INVALID.
+ */
+sdp_transport_e sdp_get_media_transport (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_TRANSPORT_INVALID);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_TRANSPORT_INVALID);
+    }
+
+    return (mca_p->transport);
+}
+
+/* Function:    sdp_get_media_num_profiles
+ * Description: Returns the number of profiles associated with the m=
+ *              media token line.  If the media line is invalid, zero will
+ *              be returned.  Application must validate the media line
+ *              before using this routine.  Multiple profile types per
+ *              media line is currently only used for AAL2.  If the appl
+ *              detects that the transport type is one of the AAL2 types,
+ *              it should use these profile access routines to access the
+ *              profile types and payload list for each.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     Number of profiles or zero.
+ */
+u16 sdp_get_media_num_profiles (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (0);
+    }
+
+    if (mca_p->media_profiles_p == NULL) {
+        return (0);
+    } else {
+        return (mca_p->media_profiles_p->num_profiles);
+    }
+}
+
+/* Function:    sdp_get_media_profile
+ * Description: Returns the specified profile type associated with the m=
+ *              media token line.  If the media line or profile number is
+ *              invalid, SDP_TRANSPORT_INVALID will be returned.
+ *              Applications must validate the media line before using this
+ *              routine.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ *              profile_num The specific profile type number to be retrieved.
+ * Returns:     The profile type or SDP_TRANSPORT_INVALID.
+ */
+sdp_transport_e sdp_get_media_profile (void *sdp_ptr, u16 level,
+                                       u16 profile_num)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_TRANSPORT_INVALID);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_TRANSPORT_INVALID);
+    }
+
+    if ((profile_num < 1) ||
+        (profile_num > mca_p->media_profiles_p->num_profiles)) {
+        return (SDP_TRANSPORT_INVALID);
+    } else {
+        return (mca_p->media_profiles_p->profile[profile_num-1]);
+    }
+}
+
+/* Function:    sdp_get_media_num_payload_types
+ * Description: Returns the number of payload types associated with the m=
+ *              media token line.  If the media line is invalid, zero will
+ *              be returned.  Application must validate the media line
+ *              before using this routine.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ * Returns:     Number of payload types or zero.
+ */
+u16 sdp_get_media_num_payload_types (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (0);
+    }
+
+    return (mca_p->num_payloads);
+}
+
+/* Function:    sdp_get_media_profile_num_payload_types
+ * Description: Returns the number of payload types associated with the
+ *              specified profile on the m= media token line.  If the
+ *              media line or profile number is invalid, zero will
+ *              be returned.  Application must validate the media line
+ *              and profile before using this routine.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ *              profile_num The specific profile number. Will be 1-n.
+ * Returns:     Number of payload types or zero.
+ */
+u16 sdp_get_media_profile_num_payload_types (void *sdp_ptr, u16 level,
+                                             u16 profile_num)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (0);
+    }
+
+    if ((profile_num < 1) ||
+        (profile_num > mca_p->media_profiles_p->num_profiles)) {
+        return (0);
+    } else {
+        return (mca_p->media_profiles_p->num_payloads[profile_num-1]);
+    }
+}
+
+/* Function:    sdp_get_media_payload_type
+ * Description: Returns the payload type of the specified payload for the m=
+ *              media token line.  If the media line or payload number is
+ *              invalid, zero will be returned.  Application must validate
+ *              the media line before using this routine.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ *              payload_num Number of the payload type to retrieve.  The
+ *                          range is (1 - max num payloads).
+ *              indicator   Returns the type of payload returned, either
+ *                          NUMERIC or ENUM.
+ * Returns:     Payload type or zero.
+ */
+u32 sdp_get_media_payload_type (void *sdp_ptr, u16 level, u16 payload_num,
+                                sdp_payload_ind_e *indicator)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+    uint16_t    num_a_lines = 0;
+    int         i;
+    uint16_t    ptype;
+    uint16_t    pack_mode = 0; /*default 0, if remote did not provide any */
+    const char *encname = NULL;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (0);
+    }
+
+    if ((payload_num < 1) || (payload_num > mca_p->num_payloads)) {
+        return (0);
+    }
+
+    *indicator = mca_p->payload_indicator[payload_num-1];
+    if ((mca_p->payload_type[payload_num-1] >= SDP_MIN_DYNAMIC_PAYLOAD) &&
+        (mca_p->payload_type[payload_num-1] <= SDP_MAX_DYNAMIC_PAYLOAD)) {
+        /*
+         * Get number of RTPMAP attributes for the AUDIO line
+         */
+        (void) sdp_attr_num_instances(sdp_p, level, 0, SDP_ATTR_RTPMAP,
+                                      &num_a_lines);
+        /*
+         * Loop through AUDIO media line RTPMAP attributes.
+         * NET dynamic payload type will be returned.
+         */
+        for (i = 0; i < num_a_lines; i++) {
+            ptype = sdp_attr_get_rtpmap_payload_type(sdp_p, level, 0,
+                                                     (uint16_t) (i + 1));
+            if (ptype == mca_p->payload_type[payload_num-1] ) {
+                encname = sdp_attr_get_rtpmap_encname(sdp_p, level, 0,
+                                                  (uint16_t) (i + 1));
+                if (encname) {
+                    if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_ILBC) == 0) {
+                        return (SET_PAYLOAD_TYPE_WITH_DYNAMIC(ptype, RTP_ILBC));
+                    }
+                    if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_L16_256K) == 0) {
+                        return (SET_PAYLOAD_TYPE_WITH_DYNAMIC(ptype, RTP_L16));
+                    }
+                    if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_ISAC) == 0) {
+                        return (SET_PAYLOAD_TYPE_WITH_DYNAMIC(ptype, RTP_ISAC));
+                    }
+                    if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_OPUS) == 0) {
+                        return (SET_PAYLOAD_TYPE_WITH_DYNAMIC(ptype, RTP_OPUS));
+                    }
+                    if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_H264) == 0) {
+                        sdp_attr_get_fmtp_pack_mode(sdp_p, level, 0, (uint16_t) (i + 1), &pack_mode);
+                        if (pack_mode == SDP_DEFAULT_PACKETIZATION_MODE_VALUE) {
+                            return (SET_PAYLOAD_TYPE_WITH_DYNAMIC(ptype, RTP_H264_P0));
+                        } else {
+                            return (SET_PAYLOAD_TYPE_WITH_DYNAMIC(ptype, RTP_H264_P1));
+                        }
+                    }
+                    if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_VP8) == 0) {
+                        return (SET_PAYLOAD_TYPE_WITH_DYNAMIC(ptype, RTP_VP8));
+                    }
+                }
+            }
+        }
+    }
+    return (mca_p->payload_type[payload_num-1]);
+}
+
+/* Function:    sdp_get_media_profile_payload_type
+ * Description: Returns the payload type of the specified payload for the m=
+ *              media token line.  If the media line or payload number is
+ *              invalid, zero will be returned.  Application must validate
+ *              the media line before using this routine.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to of the m= media line.  Will be 1-n.
+ *              payload_num Number of the payload type to retrieve.  The
+ *                          range is (1 - max num payloads).
+ *              indicator   Returns the type of payload returned, either
+ *                          NUMERIC or ENUM.
+ * Returns:     Payload type or zero.
+ */
+u32 sdp_get_media_profile_payload_type (void *sdp_ptr, u16 level, u16 prof_num,
+                                        u16 payload_num,
+                                        sdp_payload_ind_e *indicator)
+{
+    sdp_t                *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t            *mca_p;
+    sdp_media_profiles_t *prof_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (0);
+    }
+
+    prof_p = mca_p->media_profiles_p;
+    if ((prof_num < 1) ||
+        (prof_num > prof_p->num_profiles)) {
+        return (0);
+    }
+
+    if ((payload_num < 1) ||
+        (payload_num > prof_p->num_payloads[prof_num-1])) {
+        return (0);
+    }
+
+    *indicator = prof_p->payload_indicator[prof_num-1][payload_num-1];
+    return (prof_p->payload_type[prof_num-1][payload_num-1]);
+}
+
+/* Function:    sdp_insert_media_line
+ * Description: Insert a new media line at the level specified for the
+ *              given SDP.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The new media level to insert.  Will be 1-n.
+ * Returns:     SDP_SUCCESS, SDP_NO_RESOURCE, or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_insert_media_line (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+    sdp_mca_t  *new_mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if ((level < 1) || (level > (sdp_p->mca_count+1))) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Invalid media line (%u) to insert, max is "
+                      "(%u).", sdp_p->debug_str, level, sdp_p->mca_count);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Allocate resource for new media stream. */
+    new_mca_p = sdp_alloc_mca();
+    if (new_mca_p == NULL) {
+        sdp_p->conf_p->num_no_resource++;
+        return (SDP_NO_RESOURCE);
+    }
+
+    if (level == 1) {
+        /* We're inserting the first media line */
+        new_mca_p->next_p = sdp_p->mca_p;
+        sdp_p->mca_p = new_mca_p;
+    } else {
+        /* Find the pointer to the media stream just prior to where
+         * we want to insert the new stream.
+         */
+        mca_p = sdp_find_media_level(sdp_p, (u16)(level-1));
+        if (mca_p == NULL) {
+            SDP_FREE(new_mca_p);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+
+        new_mca_p->next_p = mca_p->next_p;
+        mca_p->next_p = new_mca_p;
+    }
+
+    sdp_p->mca_count++;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_delete_media_line
+ * Description: Delete the media line at the level specified for the
+ *              given SDP.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to delete.  Will be 1-n.
+ * Returns:     SDP_SUCCESS, SDP_NO_RESOURCE, or SDP_INVALID_PARAMETER
+ */
+void sdp_delete_media_line (void *sdp_ptr, u16 level)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+    sdp_mca_t  *prev_mca_p = NULL;
+    sdp_attr_t *attr_p;
+    sdp_attr_t *next_attr_p;
+    sdp_bw_t        *bw_p;
+    sdp_bw_data_t   *bw_data_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return;
+    }
+
+    /* If we're not deleting media line 1, then we need a pointer
+     * to the previous media line so we can relink. */
+    if (level == 1) {
+        mca_p = sdp_find_media_level(sdp_p, level);
+    } else {
+        prev_mca_p = sdp_find_media_level(sdp_p, (u16)(level-1));
+        if (prev_mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return;
+        }
+        mca_p = prev_mca_p->next_p;
+    }
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return;
+    }
+
+    /* Delete all attributes from this level. */
+    for (attr_p = mca_p->media_attrs_p; attr_p != NULL;) {
+        next_attr_p = attr_p->next_p;
+        sdp_free_attr(attr_p);
+        attr_p = next_attr_p;
+    }
+
+     /* Delete bw line */
+     bw_p = &(mca_p->bw);
+     bw_data_p = bw_p->bw_data_list;
+     while (bw_data_p != NULL) {
+         bw_p->bw_data_list = bw_data_p->next_p;
+         SDP_FREE(bw_data_p);
+         bw_data_p = bw_p->bw_data_list;
+     }
+
+    /* Now relink the media levels and delete the specified one. */
+    if (prev_mca_p == NULL) {
+        sdp_p->mca_p = mca_p->next_p;
+    } else {
+        prev_mca_p->next_p = mca_p->next_p;
+    }
+    SDP_FREE(mca_p);
+    sdp_p->mca_count--;
+    return;
+}
+
+/* Function:    sdp_set_media_type
+ * Description: Sets the value of the media type parameter for the m=
+ *              media token line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to set the param.  Will be 1-n.
+ *              media       Media type for the media line.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_media_type (void *sdp_ptr, u16 level, sdp_media_e media)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->media = media;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_media_port_format
+ * Description: Sets the value of the port format parameter for the m=
+ *              media token line.  Note that this parameter must be set
+ *              before any of the port type specific parameters.  If a
+ *              parameter is not valid according to the port format
+ *              specified, an attempt to set the parameter will fail.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to set the param.  Will be 1-n.
+ *              port_format Media type for the media line.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_media_port_format (void *sdp_ptr, u16 level,
+                                        sdp_port_format_e port_format)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->port_format = port_format;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_media_portnum
+ * Description: Sets the value of the port number parameter for the m=
+ *              media token line.  If the port number is not valid with the
+ *              port format specified for the media line, this call will
+ *              fail.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to set the param.  Will be 1-n.
+ *              portnum     Port number to set.
+ *              sctpport    sctp port for application m= line
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_media_portnum (void *sdp_ptr, u16 level, int32 portnum, int32 sctp_port)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->port = portnum;
+    mca_p->sctpport = sctp_port;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_media_portcount
+ * Description: Sets the value of the port count parameter for the m=
+ *              media token line.  If the port count is not valid with the
+ *              port format specified for the media line, this call will
+ *              fail.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to set the param.  Will be 1-n.
+ *              num_ports   Port count to set.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_media_portcount (void *sdp_ptr, u16 level,
+                                      int32 num_ports)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->num_ports = num_ports;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_media_vpi
+ * Description: Sets the value of the VPI parameter for the m=
+ *              media token line.  If the VPI is not valid with the
+ *              port format specified for the media line, this call will
+ *              fail.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to set the param.  Will be 1-n.
+ *              vpi         The VPI value to set.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_media_vpi (void *sdp_ptr, u16 level, int32 vpi)
+{
+    sdp_t      *sdp_p = (sdp_t*)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->vpi = vpi;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_media_vci
+ * Description: Sets the value of the VCI parameter for the m=
+ *              media token line.  If the VCI is not valid with the
+ *              port format specified for the media line, this call will
+ *              fail.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to set the param.  Will be 1-n.
+ *              vci         The VCI value to set.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_media_vci (void *sdp_ptr, u16 level, u32 vci)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->vci = vci;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_media_vcci
+ * Description: Sets the value of the VCCI parameter for the m=
+ *              media token line.  If the VCCI is not valid with the
+ *              port format specified for the media line, this call will
+ *              fail.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to set the param.  Will be 1-n.
+ *              vcci        The VCCI value to set.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_media_vcci (void *sdp_ptr, u16 level, int32 vcci)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->vcci = vcci;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_media_cid
+ * Description: Sets the value of the CID parameter for the m=
+ *              media token line.  If the CID is not valid with the
+ *              port format specified for the media line, this call will
+ *              fail.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to set the param.  Will be 1-n.
+ *              cid         The CID value to set.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_media_cid (void *sdp_ptr, u16 level, int32 cid)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->cid = cid;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_set_media_transport
+ * Description: Sets the value of the transport type parameter for the m=
+ *              media token line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to set the param.  Will be 1-n.
+ *              transport   The transport type to set.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_set_media_transport (void *sdp_ptr, u16 level,
+                                      sdp_transport_e transport)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->transport = transport;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_add_media_profile
+ * Description: Add a new profile type for the m= media token line.  This is
+ *              used for AAL2 transport/profile types where more than one can
+ *              be specified per media line.  All other transport types should
+ *              use the other transport access routines rather than this.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The media level to add the param.  Will be 1-n.
+ *              profile     The profile type to add.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_add_media_profile (void *sdp_ptr, u16 level,
+                                    sdp_transport_e profile)
+{
+    u16         prof_num;
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (mca_p->media_profiles_p == NULL) {
+        mca_p->media_profiles_p = (sdp_media_profiles_t *) \
+            SDP_MALLOC(sizeof(sdp_media_profiles_t));
+        if (mca_p->media_profiles_p == NULL) {
+            sdp_p->conf_p->num_no_resource++;
+            return (SDP_NO_RESOURCE);
+        } else {
+            mca_p->media_profiles_p->num_profiles = 0;
+            /* Set the transport type to this first profile type. */
+            mca_p->transport = profile;
+        }
+    }
+
+    if (mca_p->media_profiles_p->num_profiles >= SDP_MAX_PROFILES) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Max number of media profiles already specified"
+                      " for media level %u", sdp_p->debug_str, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    prof_num = mca_p->media_profiles_p->num_profiles++;
+    mca_p->media_profiles_p->profile[prof_num] = profile;
+    mca_p->media_profiles_p->num_payloads[prof_num] = 0;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_add_media_payload_type
+ * Description: Add a new payload type for the media line at the level
+ *              specified. The new payload type will be added at the end
+ *              of the payload type list.
+ * Parameters:  sdp_ptr      The SDP handle returned by sdp_init_description.
+ *              level        The media level to add the payload.  Will be 1-n.
+ *              payload_type The new payload type.
+ *              indicator    Defines the type of payload returned, either
+ *                           NUMERIC or ENUM.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_add_media_payload_type (void *sdp_ptr, u16 level,
+                                         u16 payload_type,
+                                         sdp_payload_ind_e indicator)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (mca_p->num_payloads == SDP_MAX_PAYLOAD_TYPES) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Max number of payload types already defined "
+                      "for media line %u", sdp_p->debug_str, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    mca_p->payload_indicator[mca_p->num_payloads] = indicator;
+    mca_p->payload_type[mca_p->num_payloads++] = payload_type;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_add_media_profile_payload_type
+ * Description: Add a new payload type for the media line at the level
+ *              specified. The new payload type will be added at the end
+ *              of the payload type list.
+ * Parameters:  sdp_ptr      The SDP handle returned by sdp_init_description.
+ *              level        The media level to add the payload.  Will be 1-n.
+ *              prof_num     The profile number to add the payload type.
+ *              payload_type The new payload type.
+ *              indicator    Defines the type of payload returned, either
+ *                           NUMERIC or ENUM.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_add_media_profile_payload_type (void *sdp_ptr, u16 level,
+                                                u16 prof_num, u16 payload_type,
+                                                sdp_payload_ind_e indicator)
+{
+    u16         num_payloads;
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t  *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if ((prof_num < 1) ||
+        (prof_num > mca_p->media_profiles_p->num_profiles)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Invalid profile number (%u) for set profile "
+                      " payload type", sdp_p->debug_str, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (mca_p->media_profiles_p->num_payloads[prof_num-1] ==
+        SDP_MAX_PAYLOAD_TYPES) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Max number of profile payload types already "
+                      "defined profile %u on media line %u",
+                      sdp_p->debug_str, prof_num, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Get the current num payloads for this profile, and inc the number
+     * of payloads at the same time.  Then store the new payload type. */
+    num_payloads = mca_p->media_profiles_p->num_payloads[prof_num-1]++;
+    mca_p->media_profiles_p->payload_indicator[prof_num-1][num_payloads] =
+                                                           indicator;
+    mca_p->media_profiles_p->payload_type[prof_num-1][num_payloads] =
+                                                           payload_type;
+    return (SDP_SUCCESS);
+}
+
+/*
+ * sdp_find_bw_line
+ *
+ * This helper function locates a specific bw line instance given the
+ * sdp, the level and the instance number of the bw line.
+ *
+ * Returns: Pointer to the sdp_bw_data_t instance, or NULL.
+ */
+sdp_bw_data_t* sdp_find_bw_line (void *sdp_ptr, u16 level, u16 inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_bw_t            *bw_p;
+    sdp_bw_data_t       *bw_data_p;
+    sdp_mca_t           *mca_p;
+    int                 bw_attr_count=0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        bw_p = &(sdp_p->bw);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (NULL);
+        }
+        bw_p = &(mca_p->bw);
+    }
+
+    for (bw_data_p = bw_p->bw_data_list;
+         bw_data_p != NULL;
+         bw_data_p = bw_data_p->next_p) {
+        bw_attr_count++;
+        if (bw_attr_count == inst_num) {
+            return bw_data_p;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * sdp_copy_all_bw_lines
+ *
+ * Appends all the bw lines from the specified level of the orig sdp to the
+ * specified level of the dst sdp.
+ *
+ * Parameters:  src_sdp_ptr The source SDP handle.
+ *              dst_sdp_ptr The dest SDP handle.
+ *              src_level   The level in the src sdp from where to get the
+ *                          attributes.
+ *              dst_level   The level in the dst sdp where to put the
+ *                          attributes.
+ * Returns:     SDP_SUCCESS Attributes were successfully copied.
+ */
+sdp_result_e sdp_copy_all_bw_lines (void *src_sdp_ptr, void *dst_sdp_ptr,
+                                    u16 src_level, u16 dst_level)
+{
+    sdp_t                *src_sdp_p = (sdp_t *)src_sdp_ptr;
+    sdp_t                *dst_sdp_p = (sdp_t *)dst_sdp_ptr;
+    sdp_bw_data_t        *orig_bw_data_p;
+    sdp_bw_data_t        *new_bw_data_p;
+    sdp_bw_data_t        *bw_data_p;
+    sdp_bw_t             *src_bw_p;
+    sdp_bw_t             *dst_bw_p;
+    sdp_mca_t            *mca_p;
+
+    if (sdp_verify_sdp_ptr(src_sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_verify_sdp_ptr(dst_sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    /* Find src bw list */
+    if (src_level == SDP_SESSION_LEVEL) {
+        src_bw_p = &(src_sdp_p->bw);
+    } else {
+        mca_p = sdp_find_media_level(src_sdp_p, src_level);
+        if (mca_p == NULL) {
+            if (src_sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s Invalid src media level (%u) for copy all "
+                          "attrs ", src_sdp_p->debug_str, src_level);
+            }
+            return (SDP_INVALID_PARAMETER);
+        }
+        src_bw_p = &(mca_p->bw);
+    }
+
+    /* Find dst bw list */
+    if (dst_level == SDP_SESSION_LEVEL) {
+        dst_bw_p = &(dst_sdp_p->bw);
+    } else {
+        mca_p = sdp_find_media_level(dst_sdp_p, dst_level);
+        if (mca_p == NULL) {
+            if (src_sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s Invalid dst media level (%u) for copy all "
+                          "attrs ", src_sdp_p->debug_str, dst_level);
+            }
+            return (SDP_INVALID_PARAMETER);
+        }
+        dst_bw_p = &(mca_p->bw);
+    }
+
+    orig_bw_data_p = src_bw_p->bw_data_list;
+    while (orig_bw_data_p) {
+        /* For ever bw line in the src, allocate a new one for the dst */
+        new_bw_data_p = (sdp_bw_data_t*)SDP_MALLOC(sizeof(sdp_bw_data_t));
+        if (new_bw_data_p == NULL) {
+            return (SDP_NO_RESOURCE);
+        }
+        new_bw_data_p->next_p = NULL;
+        new_bw_data_p->bw_modifier = orig_bw_data_p->bw_modifier;
+        new_bw_data_p->bw_val = orig_bw_data_p->bw_val;
+
+        /*
+         * Enqueue the sdp_bw_data_t instance at the end of the list of
+         * sdp_bw_data_t instances.
+         */
+        if (dst_bw_p->bw_data_list == NULL) {
+            dst_bw_p->bw_data_list = new_bw_data_p;
+        } else {
+            for (bw_data_p = dst_bw_p->bw_data_list;
+                 bw_data_p->next_p != NULL;
+                 bw_data_p = bw_data_p->next_p) {
+
+                /*sa_ignore EMPTYLOOP*/
+                ; /* Do nothing. */
+            }
+
+            bw_data_p->next_p = new_bw_data_p;
+        }
+        dst_bw_p->bw_data_count++;
+
+        orig_bw_data_p = orig_bw_data_p->next_p;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_get_bw_modifier
+ * Description: Returns the bandwidth modifier parameter from the b=
+ *              line.  If no bw modifier has been set ,
+ *              SDP_BW_MODIFIER_UNSUPPORTED will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level from which to get the bw modifier.
+ *              inst_num    instance number of bw line at that level. The first
+ *                          instance has a inst_num of 1 and so on.
+ * Returns:     Valid modifer value or SDP_BW_MODIFIER_UNSUPPORTED.
+ */
+sdp_bw_modifier_e sdp_get_bw_modifier (void *sdp_ptr, u16 level, u16 inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_bw_data_t       *bw_data_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_BW_MODIFIER_UNSUPPORTED);
+    }
+
+    bw_data_p = sdp_find_bw_line(sdp_p, level, inst_num);
+
+    if (bw_data_p) {
+        return (bw_data_p->bw_modifier);
+    } else {
+        return (SDP_BW_MODIFIER_UNSUPPORTED);
+    }
+}
+
+/* Function:    sdp_get_bw_value
+ * Description: Returns the bandwidth value parameter from the b=
+ *              line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level from which to get the bw value.
+ *              inst_num    instance number of bw line at the level. The first
+ *                          instance has a inst_num of 1 and so on.
+ * Returns:     A valid numerical bw value or SDP_INVALID_VALUE.
+ */
+int32 sdp_get_bw_value (void *sdp_ptr, u16 level, u16 inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_bw_data_t       *bw_data_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    bw_data_p = sdp_find_bw_line(sdp_p, level, inst_num);
+
+    if (bw_data_p) {
+        return (bw_data_p->bw_val);
+    } else {
+        return (SDP_INVALID_VALUE);
+    }
+}
+
+/*
+ * sdp_get_num_bw_lines
+ *
+ * Returns the number of bw lines are present at a given level.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level at which the count of bw lines is required
+ *
+ * Returns: A valid count or SDP_INVALID_VALUE
+ */
+int32 sdp_get_num_bw_lines (void *sdp_ptr, u16 level)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_bw_t            *bw_p;
+    sdp_mca_t           *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        bw_p = &(sdp_p->bw);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_VALUE);
+        }
+        bw_p = &(mca_p->bw);
+    }
+
+    return (bw_p->bw_data_count);
+}
+
+/*
+ * sdp_add_new_bw_line
+ *
+ * To specify bandwidth parameters at any level, a bw line must first be
+ * added at that level using this function. After this addition, you can set
+ * the properties of the added bw line by using sdp_set_bw().
+ *
+ * Note carefully though, that since there can be multiple instances of bw
+ * lines at any level, you must specify the instance number when setting
+ * or getting the properties of a bw line at any level.
+ *
+ * This function returns within the inst_num variable, the instance number
+ * of the created bw_line at that level. The instance number is 1-based.
+ * For example:
+ *             v=0                               #Session Level
+ *             o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
+ *             s=SDP Seminar
+ *             c=IN IP4 10.1.0.2
+ *             t=0 0
+ *             b=AS:60                           # instance number 1
+ *             b=TIAS:50780                      # instance number 2
+ *             m=audio 1234 RTP/AVP 0 101 102    # 1st Media level
+ *             b=AS:12                           # instance number 1
+ *             b=TIAS:8480                       # instance number 2
+ *             m=audio 1234 RTP/AVP 0 101 102    # 2nd Media level
+ *             b=AS:20                           # instance number 1
+ *
+ * Parameters:
+ * sdp_ptr     The SDP handle returned by sdp_init_description.
+ * level       The level to create the bw line.
+ * bw_modifier The Type of bandwidth, CT, AS or TIAS.
+ * *inst_num   This memory is set with the instance number of the newly
+ *             created bw line instance.
+ */
+sdp_result_e sdp_add_new_bw_line (void *sdp_ptr, u16 level, sdp_bw_modifier_e bw_modifier, u16 *inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_bw_t            *bw_p;
+    sdp_mca_t           *mca_p;
+    sdp_bw_data_t       *new_bw_data_p;
+    sdp_bw_data_t       *bw_data_p = NULL;
+
+    *inst_num = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        bw_p = &(sdp_p->bw);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        bw_p = &(mca_p->bw);
+    }
+
+    //see if a bw line already exist for this bw_modifier.
+    for(bw_data_p = bw_p->bw_data_list; bw_data_p != NULL; bw_data_p = bw_data_p->next_p) {
+        ++(*inst_num);
+        if (bw_data_p->bw_modifier == bw_modifier) {
+            return (SDP_SUCCESS);
+        }
+    }
+
+    /*
+     * Allocate a new sdp_bw_data_t instance and set it's values from the
+     * input parameters.
+     */
+    new_bw_data_p = (sdp_bw_data_t*)SDP_MALLOC(sizeof(sdp_bw_data_t));
+    if (new_bw_data_p == NULL) {
+        sdp_p->conf_p->num_no_resource++;
+        return (SDP_NO_RESOURCE);
+    }
+    new_bw_data_p->next_p = NULL;
+    new_bw_data_p->bw_modifier = SDP_BW_MODIFIER_UNSUPPORTED;
+    new_bw_data_p->bw_val = 0;
+
+    /*
+     * Enqueue the sdp_bw_data_t instance at the end of the list of
+     * sdp_bw_data_t instances.
+     */
+    if (bw_p->bw_data_list == NULL) {
+        bw_p->bw_data_list = new_bw_data_p;
+    } else {
+        for (bw_data_p = bw_p->bw_data_list;
+             bw_data_p->next_p != NULL;
+             bw_data_p = bw_data_p->next_p) {
+
+             /*sa_ignore EMPTYLOOP*/
+             ; /* Do nothing. */
+        }
+
+        bw_data_p->next_p = new_bw_data_p;
+    }
+    *inst_num = ++bw_p->bw_data_count;
+
+    return (SDP_SUCCESS);
+}
+
+/*
+ * sdp_delete_bw_line
+ *
+ * Deletes the bw line instance at the specified level.
+ *
+ * sdp_ptr     The SDP handle returned by sdp_init_description.
+ * level       The level to delete the bw line.
+ * inst_num   The instance of the bw line to delete.
+ */
+sdp_result_e sdp_delete_bw_line (void *sdp_ptr, u16 level, u16 inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_bw_t            *bw_p;
+    sdp_mca_t           *mca_p;
+    sdp_bw_data_t       *bw_data_p = NULL;
+    sdp_bw_data_t       *prev_bw_data_p = NULL;
+    int                 bw_data_count = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        bw_p = &(sdp_p->bw);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        bw_p = &(mca_p->bw);
+    }
+
+    bw_data_p = bw_p->bw_data_list;
+    while (bw_data_p != NULL) {
+        bw_data_count++;
+        if (bw_data_count == inst_num) {
+            break;
+        }
+
+        prev_bw_data_p = bw_data_p;
+        bw_data_p = bw_data_p->next_p;
+    }
+
+    if (bw_data_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s bw line instance %d not found.",
+                      sdp_p->debug_str, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (prev_bw_data_p == NULL) {
+        bw_p->bw_data_list = bw_data_p->next_p;
+    } else {
+        prev_bw_data_p->next_p = bw_data_p->next_p;
+    }
+    bw_p->bw_data_count--;
+
+    SDP_FREE(bw_data_p);
+    return (SDP_SUCCESS);
+}
+
+/*
+ * sdp_set_bw
+ *
+ * Once a bandwidth line is added under a level, this function can be used to
+ * set the properties of that bandwidth line.
+ *
+ * Parameters:
+ * sdp_ptr     The SDP handle returned by sdp_init_description.
+ * level       The level to at which the bw line resides.
+ * inst_num    The instance number of the bw line that is to be set.
+ * bw_modifier The Type of bandwidth, CT, AS or TIAS.
+ * bw_val      Numerical bandwidth value.
+ *
+ * NOTE: Before calling this function to set the bw line, the bw line must
+ * be added using sdp_add_new_bw_line at the required level.
+ */
+sdp_result_e sdp_set_bw (void *sdp_ptr, u16 level, u16 inst_num,
+                         sdp_bw_modifier_e bw_modifier, u32 bw_val)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_bw_data_t       *bw_data_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if ((bw_modifier < SDP_BW_MODIFIER_AS) ||
+        (bw_modifier >= SDP_MAX_BW_MODIFIER_VAL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Invalid bw modifier type: %d.",
+                      sdp_p->debug_str, bw_modifier);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    bw_data_p = sdp_find_bw_line(sdp_p, level, inst_num);
+    if (bw_data_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s The %u instance of a b= line was not found at level %u.",
+                      sdp_p->debug_str, inst_num, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    bw_data_p->bw_modifier = bw_modifier;
+    bw_data_p->bw_val = bw_val;
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_get_mid_value
+ * Description: Returns the mid value parameter from the a= mid: line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       SDP_MEDIA_LEVEL
+ * Returns:     mid value.
+ */
+int32 sdp_get_mid_value (void *sdp_ptr, u16 level)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t           *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    }
+    return (mca_p->mid);
+}
+
+/* Function:    sdp_set_mid_value
+ * Description: Sets the value of the mid value for the
+ *              a= mid:<val> line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       SDP_MEDIA_LEVEL
+ *              mid_val     mid value .
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+*/
+sdp_result_e sdp_set_mid_value (void *sdp_ptr, u16 level, u32 mid_val)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t           *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    mca_p->mid = mid_val;
+    return (SDP_SUCCESS);
+}
diff --git a/libs/sipcc/core/sdp/sdp_attr.c b/libs/sipcc/core/sdp/sdp_attr.c
new file mode 100644 (file)
index 0000000..177628f
--- /dev/null
@@ -0,0 +1,4755 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+#include <limits.h>
+
+#include "plstr.h"
+#include "sdp_os_defs.h"
+#include "sdp.h"
+#include "sdp_private.h"
+#include "sdp_base64.h"
+#include "mozilla/Assertions.h"
+#include "CSFLog.h"
+
+static const char* logTag = "sdp_attr";
+
+/*
+ * Macro for sdp_build_attr_fmtp
+ * Adds name-value pair where value is char*
+ */
+#define FMTP_BUILD_STRING(condition, name, value) \
+  if ((condition)) { \
+    sdp_append_name_and_string(fs, (name), (value), semicolon); \
+    semicolon = TRUE; \
+  }
+
+/*
+ * Macro for sdp_build_attr_fmtp
+ * Adds name-value pair where value is unsigned
+ */
+#define FMTP_BUILD_UNSIGNED(condition, name, value) \
+  if ((condition)) { \
+    sdp_append_name_and_unsigned(fs, (name), (value), semicolon); \
+    semicolon = TRUE; \
+  }
+
+/*
+ * Macro for sdp_build_attr_fmtp
+ * Adds flag string on condition
+ */
+#define FMTP_BUILD_FLAG(condition, name) \
+  if ((condition)) { \
+    if (semicolon) { \
+      flex_string_append(fs, ";"); \
+    } \
+    flex_string_append(fs, name); \
+    semicolon = TRUE; \
+  }
+
+/*
+ * Helper function for adding nv-pair where value is string.
+ */
+static void sdp_append_name_and_string(flex_string *fs,
+  const char *name,
+  const char *value,
+  tinybool semicolon)
+{
+  flex_string_sprintf(fs, "%s%s=%s",
+    semicolon ? ";" : "",
+    name,
+    value);
+}
+
+/*
+ * Helper function for adding nv-pair where value is unsigned.
+ */
+static void sdp_append_name_and_unsigned(flex_string *fs,
+  const char *name,
+  unsigned int value,
+  tinybool semicolon)
+{
+  flex_string_sprintf(fs, "%s%s=%u",
+    semicolon ? ";" : "",
+    name,
+    value);
+}
+
+/* Function:    sdp_parse_attribute
+ * Description: Figure out the type of attribute and call the appropriate
+ *              parsing routine.  If parsing errors are encountered,
+ *              warnings will be printed and the attribute will be ignored.
+ *              Unrecognized/invalid attributes do not cause overall parsing
+ *              errors.  All errors detected are noted as warnings.
+ * Parameters:  sdp_p       The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              ptr         Pointer to the attribute string to parse.
+ */
+sdp_result_e sdp_parse_attribute (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    int           i;
+    u8            xcpar_flag = FALSE;
+    sdp_result_e  result;
+    sdp_mca_t    *mca_p=NULL;
+    sdp_attr_t   *attr_p;
+    sdp_attr_t   *next_attr_p;
+    sdp_attr_t   *prev_attr_p = NULL;
+    char          tmp[SDP_MAX_STRING_LEN];
+
+    /* Validate the level */
+    if (level != SDP_SESSION_LEVEL) {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_FAILURE);
+        }
+    }
+
+    /* Find the attribute type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), ": \t", &result);
+    if (ptr == NULL) {
+        sdp_parse_error(sdp_p->peerconnection,
+          "%s No attribute type specified, parse failed.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    if (ptr[0] == ':') {
+        /* Skip the ':' char for parsing attribute parameters. */
+        ptr++;
+    }
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+          "%s No attribute type specified, parse failed.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = (sdp_attr_t *)SDP_MALLOC(sizeof(sdp_attr_t));
+    if (attr_p == NULL) {
+        sdp_p->conf_p->num_no_resource++;
+        return (SDP_NO_RESOURCE);
+    }
+    attr_p->type = SDP_ATTR_INVALID;
+    attr_p->next_p = NULL;
+    for (i=0; i < SDP_MAX_ATTR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_attr[i].name, sdp_attr[i].strlen) == 0) {
+            attr_p->type = (sdp_attr_e)i;
+            break;
+        }
+    }
+    if (attr_p->type == SDP_ATTR_INVALID) {
+        sdp_parse_error(sdp_p->peerconnection,
+          "%s Warning: Unrecognized attribute (%s) ",
+          sdp_p->debug_str, tmp);
+        sdp_free_attr(attr_p);
+        return (SDP_SUCCESS);
+    }
+
+    /* If this is an X-cpar or cpar attribute, set the flag.  The attribute
+     * type will be changed by the parse. */
+    if ((attr_p->type == SDP_ATTR_X_CPAR) ||
+       (attr_p->type == SDP_ATTR_CPAR)) {
+        xcpar_flag = TRUE;
+    }
+
+    /* Parse the attribute. */
+    result = sdp_attr[attr_p->type].parse_func(sdp_p, attr_p, ptr);
+    if (result != SDP_SUCCESS) {
+        sdp_free_attr(attr_p);
+        /* Return success so the parse won't fail.  We don't want to
+         * fail on errors with attributes but just ignore them.
+         */
+        return (SDP_SUCCESS);
+    }
+
+    /* If this was an X-cpar/cpar attribute, it was hooked into the X-cap/cdsc
+     * structure, so we're finished.
+     */
+    if (xcpar_flag == TRUE) {
+        return (result);
+    }
+
+    /* Add the attribute in the appropriate place. */
+    if (level == SDP_SESSION_LEVEL) {
+        for (next_attr_p = sdp_p->sess_attrs_p; next_attr_p != NULL;
+             prev_attr_p = next_attr_p,
+                 next_attr_p = next_attr_p->next_p) {
+            ; /* Empty for */
+        }
+        if (prev_attr_p == NULL) {
+            sdp_p->sess_attrs_p = attr_p;
+        } else {
+            prev_attr_p->next_p = attr_p;
+        }
+    } else {
+        for (next_attr_p = mca_p->media_attrs_p; next_attr_p != NULL;
+             prev_attr_p = next_attr_p,
+                 next_attr_p = next_attr_p->next_p) {
+            ; /* Empty for */
+        }
+        if (prev_attr_p == NULL) {
+            mca_p->media_attrs_p = attr_p;
+        } else {
+            prev_attr_p->next_p = attr_p;
+        }
+    }
+
+    return (result);
+}
+
+/* Build all of the attributes defined for the specified level. */
+sdp_result_e sdp_build_attribute (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    sdp_attr_t   *attr_p;
+    sdp_mca_t    *mca_p=NULL;
+    sdp_result_e  result;
+
+    if (level == SDP_SESSION_LEVEL) {
+        attr_p = sdp_p->sess_attrs_p;
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_FAILURE);
+        }
+        attr_p = mca_p->media_attrs_p;
+    }
+    /* Re-initialize the current capability number for this new level. */
+    sdp_p->cur_cap_num = 1;
+
+    /* Build all of the attributes for this level. Note that if there
+     * is a problem building an attribute, we don't fail but just ignore it.*/
+    while (attr_p != NULL) {
+        if (attr_p->type >= SDP_MAX_ATTR_TYPES) {
+            if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+                CSFLogDebug(logTag, "%s Invalid attribute type to build (%u)",
+                         sdp_p->debug_str, attr_p->type);
+            }
+        } else {
+            result = sdp_attr[attr_p->type].build_func(sdp_p, attr_p, fs);
+
+            if (result != SDP_SUCCESS) {
+              CSFLogError(logTag, "%s error building attribute %d", __FUNCTION__, result);
+              return result;
+            }
+
+            if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+                SDP_PRINT("%s Built a=%s attribute line", sdp_p->debug_str,
+                          sdp_get_attr_name(attr_p->type));
+            }
+        }
+        attr_p = attr_p->next_p;
+    }
+
+    return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_simple_string (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                           const char *ptr)
+{
+    sdp_result_e  result;
+
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.string_val,
+      sizeof(attr_p->attr.string_val), " \t", &result);
+
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No string token found for %s attribute",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Parsed a=%s, %s", sdp_p->debug_str,
+                      sdp_get_attr_name(attr_p->type),
+                      attr_p->attr.string_val);
+        }
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_build_attr_simple_string (sdp_t *sdp_p, sdp_attr_t *attr_p,
+  flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s\r\n", sdp_attr[attr_p->type].name,
+    attr_p->attr.string_val);
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_simple_u32 (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                        const char *ptr)
+{
+    sdp_result_e  result;
+
+    attr_p->attr.u32_val = sdp_getnextnumtok(ptr, &ptr, " \t", &result);
+
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Numeric token for %s attribute not found",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Parsed a=%s, %lu", sdp_p->debug_str,
+                      sdp_get_attr_name(attr_p->type), attr_p->attr.u32_val);
+        }
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_build_attr_simple_u32 (sdp_t *sdp_p, sdp_attr_t *attr_p,
+  flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%u\r\n", sdp_attr[attr_p->type].name,
+    attr_p->attr.u32_val);
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_simple_bool (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                         const char *ptr)
+{
+    sdp_result_e  result;
+
+    if (sdp_getnextnumtok(ptr, &ptr, " \t", &result) == 0) {
+        attr_p->attr.boolean_val = FALSE;
+    } else {
+        attr_p->attr.boolean_val= TRUE;
+    }
+
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Boolean token for %s attribute not found",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            if (attr_p->attr.boolean_val) {
+                SDP_PRINT("%s Parsed a=%s, boolean is TRUE", sdp_p->debug_str,
+                          sdp_get_attr_name(attr_p->type));
+            } else {
+                SDP_PRINT("%s Parsed a=%s, boolean is FALSE", sdp_p->debug_str,
+                          sdp_get_attr_name(attr_p->type));
+            }
+        }
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_build_attr_simple_bool (sdp_t *sdp_p, sdp_attr_t *attr_p,
+  flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s\r\n", sdp_attr[attr_p->type].name,
+    attr_p->attr.boolean_val ? "1" : "0");
+
+  return SDP_SUCCESS;
+}
+
+/*
+ * sdp_parse_attr_maxprate
+ *
+ * This function parses maxprate attribute lines. The ABNF for this a=
+ * line is:
+ *    max-p-rate-def = "a" "=" "maxprate" ":" packet-rate CRLF
+ *    packet-rate = 1*DIGIT ["." 1*DIGIT]
+ *
+ * Returns:
+ * SDP_INVALID_PARAMETER - If we are unable to parse the string OR if
+ *                         packet-rate is not in the right format as per
+ *                         the ABNF.
+ *
+ * SDP_SUCCESS - If we are able to successfully parse the a= line.
+ */
+sdp_result_e sdp_parse_attr_maxprate (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                      const char *ptr)
+{
+    sdp_result_e  result;
+
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.string_val,
+      sizeof(attr_p->attr.string_val), " \t", &result);
+
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No string token found for %s attribute",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (!sdp_validate_maxprate(attr_p->attr.string_val)) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s is not a valid maxprate value.",
+                attr_p->attr.string_val);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Parsed a=%s, %s", sdp_p->debug_str,
+                      sdp_get_attr_name(attr_p->type),
+                      attr_p->attr.string_val);
+        }
+        return (SDP_SUCCESS);
+    }
+}
+
+/*
+ * sdp_attr_fmtp_no_value
+ * Helper function for sending the warning when a parameter value is
+ * missing.
+ *
+ */
+static void sdp_attr_fmtp_no_value(sdp_t *sdp, char *param_name)
+{
+  sdp_parse_error(sdp->peerconnection,
+    "%s Warning: No %s value specified for fmtp attribute",
+    sdp->debug_str, param_name);
+  sdp->conf_p->num_invalid_param++;
+}
+
+/*
+ * sdp_attr_fmtp_invalid_value
+ * Helper function for sending the warning when a parameter value is
+ * incorrect.
+ *
+ */
+
+static void sdp_attr_fmtp_invalid_value(sdp_t *sdp, char *param_name,
+  char* param_value)
+{
+  sdp_parse_error(sdp->peerconnection,
+    "%s Warning: Invalid %s: %s specified for fmtp attribute",
+    sdp->debug_str, param_name, param_value);
+  sdp->conf_p->num_invalid_param++;
+}
+
+/* Note:  The fmtp attribute formats currently handled are:
+ *        fmtp:<payload type> <event>,<event>...
+ *        fmtp:<payload_type> [annexa=yes/no] [annexb=yes/no] [bitrate=<value>]
+ *        [QCIF =<value>] [CIF =<value>] [MaxBR = <value>] one or more
+ *        Other FMTP params as per H.263, H.263+, H.264 codec support.
+ *        Note -"value" is a numeric value > 0 and each event is a
+ *        single number or a range separated by a '-'.
+ *        Example:  fmtp:101 1,3-15,20
+ * Video codecs have annexes that can be listed in the following legal formats:
+ * a) a=fmtp:34 param1=token;D;I;J;K=1;N=2;P=1,3
+ * b) a=fmtp:34 param1=token;D;I;J;K=1;N=2;P=1,3;T
+ * c) a=fmtp:34 param1=token;D;I;J
+ *
+ */
+
+sdp_result_e sdp_parse_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                  const char *ptr)
+{
+    u16           i;
+    u32           mapword;
+    u32           bmap;
+    u8            low_val;
+    u8            high_val;
+    const char    *ptr2;
+    const char    *fmtp_ptr;
+    sdp_result_e  result1 = SDP_SUCCESS;
+    sdp_result_e  result2 = SDP_SUCCESS;
+    tinybool      done = FALSE;
+    tinybool      codec_info_found = FALSE;
+    sdp_fmtp_t   *fmtp_p;
+    char          tmp[SDP_MAX_STRING_LEN];
+    char          *src_ptr;
+    char          *temp_ptr = NULL;
+    tinybool flag=FALSE;
+    char         *tok=NULL;
+    char         *temp=NULL;
+    u16          custom_x=0;
+    u16          custom_y=0;
+    u16          custom_mpi=0;
+    u16          par_height=0;
+    u16          par_width=0;
+    u16          cpcf=0;
+    u16          iter=0;
+
+    ulong        l_val = 0;
+    char*        strtok_state;
+    unsigned long strtoul_result;
+    char*        strtoul_end;
+
+    /* Find the payload type number. */
+    attr_p->attr.fmtp.payload_num = (u16)sdp_getnextnumtok(ptr, &ptr,
+                                                      " \t", &result1);
+    if (result1 != SDP_SUCCESS) {
+        sdp_attr_fmtp_no_value(sdp_p, "payload type");
+        return SDP_INVALID_PARAMETER;
+    }
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_UNKNOWN_TYPE;
+    fmtp_p->parameter_add = TRUE;
+    fmtp_p->flag = 0;
+
+    /*
+     * set default value of packetization mode and level-asymmetry-allowed. If
+     * remote sdp does not specify any value for these two parameters, then the
+     * default value will be assumed for remote sdp. If remote sdp does specify
+     * any value for these parameters, then default value will be overridden.
+    */
+    fmtp_p->packetization_mode = 0;
+    fmtp_p->level_asymmetry_allowed = SDP_DEFAULT_LEVEL_ASYMMETRY_ALLOWED_VALUE;
+
+    /* BEGIN - a typical macro fn to replace '/' with ';' from fmtp line*/
+    /* This ugly replacement of '/' with ';' is only done because
+    *  econf/MS client sends in this wierd /illegal format.
+    * fmtp parameters MUST be  separated by ';'
+    */
+    temp_ptr = cpr_strdup(ptr);
+    if (temp_ptr == NULL) {
+        return (SDP_FAILURE);
+    }
+    fmtp_ptr = src_ptr = temp_ptr;
+    while (flag == FALSE) {
+        if (*src_ptr == '\n') {
+            flag = TRUE;
+            break;
+        }
+        if (*src_ptr == '/') {
+            *src_ptr =';' ;
+        }
+        src_ptr++;
+    }
+    /* END */
+    /* Once we move to RFC compliant video codec implementations, the above
+    *  patch should be removed */
+    while (!done) {
+      fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "= \t", &result1);
+      if (result1 == SDP_SUCCESS) {
+        if (cpr_strncasecmp(tmp, sdp_fmtp_codec_param[1].name,
+                       sdp_fmtp_codec_param[1].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr  = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "annexb");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+            tok = tmp;
+           tok++;
+           if (cpr_strncasecmp(tok,sdp_fmtp_codec_param_val[0].name,
+                           sdp_fmtp_codec_param_val[0].strlen) == 0) {
+               fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+               fmtp_p->annexb_required = TRUE;
+               fmtp_p->annexb = TRUE;
+           } else if (cpr_strncasecmp(tok,sdp_fmtp_codec_param_val[1].name,
+                                  sdp_fmtp_codec_param_val[1].strlen) == 0) {
+               fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+               fmtp_p->annexb_required = TRUE;
+               fmtp_p->annexb = FALSE;
+           } else {
+                sdp_attr_fmtp_invalid_value(sdp_p, "annexb", tok);
+               SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           codec_info_found = TRUE;
+
+       } else if (cpr_strncasecmp(tmp, sdp_fmtp_codec_param[0].name,
+                              sdp_fmtp_codec_param[0].strlen) == 0) {
+
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "annexa");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+           if (cpr_strncasecmp(tok,sdp_fmtp_codec_param_val[0].name,
+                           sdp_fmtp_codec_param_val[0].strlen) == 0) {
+               fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+               fmtp_p->annexa = TRUE;
+               fmtp_p->annexa_required = TRUE;
+           } else if (cpr_strncasecmp(tok,sdp_fmtp_codec_param_val[1].name,
+                                  sdp_fmtp_codec_param_val[1].strlen) == 0) {
+               fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+               fmtp_p->annexa = FALSE;
+               fmtp_p->annexa_required = TRUE;
+           } else {
+                sdp_attr_fmtp_invalid_value(sdp_p, "annexa", tok);
+               SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           codec_info_found = TRUE;
+
+       } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[2].name,
+                               sdp_fmtp_codec_param[2].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "bitrate");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+            }
+            tok = tmp;
+            tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > UINT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "bitrate", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+
+            fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->bitrate = (u32) strtoul_result;
+            codec_info_found = TRUE;
+
+         } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[41].name,
+                               sdp_fmtp_codec_param[41].strlen) == 0) {
+            fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+            if (result1 != SDP_SUCCESS) {
+                fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+                if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "mode");
+                    SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            tok = tmp;
+            tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end || strtoul_result > UINT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "mode", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+
+            fmtp_p->fmtp_format = SDP_FMTP_MODE;
+            fmtp_p->mode = (u32) strtoul_result;
+            codec_info_found = TRUE;
+
+       } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[3].name,
+                               sdp_fmtp_codec_param[3].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+            if (result1 != SDP_SUCCESS) {
+                sdp_attr_fmtp_no_value(sdp_p, "qcif");
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+        }
+        tok = tmp;
+        tok++;
+
+        errno = 0;
+        strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+        if (errno || tok == strtoul_end ||
+            strtoul_result < SDP_MIN_CIF_VALUE || strtoul_result > SDP_MAX_CIF_VALUE) {
+            sdp_attr_fmtp_invalid_value(sdp_p, "qcif", tok);
+            SDP_FREE(temp_ptr);
+            return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->qcif = (u16) strtoul_result;
+           codec_info_found = TRUE;
+       } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[4].name,
+                               sdp_fmtp_codec_param[4].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+            fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+            if (result1 != SDP_SUCCESS) {
+                sdp_attr_fmtp_no_value(sdp_p, "cif");
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+        }
+        tok = tmp;
+        tok++;
+
+        errno = 0;
+        strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+        if (errno || tok == strtoul_end ||
+            strtoul_result < SDP_MIN_CIF_VALUE || strtoul_result > SDP_MAX_CIF_VALUE) {
+            sdp_attr_fmtp_invalid_value(sdp_p, "cif", tok);
+            SDP_FREE(temp_ptr);
+            return SDP_INVALID_PARAMETER;
+       }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->cif = (u16) strtoul_result;
+           codec_info_found = TRUE;
+       } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[5].name,
+                               sdp_fmtp_codec_param[5].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+            fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "maxbr");
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+        }
+        tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end ||
+                strtoul_result == 0 || strtoul_result > USHRT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "maxbr", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->maxbr = (u16) strtoul_result;
+           codec_info_found = TRUE;
+       } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[6].name,
+                               sdp_fmtp_codec_param[6].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "sqcif");
+                    SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+           }
+           tok = tmp;
+           tok++;
+
+        errno = 0;
+        strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+        if (errno || tok == strtoul_end ||
+            strtoul_result < SDP_MIN_CIF_VALUE || strtoul_result > SDP_MAX_CIF_VALUE) {
+            sdp_attr_fmtp_invalid_value(sdp_p, "sqcif", tok);
+            SDP_FREE(temp_ptr);
+            return SDP_INVALID_PARAMETER;
+        }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->sqcif = (u16) strtoul_result;
+           codec_info_found = TRUE;
+       } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[7].name,
+                               sdp_fmtp_codec_param[7].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "cif4");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+        errno = 0;
+        strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end ||
+                strtoul_result < SDP_MIN_CIF_VALUE || strtoul_result > SDP_MAX_CIF_VALUE) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "cif4", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->cif4 = (u16) strtoul_result;
+           codec_info_found = TRUE;
+       } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[8].name,
+                               sdp_fmtp_codec_param[8].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "cif16");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+        errno = 0;
+        strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+        if (errno || tok == strtoul_end ||
+            strtoul_result < SDP_MIN_CIF_VALUE || strtoul_result > SDP_MAX_CIF_VALUE) {
+            sdp_attr_fmtp_invalid_value(sdp_p, "cif16", tok);
+            SDP_FREE(temp_ptr);
+            return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->cif16 = (u16) strtoul_result;
+           codec_info_found = TRUE;
+        } else  if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[9].name,
+                               sdp_fmtp_codec_param[9].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "custom");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++; temp=PL_strtok_r(tok, ",", &strtok_state);
+           iter++;
+        if (temp) {
+            iter=1;
+            while (temp != NULL) {
+                errno = 0;
+                strtoul_result = strtoul(temp, &strtoul_end, 10);
+
+                if (errno || temp == strtoul_end || strtoul_result > USHRT_MAX){
+                    custom_x = custom_y = custom_mpi = 0;
+                    break;
+                }
+
+                if (iter == 1)
+                    custom_x = (u16) strtoul_result;
+                if (iter == 2)
+                    custom_y = (u16) strtoul_result;
+                if (iter == 3)
+                    custom_mpi = (u16) strtoul_result;
+
+                temp=PL_strtok_r(NULL, ",", &strtok_state);
+                iter++;
+            }
+        }
+
+        /* custom x,y and mpi values from tmp */
+           if (!custom_x || !custom_y || !custom_mpi) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "x/y/MPI", temp);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->custom_x = custom_x;
+            fmtp_p->custom_y = custom_y;
+            fmtp_p->custom_mpi = custom_mpi;
+           codec_info_found = TRUE;
+        } else  if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[10].name,
+                               sdp_fmtp_codec_param[10].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "par");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++; temp=PL_strtok_r(tok, ":", &strtok_state);
+        if (temp) {
+            iter=1;
+            /* get par width and par height for the aspect ratio */
+            while (temp != NULL) {
+                errno = 0;
+                strtoul_result = strtoul(temp, &strtoul_end, 10);
+
+                if (errno || temp == strtoul_end || strtoul_result > USHRT_MAX) {
+                    par_width = par_height = 0;
+                    break;
+                }
+
+                if (iter == 1)
+                    par_width = (u16) strtoul_result;
+                else
+                    par_height = (u16) strtoul_result;
+
+                temp=PL_strtok_r(NULL, ",", &strtok_state);
+                iter++;
+            }
+        }
+           if (!par_width || !par_height) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "par_width or par_height", temp);
+               SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->par_width = par_width;
+            fmtp_p->par_height = par_height;
+           codec_info_found = TRUE;
+        } else  if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[11].name,
+                               sdp_fmtp_codec_param[11].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "cpcf");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++; temp=PL_strtok_r(tok, ".", &strtok_state);
+        if ( temp != NULL  ) {
+            errno = 0;
+            strtoul_result = strtoul(temp, &strtoul_end, 10);
+
+            if (errno || temp == strtoul_end || strtoul_result > USHRT_MAX) {
+                cpcf = 0;
+            } else {
+                cpcf = (u16) strtoul_result;
+            }
+        }
+
+           if (!cpcf) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "cpcf", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->cpcf = cpcf;
+           codec_info_found = TRUE;
+        } else  if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[12].name,
+                               sdp_fmtp_codec_param[12].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "bpp");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+        errno = 0;
+        strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+        if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > USHRT_MAX) {
+            sdp_attr_fmtp_invalid_value(sdp_p, "bpp", tok);
+            SDP_FREE(temp_ptr);
+            return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+        fmtp_p->bpp = (u16) strtoul_result;
+           codec_info_found = TRUE;
+        } else  if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[13].name,
+                               sdp_fmtp_codec_param[13].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "hrd");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > USHRT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "hrd", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->hrd = (u16) strtoul_result;
+           codec_info_found = TRUE;
+       } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[14].name,
+                               sdp_fmtp_codec_param[14].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "profile");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end ||
+                strtoul_result > SDP_MAX_PROFILE_VALUE) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "profile", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->profile = (short) strtoul_result;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[15].name,
+                               sdp_fmtp_codec_param[15].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "level");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+        errno = 0;
+        strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+       if (errno || tok == strtoul_end ||
+            strtoul_result > SDP_MAX_LEVEL_VALUE) {
+            sdp_attr_fmtp_invalid_value(sdp_p, "level", tok);
+            SDP_FREE(temp_ptr);
+            return SDP_INVALID_PARAMETER;
+       }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->level = (short) strtoul_result;
+           codec_info_found = TRUE;
+        } if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[16].name,
+                               sdp_fmtp_codec_param[16].strlen) == 0) {
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->is_interlace = TRUE;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[17].name,
+                               sdp_fmtp_codec_param[17].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "profile_level_id");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            sstrncpy(fmtp_p->profile_level_id , tok, sizeof(fmtp_p->profile_level_id));
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[18].name,
+                               sdp_fmtp_codec_param[18].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "parameter_sets");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            sstrncpy(fmtp_p->parameter_sets , tok, sizeof(fmtp_p->parameter_sets));
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[19].name,
+                               sdp_fmtp_codec_param[19].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "packetization_mode");
+                    SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result > 2) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "packetization_mode", tok);
+                sdp_p->conf_p->num_invalid_param++;
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+
+            fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->packetization_mode = (int16) strtoul_result;
+            codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[20].name,
+                               sdp_fmtp_codec_param[20].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "interleaving_depth");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > USHRT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "interleaving_depth", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->interleaving_depth = (u16) strtoul_result;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[21].name,
+                               sdp_fmtp_codec_param[21].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "deint_buf");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+            if (sdp_checkrange(sdp_p, tok, &l_val) == TRUE) {
+               fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+               fmtp_p->deint_buf_req = (u32) l_val;
+                fmtp_p->flag |= SDP_DEINT_BUF_REQ_FLAG;
+               codec_info_found = TRUE;
+            } else {
+                sdp_attr_fmtp_invalid_value(sdp_p, "deint_buf_req", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[22].name,
+                               sdp_fmtp_codec_param[22].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "max_don_diff");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > UINT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "max_don_diff", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->max_don_diff = (u32) strtoul_result;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[23].name,
+                               sdp_fmtp_codec_param[23].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "init_buf_time");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+            if (sdp_checkrange(sdp_p, tok, &l_val) == TRUE) {
+               fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+               fmtp_p->init_buf_time = (u32) l_val;
+                fmtp_p->flag |= SDP_INIT_BUF_TIME_FLAG;
+               codec_info_found = TRUE;
+            } else {
+                sdp_attr_fmtp_invalid_value(sdp_p, "init_buf_time", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[24].name,
+                               sdp_fmtp_codec_param[24].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "max_mbps");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > UINT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "max_mbps", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+        fmtp_p->max_mbps = (u32) strtoul_result;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[25].name,
+                               sdp_fmtp_codec_param[25].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "max_fs");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > UINT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "max_fs", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->max_fs = (u32) strtoul_result;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[26].name,
+                               sdp_fmtp_codec_param[26].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "max_cbp");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > UINT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "max_cpb", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->max_cpb = (u32) strtoul_result;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[27].name,
+                               sdp_fmtp_codec_param[27].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "max_dpb");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > UINT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "max_dpb", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->max_dpb = (u32) strtoul_result;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[28].name,
+                               sdp_fmtp_codec_param[28].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "max_br");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > UINT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "max_br", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->max_br = (u32) strtoul_result;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[29].name,
+                               sdp_fmtp_codec_param[29].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "redundant_pic_cap");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+        errno = 0;
+        strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+        if (!errno && tok != strtoul_end && strtoul_result == 1) {
+            fmtp_p->redundant_pic_cap = TRUE;
+        } else {
+            fmtp_p->redundant_pic_cap = FALSE;
+        }
+
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[30].name,
+                               sdp_fmtp_codec_param[30].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "deint_buf_cap");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+            if (sdp_checkrange(sdp_p, tok, &l_val) == TRUE) {
+               fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+               fmtp_p->deint_buf_cap = (u32) l_val;
+                fmtp_p->flag |= SDP_DEINT_BUF_CAP_FLAG;
+               codec_info_found = TRUE;
+            } else {
+                sdp_attr_fmtp_invalid_value(sdp_p, "deint_buf_cap", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+        }  else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[31].name,
+                               sdp_fmtp_codec_param[31].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "max_rcmd_nalu_size");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+            if (sdp_checkrange(sdp_p, tok, &l_val) == TRUE) {
+               fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+               fmtp_p->max_rcmd_nalu_size = (u32) l_val;
+                fmtp_p->flag |= SDP_MAX_RCMD_NALU_SIZE_FLAG;
+               codec_info_found = TRUE;
+            } else {
+                sdp_attr_fmtp_invalid_value(sdp_p, "max_rcmd_nalu_size", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[32].name,
+                               sdp_fmtp_codec_param[32].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+               fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "parameter_add");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+               }
+           }
+           tok = tmp;
+           tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end || strtoul_result > 1) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "parameter_add", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+
+               fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+               if (strtoul_result == 1) {
+                   fmtp_p->parameter_add = TRUE;
+               } else {
+                   fmtp_p->parameter_add = FALSE;
+               }
+
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[33].name,
+                               sdp_fmtp_codec_param[33].strlen) == 0) {
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->annex_d = TRUE;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[34].name,
+                               sdp_fmtp_codec_param[34].strlen) == 0) {
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->annex_f = TRUE;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[35].name,
+                               sdp_fmtp_codec_param[35].strlen) == 0) {
+            fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->annex_i = TRUE;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[36].name,
+                               sdp_fmtp_codec_param[36].strlen) == 0) {
+            fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->annex_j = TRUE;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[37].name,
+                               sdp_fmtp_codec_param[36].strlen) == 0) {
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->annex_t = TRUE;
+           codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[38].name,
+                             sdp_fmtp_codec_param[38].strlen) == 0) {
+                fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+                if (result1 != SDP_SUCCESS) {
+                    fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+                    if (result1 != SDP_SUCCESS) {
+                        sdp_attr_fmtp_no_value(sdp_p, "annex_k");
+                       SDP_FREE(temp_ptr);
+                        return SDP_INVALID_PARAMETER;
+                    }
+                }
+                tok = tmp;
+                tok++;
+
+                errno = 0;
+                strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+                if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > USHRT_MAX) {
+                    sdp_attr_fmtp_invalid_value(sdp_p, "annex_k", tok);
+                    SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+                fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+                fmtp_p->annex_k_val = (u16) strtoul_result;
+                codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[39].name,
+                               sdp_fmtp_codec_param[39].strlen) == 0) {
+            fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+            if (result1 != SDP_SUCCESS) {
+                fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+                if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "annex_n");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            tok = tmp;
+            tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > USHRT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "annex_n", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+            fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->annex_n_val = (u16) strtoul_result;
+            codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[40].name,
+                               sdp_fmtp_codec_param[40].strlen) == 0) {
+            fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+            if (result1 != SDP_SUCCESS) {
+                fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+                if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "annex_p");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            fmtp_p->annex_p_val_picture_resize = 0;
+            fmtp_p->annex_p_val_warp = 0;
+            tok = tmp;
+            tok++; temp=PL_strtok_r(tok, ",", &strtok_state);
+            if (temp) {
+                iter=1;
+                while (temp != NULL) {
+                    errno = 0;
+                    strtoul_result = strtoul(temp, &strtoul_end, 10);
+
+                    if (errno || temp == strtoul_end || strtoul_result > USHRT_MAX) {
+                        break;
+                    }
+
+                    if (iter == 1)
+                        fmtp_p->annex_p_val_picture_resize = (u16) strtoul_result;
+                    else if (iter == 2)
+                        fmtp_p->annex_p_val_warp = (u16) strtoul_result;
+
+                    temp=PL_strtok_r(NULL, ",", &strtok_state);
+                    iter++;
+                }
+            }
+
+            fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[42].name,
+                               sdp_fmtp_codec_param[42].strlen) == 0) {
+            fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+            if (result1 != SDP_SUCCESS) {
+                fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+                if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "level_asymmetry_allowed");
+                    SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            tok = tmp;
+            tok++;
+
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end || strtoul_result > SDP_MAX_LEVEL_ASYMMETRY_ALLOWED_VALUE) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "level_asymmetry_allowed", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+            fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+            fmtp_p->level_asymmetry_allowed = (int) strtoul_result;
+            codec_info_found = TRUE;
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[43].name,
+                                   sdp_fmtp_codec_param[43].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+                    fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "maxaveragebitrate");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            tok = tmp;
+           tok++;
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end || strtoul_result == 0 || strtoul_result > UINT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "maxaveragebitrate", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->maxaveragebitrate = (u32) strtoul_result;
+           codec_info_found = TRUE;
+
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[44].name,
+                                   sdp_fmtp_codec_param[44].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+                    fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "usedtx");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            tok = tmp;
+           tok++;
+            errno = 0;
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result > 1) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "usedtx", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->usedtx = (u16) strtoul_result;
+           codec_info_found = TRUE;
+
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[45].name,
+                                   sdp_fmtp_codec_param[45].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+                    fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "stereo");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            tok = tmp;
+           tok++;
+            errno = 0;
+
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result > 1) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "stereo", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->stereo = (u16) strtoul_result;
+           codec_info_found = TRUE;
+
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[46].name,
+                                   sdp_fmtp_codec_param[46].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+                    fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "useinbandfec");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            tok = tmp;
+           tok++;
+            errno = 0;
+
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result > 1) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "useinbandfec", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->useinbandfec = (u16) strtoul_result;
+           codec_info_found = TRUE;
+
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[47].name,
+                                       sdp_fmtp_codec_param[47].strlen) == 0) {
+                   fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+                   if (result1 != SDP_SUCCESS) {
+                       fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+                       if (result1 != SDP_SUCCESS) {
+                            sdp_attr_fmtp_no_value(sdp_p, "maxcodedaudiobandwidth");
+                            sdp_p->conf_p->num_invalid_param++;
+                           SDP_FREE(temp_ptr);
+                            return SDP_INVALID_PARAMETER;
+                       }
+                   }
+                   tok = tmp;
+                   tok++;
+                   fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+                    sstrncpy(fmtp_p->maxcodedaudiobandwidth , tok, sizeof(fmtp_p->maxcodedaudiobandwidth));
+                   codec_info_found = TRUE;
+
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[48].name,
+                                   sdp_fmtp_codec_param[48].strlen) == 0) {
+           fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+           if (result1 != SDP_SUCCESS) {
+                fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+               if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "cbr");
+                   SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            tok = tmp;
+           tok++;
+            errno = 0;
+
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+           if (errno || tok == strtoul_end || strtoul_result > 1) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "cbr", tok);
+               SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+           }
+           fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+           fmtp_p->cbr = (u16) strtoul_result;
+           codec_info_found = TRUE;
+
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[49].name,
+                        sdp_fmtp_codec_param[49].strlen) == 0) {
+            fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+            if (result1 != SDP_SUCCESS) {
+                fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+                if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "streams");
+                    SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                }
+            }
+            tok = tmp;
+            tok++;
+            errno = 0;
+
+            strtoul_result = strtoul(tok, &strtoul_end, 10);
+
+            if (errno || tok == strtoul_end || strtoul_result > INT_MAX) {
+                sdp_attr_fmtp_invalid_value(sdp_p, "streams", tok);
+                SDP_FREE(temp_ptr);
+                return SDP_INVALID_PARAMETER;
+            }
+
+            fmtp_p->fmtp_format = SDP_FMTP_DATACHANNEL;
+            fmtp_p->streams = (int) strtoul_result;
+            codec_info_found = TRUE;
+
+        } else if (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[50].name,
+                sdp_fmtp_codec_param[50].strlen) == 0) {
+            fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t", &result1);
+            if (result1 != SDP_SUCCESS) {
+                fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
+                if (result1 != SDP_SUCCESS) {
+                    sdp_attr_fmtp_no_value(sdp_p, "protocol");
+                    SDP_FREE(temp_ptr);
+                    return SDP_INVALID_PARAMETER;
+                 }
+             }
+             tok = tmp;
+             tok++;
+             fmtp_p->fmtp_format = SDP_FMTP_DATACHANNEL;
+             sstrncpy(fmtp_p->protocol , tok, sizeof(fmtp_p->protocol));
+                        codec_info_found = TRUE;
+
+        } else if (fmtp_ptr != NULL && *fmtp_ptr == '\n') {
+            temp=PL_strtok_r(tmp, ";", &strtok_state);
+            if (temp) {
+                if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+                    SDP_PRINT("%s Annexes are possibly there for this fmtp %s  tmp: %s line\n",
+                              sdp_p->debug_str, fmtp_ptr, tmp);
+                }
+                while (temp != NULL) {
+                    if (strchr(temp, 'D') !=NULL) {
+                        attr_p->attr.fmtp.annex_d = TRUE;
+                    }
+                    if (strchr(temp, 'F') !=NULL) {
+                        attr_p->attr.fmtp.annex_f = TRUE;
+                    }
+                    if (strchr(temp, 'I') !=NULL) {
+                        attr_p->attr.fmtp.annex_i = TRUE;
+                    }
+                    if (strchr(temp, 'J') !=NULL) {
+                        attr_p->attr.fmtp.annex_j = TRUE;
+                    }
+                    if (strchr(temp, 'T') !=NULL) {
+                        attr_p->attr.fmtp.annex_t = TRUE;
+                    }
+                    temp=PL_strtok_r(NULL, ";", &strtok_state);
+                }
+            } /* if (temp) */
+            done = TRUE;
+        }
+        fmtp_ptr++;
+      } else {
+          done = TRUE;
+      }
+    } /* while  - done loop*/
+
+    if (codec_info_found) {
+
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Parsed a=%s, payload type %u, bitrate %lu, mode %u QCIF = %u, CIF = %u, MAXBR= %u, SQCIF=%u, CIF4= %u, CIF16=%u, CUSTOM=%u,%u,%u , PAR=%u:%u,CPCF=%u, BPP=%u, HRD=%u \n",
+                      sdp_p->debug_str,
+                      sdp_get_attr_name(attr_p->type),
+                      attr_p->attr.fmtp.payload_num,
+                      attr_p->attr.fmtp.bitrate,
+                      attr_p->attr.fmtp.mode,
+                      attr_p->attr.fmtp.qcif,
+                      attr_p->attr.fmtp.cif,
+                      attr_p->attr.fmtp.maxbr,
+                      attr_p->attr.fmtp.sqcif,
+                      attr_p->attr.fmtp.cif4,
+                      attr_p->attr.fmtp.cif16,
+                      attr_p->attr.fmtp.custom_x,attr_p->attr.fmtp.custom_y,
+                      attr_p->attr.fmtp.custom_mpi,
+                      attr_p->attr.fmtp.par_width,
+                      attr_p->attr.fmtp.par_height,
+                      attr_p->attr.fmtp.cpcf,
+                      attr_p->attr.fmtp.bpp,
+                      attr_p->attr.fmtp.hrd
+                     );
+        }
+
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Parsed a=%s, payload type %u,PROFILE=%u,LEVEL=%u, INTERLACE - %s",
+                      sdp_p->debug_str,
+                      sdp_get_attr_name(attr_p->type),
+                      attr_p->attr.fmtp.payload_num,
+                      attr_p->attr.fmtp.profile,
+                      attr_p->attr.fmtp.level,
+                      attr_p->attr.fmtp.is_interlace ? "YES":"NO");
+        }
+
+       if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Parsed H.264 attributes: profile-level-id=%s, parameter-sets=%s, packetization-mode=%d level-asymmetry-allowed=%d interleaving-depth=%d deint-buf-req=%lu max-don-diff=%lu, init_buf-time=%lu\n",
+                      sdp_p->debug_str,
+                      attr_p->attr.fmtp.profile_level_id,
+                      attr_p->attr.fmtp.parameter_sets,
+                      attr_p->attr.fmtp.packetization_mode,
+                      attr_p->attr.fmtp.level_asymmetry_allowed,
+                      attr_p->attr.fmtp.interleaving_depth,
+                      attr_p->attr.fmtp.deint_buf_req,
+                      attr_p->attr.fmtp.max_don_diff,
+                      attr_p->attr.fmtp.init_buf_time
+                     );
+        }
+
+       if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("\n%s Parsed H.264 opt attributes: max-mbps=%lu, max-fs=%lu, max-cpb=%lu max-dpb=%lu max-br=%lu redundant-pic-cap=%d, deint-buf-cap=%lu, max-rcmd-nalu-size=%lu , parameter-add=%d\n",
+                      sdp_p->debug_str,
+                      attr_p->attr.fmtp.max_mbps,
+                      attr_p->attr.fmtp.max_fs,
+                      attr_p->attr.fmtp.max_cpb,
+                      attr_p->attr.fmtp.max_dpb,
+                      attr_p->attr.fmtp.max_br,
+                      attr_p->attr.fmtp.redundant_pic_cap,
+                      attr_p->attr.fmtp.deint_buf_cap,
+                      attr_p->attr.fmtp.max_rcmd_nalu_size,
+                      attr_p->attr.fmtp.parameter_add);
+
+        }
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Parsed annexes are : D=%d F=%d I=%d J=%d T=%d, K=%d N=%d P=%d,%d\n",
+                      sdp_p->debug_str,
+                      attr_p->attr.fmtp.annex_d,
+                      attr_p->attr.fmtp.annex_f,  attr_p->attr.fmtp.annex_i,
+                      attr_p->attr.fmtp.annex_j,  attr_p->attr.fmtp.annex_t,
+                      attr_p->attr.fmtp.annex_k_val,
+                     attr_p->attr.fmtp.annex_n_val,
+                      attr_p->attr.fmtp.annex_p_val_picture_resize,
+                      attr_p->attr.fmtp.annex_p_val_warp);
+
+        }
+       SDP_FREE(temp_ptr);
+        return (SDP_SUCCESS);
+    } else {
+        done = FALSE;
+       fmtp_ptr = src_ptr;
+        tmp[0] = '\0';
+    }
+
+    for (i=0; !done; i++) {
+        fmtp_p->fmtp_format = SDP_FMTP_NTE;
+        /* Look for comma separated events */
+        fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), ", \t", &result1);
+        if (result1 != SDP_SUCCESS) {
+            done = TRUE;
+            continue;
+        }
+        /* Now look for '-' separated range */
+        ptr2 = tmp;
+        low_val = (u8)sdp_getnextnumtok(ptr2, (const char **)&ptr2,
+                                    "- \t", &result1);
+        if (*ptr2 == '-') {
+            high_val = (u8)sdp_getnextnumtok(ptr2, (const char **)&ptr2,
+                                         "- \t", &result2);
+        } else {
+            high_val = low_val;
+        }
+
+        if ((result1 != SDP_SUCCESS) || (result2 != SDP_SUCCESS)) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: Invalid named events specified for fmtp attribute.",
+                sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+           SDP_FREE(temp_ptr);
+            return (SDP_INVALID_PARAMETER);
+        }
+
+        for (i = low_val; i <= high_val; i++) {
+            mapword = i/SDP_NE_BITS_PER_WORD;
+            bmap = SDP_NE_BIT_0 << (i%32);
+            fmtp_p->bmap[mapword] |= bmap;
+        }
+        if (high_val > fmtp_p->maxval) {
+            fmtp_p->maxval = high_val;
+        }
+    }
+
+    if (fmtp_p->maxval == 0) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No named events specified for fmtp attribute.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+       SDP_FREE(temp_ptr);
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, payload type %u, ", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  attr_p->attr.fmtp.payload_num);
+    }
+    SDP_FREE(temp_ptr);
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs)
+{
+  u16         event_id;
+  u32         mask;
+  u32         mapword;
+  u8          min = 0;
+  u8          max = 0;
+  tinybool    range_start = FALSE;
+  tinybool    range_end = FALSE;
+  tinybool    semicolon = FALSE;
+  sdp_fmtp_t *fmtp_p;
+
+  flex_string_sprintf(fs, "a=%s:%u ",
+    sdp_attr[attr_p->type].name,
+    attr_p->attr.fmtp.payload_num);
+
+  fmtp_p = &(attr_p->attr.fmtp);
+  switch (fmtp_p->fmtp_format) {
+    case SDP_FMTP_MODE:
+      sdp_append_name_and_unsigned(fs, "mode", fmtp_p->mode, FALSE);
+      break;
+
+    case SDP_FMTP_CODEC_INFO:
+      FMTP_BUILD_UNSIGNED(fmtp_p->bitrate > 0, "bitrate", fmtp_p->bitrate)
+
+      FMTP_BUILD_STRING(fmtp_p->annexa_required,
+        "annexa", (fmtp_p->annexa ? "yes" : "no"))
+
+      FMTP_BUILD_STRING(fmtp_p->annexb_required,
+        "annexb", (fmtp_p->annexa ? "yes" : "no"))
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->qcif > 0, "QCIF", fmtp_p->qcif)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->cif > 0, "CIF", fmtp_p->cif)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->maxbr > 0, "MAXBR", fmtp_p->maxbr)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->sqcif > 0, "SQCIF", fmtp_p->sqcif)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->cif4 > 0, "CIF4", fmtp_p->cif4)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->cif16 > 0, "CIF16", fmtp_p->cif16)
+
+      if ((fmtp_p->custom_x > 0) && (fmtp_p->custom_y > 0) &&
+        (fmtp_p->custom_mpi > 0)) {
+        flex_string_sprintf(fs, "%sCUSTOM=%u,%u,%u",
+          semicolon ? ";" : "",
+          fmtp_p->custom_x,
+          fmtp_p->custom_y,
+          fmtp_p->custom_mpi);
+
+        semicolon = TRUE;
+      }
+
+      if ((fmtp_p->par_height > 0) && (fmtp_p->par_width > 0)) {
+        flex_string_sprintf(fs, "%sPAR=%u:%u",
+          semicolon ? ";" : "",
+          fmtp_p->par_width,
+          fmtp_p->par_width);
+
+        semicolon = TRUE;
+      }
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->cpcf > 0, "CPCF", fmtp_p->cpcf)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->bpp > 0, "BPP", fmtp_p->bpp)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->hrd > 0, "HRD", fmtp_p->hrd)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->profile >= 0, "PROFILE", fmtp_p->profile)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->level >= 0, "LEVEL", fmtp_p->level)
+
+      FMTP_BUILD_FLAG(fmtp_p->is_interlace, "INTERLACE")
+
+      FMTP_BUILD_FLAG(fmtp_p->annex_d, "D")
+
+      FMTP_BUILD_FLAG(fmtp_p->annex_f, "F")
+
+      FMTP_BUILD_FLAG(fmtp_p->annex_i, "I")
+
+      FMTP_BUILD_FLAG(fmtp_p->annex_j, "J")
+
+      FMTP_BUILD_FLAG(fmtp_p->annex_t, "T")
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->annex_k_val > 0,
+        "K", fmtp_p->annex_k_val)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->annex_n_val > 0,
+        "N", fmtp_p->annex_n_val)
+
+      if ((fmtp_p->annex_p_val_picture_resize > 0) &&
+        (fmtp_p->annex_p_val_warp > 0)) {
+        flex_string_sprintf(fs, "%sP=%d:%d",
+          semicolon ? ";" : "",
+          fmtp_p->annex_p_val_picture_resize,
+          fmtp_p->annex_p_val_warp);
+
+        semicolon = TRUE;
+      }
+
+      FMTP_BUILD_STRING(strlen(fmtp_p->profile_level_id) > 0,
+        "profile-level-id", fmtp_p->profile_level_id)
+
+      FMTP_BUILD_STRING(strlen(fmtp_p->parameter_sets) > 0,
+        "sprop-parameter-sets", fmtp_p->parameter_sets)
+
+      FMTP_BUILD_UNSIGNED(
+        fmtp_p->packetization_mode < SDP_MAX_PACKETIZATION_MODE_VALUE,
+        "packetization-mode", fmtp_p->packetization_mode)
+
+      FMTP_BUILD_UNSIGNED(
+        fmtp_p->level_asymmetry_allowed <=
+        SDP_MAX_LEVEL_ASYMMETRY_ALLOWED_VALUE,
+        "level-asymmetry-allowed", fmtp_p->level_asymmetry_allowed)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->interleaving_depth > 0,
+        "sprop-interleaving-depth", fmtp_p->interleaving_depth)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->flag & SDP_DEINT_BUF_REQ_FLAG,
+        "sprop-deint-buf-req", fmtp_p->deint_buf_req)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->max_don_diff > 0,
+        "sprop-max-don-diff", fmtp_p->max_don_diff)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->flag & SDP_INIT_BUF_TIME_FLAG,
+        "sprop-init-buf-time", fmtp_p->init_buf_time)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->max_mbps > 0,
+        "max-mbps", fmtp_p->max_mbps)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->max_fs > 0, "max-fs", fmtp_p->max_fs)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->max_cpb > 0, "max-cpb", fmtp_p->max_cpb)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->max_dpb > 0, "max-dpb", fmtp_p->max_dpb)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->max_br > 0, "max-br", fmtp_p->max_br)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->redundant_pic_cap > 0,
+        "redundant-pic-cap", fmtp_p->redundant_pic_cap)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->flag & SDP_DEINT_BUF_CAP_FLAG,
+        "deint-buf-cap", fmtp_p->deint_buf_cap)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->flag & SDP_MAX_RCMD_NALU_SIZE_FLAG,
+        "max-rcmd-naFMTP_BUILD_FLlu-size", fmtp_p->max_rcmd_nalu_size)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->parameter_add > 0,
+        "parameter-add", fmtp_p->parameter_add)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->maxaveragebitrate > 0,
+        "maxaveragebitrate", fmtp_p->maxaveragebitrate)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->usedtx <= 1, "usedtx", fmtp_p->usedtx)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->stereo <= 1, "stereo", fmtp_p->stereo)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->useinbandfec <= 1,
+        "useinbandfec", fmtp_p->useinbandfec)
+
+      FMTP_BUILD_STRING(strlen(fmtp_p->maxcodedaudiobandwidth) > 0,
+        "maxcodedaudiobandwidth", fmtp_p->maxcodedaudiobandwidth)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->cbr <= 1, "cbr", fmtp_p->cbr)
+
+      break;
+
+    case SDP_FMTP_DATACHANNEL:
+      FMTP_BUILD_STRING(strlen(fmtp_p->protocol) > 0,
+        "protocol", fmtp_p->protocol)
+
+      FMTP_BUILD_UNSIGNED(fmtp_p->streams > 0, "streams", fmtp_p->streams)
+
+      break;
+
+    case SDP_FMTP_NTE:
+    default:
+      break;
+  }
+
+     for(event_id = 0, mapword = 0, mask = SDP_NE_BIT_0;
+         event_id <= fmtp_p->maxval;
+         event_id++, mapword = event_id/SDP_NE_BITS_PER_WORD ) {
+
+         if (event_id % SDP_NE_BITS_PER_WORD) {
+             mask <<= 1;
+         } else {
+         /* crossed a bitmap word boundary */
+         mask = SDP_NE_BIT_0;
+             if (!range_start && !range_end && !fmtp_p->bmap[mapword]) {
+           /* no events in this word, skip to the last event id
+             * in this bitmap word. */
+                event_id += SDP_NE_BITS_PER_WORD - 1;
+                continue;
+            }
+         }
+
+        if (fmtp_p->bmap[mapword] & mask) {
+            if (!range_start) {
+                range_start = TRUE;
+                min = max = (u8)event_id;
+            } else {
+                max = (u8)event_id;
+            }
+        range_end = (max == fmtp_p->maxval);
+        } else {
+        /* If we were in the middle of a range, then we've hit the
+         * end.  If we weren't, there is no end to hit. */
+            range_end = range_start;
+        }
+
+        /* If this is the end of the range, print it to the string. */
+        if (range_end) {
+            range_start = range_end = FALSE;
+
+            flex_string_sprintf(fs, "%u", min);
+
+            if (min != max) {
+              flex_string_sprintf(fs, "-%u", max);
+            }
+
+            if (max != fmtp_p->maxval) {
+              flex_string_append(fs, ",");
+            }
+        }
+    }
+
+    flex_string_append(fs, "\r\n");
+
+    return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_direction (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                       const char *ptr)
+{
+    /* No parameters to parse. */
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type));
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_direction (sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s\r\n", sdp_get_attr_name(attr_p->type));
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_qos (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                 const char *ptr)
+{
+    int i;
+    sdp_result_e result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    /* Find the strength tag. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No qos strength tag specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.qos.strength = SDP_QOS_STRENGTH_UNKNOWN;
+    for (i=0; i < SDP_MAX_QOS_STRENGTH; i++) {
+        if (cpr_strncasecmp(tmp, sdp_qos_strength[i].name,
+                        sdp_qos_strength[i].strlen) == 0) {
+            attr_p->attr.qos.strength = (sdp_qos_strength_e)i;
+        }
+    }
+    if (attr_p->attr.qos.strength == SDP_QOS_STRENGTH_UNKNOWN) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: QOS strength tag unrecognized (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the qos direction. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No qos direction specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.qos.direction = SDP_QOS_DIR_UNKNOWN;
+    for (i=0; i < SDP_MAX_QOS_DIR; i++) {
+        if (cpr_strncasecmp(tmp, sdp_qos_direction[i].name,
+                        sdp_qos_direction[i].strlen) == 0) {
+            attr_p->attr.qos.direction = (sdp_qos_dir_e)i;
+        }
+    }
+    if (attr_p->attr.qos.direction == SDP_QOS_DIR_UNKNOWN) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: QOS direction unrecognized (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* See if confirm was specified.  Defaults to FALSE. */
+    attr_p->attr.qos.confirm = FALSE;
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result == SDP_SUCCESS) {
+        if (cpr_strncasecmp(tmp, "confirm", sizeof("confirm")) == 0) {
+            attr_p->attr.qos.confirm = TRUE;
+        }
+        if (attr_p->attr.qos.confirm == FALSE) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: QOS confirm parameter invalid (%s)",
+                sdp_p->debug_str, tmp);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, strength %s, direction %s, confirm %s",
+                  sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
+                  sdp_get_qos_strength_name(attr_p->attr.qos.strength),
+                  sdp_get_qos_direction_name(attr_p->attr.qos.direction),
+                  (attr_p->attr.qos.confirm ? "set" : "not set"));
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_qos (sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s %s%s\r\n", sdp_attr[attr_p->type].name,
+    sdp_get_qos_strength_name(attr_p->attr.qos.strength),
+    sdp_get_qos_direction_name(attr_p->attr.qos.direction),
+    attr_p->attr.qos.confirm ? " confirm" : "");
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_curr (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                 const char *ptr)
+{
+    int i;
+    sdp_result_e result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    /* Find the curr type tag. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No curr attr type specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.curr.type = SDP_CURR_UNKNOWN_TYPE;
+    for (i=0; i < SDP_MAX_CURR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_curr_type[i].name,
+                        sdp_curr_type[i].strlen) == 0) {
+            attr_p->attr.curr.type = (sdp_curr_type_e)i;
+        }
+    }
+
+    if (attr_p->attr.curr.type != SDP_CURR_QOS_TYPE) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Unknown curr type.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Check qos status type */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+     if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No curr attr type specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.curr.status_type = SDP_QOS_STATUS_TYPE_UNKNOWN;
+    for (i=0; i < SDP_MAX_QOS_STATUS_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_qos_status_type[i].name,
+                        sdp_qos_status_type[i].strlen) == 0) {
+            attr_p->attr.curr.status_type = (sdp_qos_status_types_e)i;
+        }
+    }
+
+
+    /* Find the qos direction. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No qos direction specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.curr.direction = SDP_QOS_DIR_UNKNOWN;
+    for (i=0; i < SDP_MAX_QOS_DIR; i++) {
+        if (cpr_strncasecmp(tmp, sdp_qos_direction[i].name,
+                        sdp_qos_direction[i].strlen) == 0) {
+            attr_p->attr.curr.direction = (sdp_qos_dir_e)i;
+        }
+    }
+    if (attr_p->attr.curr.direction == SDP_QOS_DIR_UNKNOWN) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: QOS direction unrecognized (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, type %s status type %s, direction %s",
+                  sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
+                  sdp_get_curr_type_name(attr_p->attr.curr.type),
+                  sdp_get_qos_status_type_name(attr_p->attr.curr.status_type),
+                  sdp_get_qos_direction_name(attr_p->attr.curr.direction));
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_curr (sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s %s %s\r\n",
+    sdp_attr[attr_p->type].name,
+    sdp_get_curr_type_name(attr_p->attr.curr.type),
+    sdp_get_qos_status_type_name(attr_p->attr.curr.status_type),
+    sdp_get_qos_direction_name(attr_p->attr.curr.direction));
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_des (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                 const char *ptr)
+{
+    int i;
+    sdp_result_e result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    /* Find the curr type tag. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No des attr type specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.des.type = SDP_DES_UNKNOWN_TYPE;
+    for (i=0; i < SDP_MAX_CURR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_des_type[i].name,
+                        sdp_des_type[i].strlen) == 0) {
+            attr_p->attr.des.type = (sdp_des_type_e)i;
+        }
+    }
+
+    if (attr_p->attr.des.type != SDP_DES_QOS_TYPE) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Unknown conf type.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the strength tag. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No qos strength tag specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.des.strength = SDP_QOS_STRENGTH_UNKNOWN;
+    for (i=0; i < SDP_MAX_QOS_STRENGTH; i++) {
+        if (cpr_strncasecmp(tmp, sdp_qos_strength[i].name,
+                        sdp_qos_strength[i].strlen) == 0) {
+            attr_p->attr.des.strength = (sdp_qos_strength_e)i;
+        }
+    }
+    if (attr_p->attr.des.strength == SDP_QOS_STRENGTH_UNKNOWN) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: QOS strength tag unrecognized (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Check qos status type */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+     if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No des attr type specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.des.status_type = SDP_QOS_STATUS_TYPE_UNKNOWN;
+    for (i=0; i < SDP_MAX_QOS_STATUS_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_qos_status_type[i].name,
+                        sdp_qos_status_type[i].strlen) == 0) {
+            attr_p->attr.des.status_type = (sdp_qos_status_types_e)i;
+        }
+    }
+
+
+    /* Find the qos direction. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No qos direction specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.des.direction = SDP_QOS_DIR_UNKNOWN;
+    for (i=0; i < SDP_MAX_QOS_DIR; i++) {
+        if (cpr_strncasecmp(tmp, sdp_qos_direction[i].name,
+                        sdp_qos_direction[i].strlen) == 0) {
+            attr_p->attr.des.direction = (sdp_qos_dir_e)i;
+        }
+    }
+    if (attr_p->attr.des.direction == SDP_QOS_DIR_UNKNOWN) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: QOS direction unrecognized (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, type %s strength %s status type %s, direction %s",
+                  sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
+                  sdp_get_des_type_name(attr_p->attr.des.type),
+                  sdp_get_qos_strength_name(attr_p->attr.qos.strength),
+                  sdp_get_qos_status_type_name(attr_p->attr.des.status_type),
+                  sdp_get_qos_direction_name(attr_p->attr.des.direction));
+    }
+
+    return (SDP_SUCCESS);
+}
+
+
+sdp_result_e sdp_build_attr_des (sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s %s %s %s\r\n",
+    sdp_attr[attr_p->type].name,
+    sdp_get_curr_type_name((sdp_curr_type_e)attr_p->attr.des.type),
+    sdp_get_qos_strength_name(attr_p->attr.des.strength),
+    sdp_get_qos_status_type_name(attr_p->attr.des.status_type),
+    sdp_get_qos_direction_name(attr_p->attr.des.direction));
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_conf (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                 const char *ptr)
+{
+    int i;
+    sdp_result_e result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    /* Find the curr type tag. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No conf attr type specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.conf.type = SDP_CONF_UNKNOWN_TYPE;
+    for (i=0; i < SDP_MAX_CURR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_conf_type[i].name,
+                        sdp_conf_type[i].strlen) == 0) {
+            attr_p->attr.conf.type = (sdp_conf_type_e)i;
+        }
+    }
+
+    if (attr_p->attr.conf.type != SDP_CONF_QOS_TYPE) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Unknown conf type.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Check qos status type */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+     if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No conf attr type specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.conf.status_type = SDP_QOS_STATUS_TYPE_UNKNOWN;
+    for (i=0; i < SDP_MAX_QOS_STATUS_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_qos_status_type[i].name,
+                        sdp_qos_status_type[i].strlen) == 0) {
+            attr_p->attr.conf.status_type = (sdp_qos_status_types_e)i;
+        }
+    }
+
+
+    /* Find the qos direction. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No qos direction specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.conf.direction = SDP_QOS_DIR_UNKNOWN;
+    for (i=0; i < SDP_MAX_QOS_DIR; i++) {
+        if (cpr_strncasecmp(tmp, sdp_qos_direction[i].name,
+                        sdp_qos_direction[i].strlen) == 0) {
+            attr_p->attr.conf.direction = (sdp_qos_dir_e)i;
+        }
+    }
+    if (attr_p->attr.conf.direction == SDP_QOS_DIR_UNKNOWN) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: QOS direction unrecognized (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, type %s status type %s, direction %s",
+                  sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
+                  sdp_get_conf_type_name(attr_p->attr.conf.type),
+                  sdp_get_qos_status_type_name(attr_p->attr.conf.status_type),
+                  sdp_get_qos_direction_name(attr_p->attr.conf.direction));
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_conf (sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s %s %s\r\n",
+    sdp_attr[attr_p->type].name,
+    sdp_get_conf_type_name(attr_p->attr.conf.type),
+    sdp_get_qos_status_type_name(attr_p->attr.conf.status_type),
+    sdp_get_qos_direction_name(attr_p->attr.conf.direction));
+
+  return SDP_SUCCESS;
+}
+
+/*
+ *  Parse a rtpmap or a sprtmap. Both formats use the same structure
+ *  the only difference being the keyword "rtpmap" vs "sprtmap". The
+ *  rtpmap field in the sdp_attr_t is used to store both mappings.
+ */
+sdp_result_e sdp_parse_attr_transport_map (sdp_t *sdp_p, sdp_attr_t *attr_p,
+       const char *ptr)
+{
+    sdp_result_e  result;
+
+    attr_p->attr.transport_map.payload_num = 0;
+    attr_p->attr.transport_map.encname[0]  = '\0';
+    attr_p->attr.transport_map.clockrate   = 0;
+    attr_p->attr.transport_map.num_chan    = 1;
+
+    /* Find the payload type number. */
+    attr_p->attr.transport_map.payload_num =
+    (u16)sdp_getnextnumtok(ptr, &ptr, " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Invalid payload type specified for %s attribute.",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the encoding name. */
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.transport_map.encname,
+                            sizeof(attr_p->attr.transport_map.encname), "/ \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No encoding name specified in %s attribute.",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the clockrate. */
+    attr_p->attr.transport_map.clockrate =
+       sdp_getnextnumtok(ptr, &ptr, "/ \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No clockrate specified for "
+            "%s attribute, set to default of 8000.",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        attr_p->attr.transport_map.clockrate = 8000;
+    }
+
+    /* Find the number of channels, if specified. This is optional. */
+    if (*ptr == '/') {
+        /* If a '/' exists, expect something valid beyond it. */
+        attr_p->attr.transport_map.num_chan =
+           (u16)sdp_getnextnumtok(ptr, &ptr, "/ \t", &result);
+        if (result != SDP_SUCCESS) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: Invalid number of channels parameter"
+                " for rtpmap attribute.", sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, payload type %u, encoding name %s, "
+                  "clockrate %lu", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  attr_p->attr.transport_map.payload_num,
+                  attr_p->attr.transport_map.encname,
+                  attr_p->attr.transport_map.clockrate);
+        if (attr_p->attr.transport_map.num_chan != 1) {
+            SDP_PRINT("/%u", attr_p->attr.transport_map.num_chan);
+        }
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/*
+ *  Build a rtpmap or a sprtmap. Both formats use the same structure
+ *  the only difference being the keyword "rtpmap" vs "sprtmap". The
+ *  rtpmap field in the sdp_attr_t is used for both mappings.
+ */
+sdp_result_e sdp_build_attr_transport_map (sdp_t *sdp_p, sdp_attr_t *attr_p,
+       flex_string *fs)
+{
+  if (attr_p->attr.transport_map.num_chan == 1) {
+    flex_string_sprintf(fs, "a=%s:%u %s/%u\r\n",
+      sdp_attr[attr_p->type].name,
+      attr_p->attr.transport_map.payload_num,
+      attr_p->attr.transport_map.encname,
+      attr_p->attr.transport_map.clockrate);
+  } else {
+    flex_string_sprintf(fs, "a=%s:%u %s/%u/%u\r\n",
+      sdp_attr[attr_p->type].name,
+      attr_p->attr.transport_map.payload_num,
+      attr_p->attr.transport_map.encname,
+      attr_p->attr.transport_map.clockrate,
+      attr_p->attr.transport_map.num_chan);
+  }
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_subnet (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                    const char *ptr)
+{
+    int i;
+    char         *slash_ptr;
+    sdp_result_e  result;
+    tinybool      type_found = FALSE;
+    char          tmp[SDP_MAX_STRING_LEN];
+
+    /* Find the subnet network type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No network type specified in subnet attribute.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.subnet.nettype = SDP_NT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_NETWORK_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_nettype[i].name,
+                        sdp_nettype[i].strlen) == 0) {
+            type_found = TRUE;
+        }
+        if (type_found == TRUE) {
+            if (sdp_p->conf_p->nettype_supported[i] == TRUE) {
+                attr_p->attr.subnet.nettype = (sdp_nettype_e)i;
+            }
+            type_found = FALSE;
+        }
+    }
+    if (attr_p->attr.subnet.nettype == SDP_NT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Subnet network type unsupported (%s).",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the subnet address type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No address type specified in subnet attribute.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.subnet.addrtype = SDP_AT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_ADDR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_addrtype[i].name,
+                        sdp_addrtype[i].strlen) == 0) {
+            type_found = TRUE;
+        }
+        if (type_found == TRUE) {
+            if (sdp_p->conf_p->addrtype_supported[i] == TRUE) {
+                attr_p->attr.subnet.addrtype = (sdp_addrtype_e)i;
+            }
+            type_found = FALSE;
+        }
+    }
+    if (attr_p->attr.subnet.addrtype == SDP_AT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Subnet address type unsupported (%s).",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the subnet address.  */
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.subnet.addr,
+                            sizeof(attr_p->attr.subnet.addr), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No subnet address specified in "
+            "subnet attribute.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    slash_ptr = sdp_findchar(attr_p->attr.subnet.addr, "/");
+    if (*slash_ptr == '/') {
+        *slash_ptr++ = '\0';
+        /* If the '/' exists, expect a valid prefix to follow. */
+        attr_p->attr.subnet.prefix = sdp_getnextnumtok(slash_ptr,
+                                                  (const char **)&slash_ptr,
+                                                  " \t", &result);
+        if (result != SDP_SUCCESS) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: Invalid subnet prefix specified in "
+                "subnet attribute.", sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+    } else {
+        attr_p->attr.subnet.prefix = SDP_INVALID_VALUE;
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, network %s, addr type %s, address %s ",
+                  sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
+                  sdp_get_network_name(attr_p->attr.subnet.nettype),
+                  sdp_get_address_name(attr_p->attr.subnet.addrtype),
+                  attr_p->attr.subnet.addr);
+        if (attr_p->attr.subnet.prefix != SDP_INVALID_VALUE) {
+            SDP_PRINT("/%u", (ushort)attr_p->attr.subnet.prefix);
+        }
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_subnet (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                    flex_string *fs)
+{
+  if (attr_p->attr.subnet.prefix == SDP_INVALID_VALUE) {
+    flex_string_sprintf(fs, "a=%s:%s %s %s\r\n",
+      sdp_attr[attr_p->type].name,
+      sdp_get_network_name(attr_p->attr.subnet.nettype),
+      sdp_get_address_name(attr_p->attr.subnet.addrtype),
+      attr_p->attr.subnet.addr);
+  } else {
+    flex_string_sprintf(fs, "a=%s:%s %s %s/%u\r\n",
+      sdp_attr[attr_p->type].name,
+      sdp_get_network_name(attr_p->attr.subnet.nettype),
+      sdp_get_address_name(attr_p->attr.subnet.addrtype),
+      attr_p->attr.subnet.addr,
+      (ushort)attr_p->attr.subnet.prefix);
+  }
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_t38_ratemgmt (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                          const char *ptr)
+{
+    int i;
+    sdp_result_e result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    /* Find the rate mgmt. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No t38 rate management specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.t38ratemgmt = SDP_T38_UNKNOWN_RATE;
+    for (i=0; i < SDP_T38_MAX_RATES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_t38_rate[i].name,
+                        sdp_t38_rate[i].strlen) == 0) {
+            attr_p->attr.t38ratemgmt = (sdp_t38_ratemgmt_e)i;
+        }
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, rate %s", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  sdp_get_t38_ratemgmt_name(attr_p->attr.t38ratemgmt));
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_t38_ratemgmt (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                          flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s\r\n",
+    sdp_attr[attr_p->type].name,
+    sdp_get_t38_ratemgmt_name(attr_p->attr.t38ratemgmt));
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_t38_udpec (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                       const char *ptr)
+{
+    int i;
+    sdp_result_e result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    /* Find the udpec. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No t38 udpEC specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.t38udpec = SDP_T38_UDPEC_UNKNOWN;
+    for (i=0; i < SDP_T38_MAX_UDPEC; i++) {
+        if (cpr_strncasecmp(tmp, sdp_t38_udpec[i].name,
+                        sdp_t38_udpec[i].strlen) == 0) {
+            attr_p->attr.t38udpec = (sdp_t38_udpec_e)i;
+        }
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, udpec %s", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  sdp_get_t38_udpec_name(attr_p->attr.t38udpec));
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_t38_udpec (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                       flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s\r\n",
+    sdp_attr[attr_p->type].name,
+    sdp_get_t38_udpec_name(attr_p->attr.t38udpec));
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_pc_codec (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                      const char *ptr)
+{
+    u16 i;
+    sdp_result_e result;
+
+    for (i=0; i < SDP_MAX_PAYLOAD_TYPES; i++) {
+        attr_p->attr.pccodec.payload_type[i] = (ushort)sdp_getnextnumtok(ptr, &ptr,
+                                                               " \t", &result);
+        if (result != SDP_SUCCESS) {
+            break;
+        }
+        attr_p->attr.pccodec.num_payloads++;
+    }
+
+    if (attr_p->attr.pccodec.num_payloads == 0) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No payloads specified for %s attr.",
+            sdp_p->debug_str, sdp_attr[attr_p->type].name);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, num payloads %u, payloads: ",
+                  sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
+                  attr_p->attr.pccodec.num_payloads);
+        for (i=0; i < attr_p->attr.pccodec.num_payloads; i++) {
+            SDP_PRINT("%u ", attr_p->attr.pccodec.payload_type[i]);
+        }
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_pc_codec (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                      flex_string *fs)
+{
+  int i;
+
+  flex_string_sprintf(fs, "a=%s: ", sdp_attr[attr_p->type].name);
+
+  for (i=0; i < attr_p->attr.pccodec.num_payloads; i++) {
+    flex_string_sprintf(fs, "%u ", attr_p->attr.pccodec.payload_type[i]);
+  }
+
+  flex_string_append(fs, "\r\n");
+
+  return SDP_SUCCESS;
+}
+
+
+sdp_result_e sdp_parse_attr_cap (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                 const char *ptr)
+{
+    u16           i;
+    sdp_result_e  result;
+    sdp_mca_t    *cap_p;
+    char          tmp[SDP_MAX_STRING_LEN];
+
+    /* Set the capability pointer to NULL for now in case we encounter
+     * an error in parsing.
+     */
+    attr_p->attr.cap_p = NULL;
+    /* Set the capability valid flag to FALSE in case we encounter an
+     * error.  If we do, we don't want to process any X-cpar/cpar attributes
+     * from this point until we process the next valid X-cap/cdsc attr. */
+    sdp_p->cap_valid = FALSE;
+
+    /* Allocate resource for new capability. Note that the capability
+     * uses the same structure used for media lines.
+     */
+    cap_p = sdp_alloc_mca();
+    if (cap_p == NULL) {
+        sdp_p->conf_p->num_no_resource++;
+        return (SDP_NO_RESOURCE);
+    }
+
+    /* Find the capability number. We don't need to store this since we
+     * calculate it for ourselves as we need to. But it must be specified. */
+    (void)sdp_getnextnumtok(ptr, &ptr, "/ \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Capability not specified for %s, "
+            "unable to parse.", sdp_p->debug_str,
+            sdp_get_attr_name(attr_p->type));
+        SDP_FREE(cap_p);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the media type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No media type specified for %s attribute, "
+            "unable to parse.",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        SDP_FREE(cap_p);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    cap_p->media = SDP_MEDIA_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_MEDIA_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_media[i].name, sdp_media[i].strlen) == 0) {
+            cap_p->media = (sdp_media_e)i;
+            break;
+        }
+    }
+    if (cap_p->media == SDP_MEDIA_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Media type unsupported (%s).",
+            sdp_p->debug_str, tmp);
+        SDP_FREE(cap_p);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the transport protocol type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No transport protocol type specified, "
+            "unable to parse.", sdp_p->debug_str);
+        SDP_FREE(cap_p);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    cap_p->transport = SDP_TRANSPORT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_TRANSPORT_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_transport[i].name,
+                        sdp_transport[i].strlen) == 0) {
+            cap_p->transport = (sdp_transport_e)i;
+            break;
+        }
+    }
+    if (cap_p->transport == SDP_TRANSPORT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Transport protocol type unsupported (%s).",
+            sdp_p->debug_str, tmp);
+        SDP_FREE(cap_p);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find payload formats. AAL2 X-cap lines allow multiple
+     * transport/profile types per line, so these are handled differently.
+     */
+    if ((cap_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
+        (cap_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
+        (cap_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) {
+        /* Capability processing is not currently defined for AAL2 types
+         * with multiple profiles. We don't process. */
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: AAL2 profiles unsupported with "
+            "%s attributes.", sdp_p->debug_str,
+            sdp_get_attr_name(attr_p->type));
+        SDP_FREE(cap_p);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        /* Transport is a non-AAL2 type.  Parse payloads normally. */
+        sdp_parse_payload_types(sdp_p, cap_p, ptr);
+        if (cap_p->num_payloads == 0) {
+            SDP_FREE(cap_p);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+    }
+
+    attr_p->attr.cap_p = cap_p;
+    /*
+     * This capability attr is valid.  We can now handle X-cpar or
+     * cpar attrs.
+     */
+    sdp_p->cap_valid = TRUE;
+    sdp_p->last_cap_inst++;
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed %s media type %s, Transport %s, "
+                  "Num payloads %u", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  sdp_get_media_name(cap_p->media),
+                  sdp_get_transport_name(cap_p->transport),
+                  cap_p->num_payloads);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_cap (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                 flex_string *fs)
+{
+    u16                   i, j;
+    sdp_mca_t            *cap_p;
+    sdp_media_profiles_t *profile_p;
+
+    /* Get a pointer to the capability structure. */
+    cap_p = attr_p->attr.cap_p;
+
+    if (cap_p == NULL) {
+        CSFLogError(logTag, "%s Invalid %s attribute, unable to build.",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        /* Return success so build won't fail. */
+        return (SDP_SUCCESS);
+    }
+
+    /* Validate params for this capability line */
+    if ((cap_p->media >= SDP_MAX_MEDIA_TYPES) ||
+        (cap_p->transport >= SDP_MAX_TRANSPORT_TYPES)) {
+        CSFLogDebug(logTag, logTag, "%s Media or transport type invalid for %s "
+            "attribute, unable to build.", sdp_p->debug_str,
+                       sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        /* Return success so build won't fail. */
+        return (SDP_SUCCESS);
+    }
+
+    flex_string_sprintf(fs, "a=%s: %u %s ", sdp_attr[attr_p->type].name,
+                     sdp_p->cur_cap_num, sdp_get_media_name(cap_p->media));
+
+    /* If the X-cap line has AAL2 profiles, build them differently. */
+    if ((cap_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
+        (cap_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
+        (cap_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) {
+        profile_p = cap_p->media_profiles_p;
+        for (i=0; i < profile_p->num_profiles; i++) {
+            flex_string_sprintf(fs, "%s",
+                             sdp_get_transport_name(profile_p->profile[i]));
+
+            for (j=0; j < profile_p->num_payloads[i]; j++) {
+                flex_string_sprintf(fs, " %u",
+                                 profile_p->payload_type[i][j]);
+            }
+            flex_string_append(fs, " ");
+        }
+
+        flex_string_append(fs, "\r\n");
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Built m= media line", sdp_p->debug_str);
+        }
+        return SDP_SUCCESS;
+    }
+
+    /* Build the transport name */
+    flex_string_sprintf(fs, "%s", sdp_get_transport_name(cap_p->transport));
+
+    /* Build the format lists */
+    for (i=0; i < cap_p->num_payloads; i++) {
+        if (cap_p->payload_indicator[i] == SDP_PAYLOAD_ENUM) {
+            flex_string_sprintf(fs, " %s",
+                             sdp_get_payload_name((sdp_payload_e)cap_p->payload_type[i]));
+        } else {
+            flex_string_sprintf(fs, " %u", cap_p->payload_type[i]);
+        }
+    }
+
+    flex_string_append(fs, "\r\n");
+
+    /* Increment the current capability number for the next X-cap/cdsc attr. */
+    sdp_p->cur_cap_num += cap_p->num_payloads;
+    sdp_p->last_cap_type = attr_p->type;
+
+    /* Build any X-cpar/cpar attributes associated with this X-cap/cdsc line. */
+    return sdp_build_attr_cpar(sdp_p, cap_p->media_attrs_p, fs);
+}
+
+
+sdp_result_e sdp_parse_attr_cpar (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                  const char *ptr)
+{
+    u16           i;
+    sdp_result_e  result;
+    sdp_mca_t    *cap_p;
+    sdp_attr_t   *cap_attr_p = NULL;
+    sdp_attr_t   *prev_attr_p;
+    char          tmp[SDP_MAX_STRING_LEN];
+
+    /* Make sure we've processed a valid X-cap/cdsc attr prior to this and
+     * if so, get the cap pointer. */
+    if (sdp_p->cap_valid == TRUE) {
+       sdp_attr_e cap_type;
+
+       if (attr_p->type == SDP_ATTR_CPAR) {
+           cap_type = SDP_ATTR_CDSC;
+       } else {
+           /* Default to X-CAP for everything else */
+           cap_type = SDP_ATTR_X_CAP;
+       }
+
+        if (sdp_p->mca_count == 0) {
+            cap_attr_p = sdp_find_attr(sdp_p, SDP_SESSION_LEVEL, 0,
+                                       cap_type, sdp_p->last_cap_inst);
+        } else {
+            cap_attr_p = sdp_find_attr(sdp_p, sdp_p->mca_count, 0,
+                                       cap_type, sdp_p->last_cap_inst);
+        }
+    }
+    if ((cap_attr_p == NULL) || (cap_attr_p->attr.cap_p == NULL)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: %s attribute specified with no "
+            "prior %s attribute", sdp_p->debug_str,
+                        sdp_get_attr_name(attr_p->type),
+                        (attr_p->type == SDP_ATTR_CPAR)?
+                              (sdp_get_attr_name(SDP_ATTR_CDSC)) :
+                              (sdp_get_attr_name(SDP_ATTR_X_CAP)) );
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /*
+     * Ensure there is no mixed syntax like CDSC followed by X-CPAR
+     * or X-CAP followed by CPAR.
+     */
+    if (((cap_attr_p->type == SDP_ATTR_CDSC) &&
+        (attr_p->type == SDP_ATTR_X_CPAR)) ||
+        ( (cap_attr_p->type == SDP_ATTR_X_CAP) &&
+          (attr_p->type == SDP_ATTR_CPAR)) ) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: %s attribute inconsistent with "
+            "prior %s attribute", sdp_p->debug_str,
+            sdp_get_attr_name(attr_p->type),
+            sdp_get_attr_name(cap_attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    cap_p = cap_attr_p->attr.cap_p;
+
+    /* a= is the only token we handle in an X-cpar/cpar attribute. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), "= \t", &result);
+
+    if ((result != SDP_SUCCESS) || (tmp[0] != 'a') || (tmp[1] != '\0')) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Invalid token type (%s) in %s "
+            "attribute, unable to parse", sdp_p->debug_str, tmp,
+            sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    /*sa_ignore NO_NULL_CHK
+     *{ptr is valid since the pointer was checked earlier and the
+     * function would have exited if NULL.}
+     */
+    if (*ptr == '=') {
+        ptr++;
+    }
+
+    /* Find the attribute type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), ": \t", &result);
+    /*sa_ignore NO_NULL_CHK
+     *{ptr is valid since the pointer was checked earlier and the
+     * function would have exited if NULL.}
+     */
+    if (ptr[0] == ':') {
+        /* Skip the ':' char for parsing attribute parameters. */
+        ptr++;
+    }
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No attribute type specified for %s attribute, unable to parse.",
+            sdp_p->debug_str,
+            sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Reset the type of the attribute from X-cpar/cpar to whatever the
+     * specified type is. */
+    attr_p->type = SDP_ATTR_INVALID;
+    attr_p->next_p = NULL;
+    for (i=0; i < SDP_MAX_ATTR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_attr[i].name, sdp_attr[i].strlen) == 0) {
+            attr_p->type = (sdp_attr_e)i;
+        }
+    }
+    if (attr_p->type == SDP_ATTR_INVALID) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Unrecognized attribute (%s) for %s attribute, unable to parse.",
+            sdp_p->debug_str, tmp,
+            sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* We don't allow recursion with the capability attributes. */
+    if ((attr_p->type == SDP_ATTR_X_SQN) ||
+        (attr_p->type == SDP_ATTR_X_CAP) ||
+        (attr_p->type == SDP_ATTR_X_CPAR) ||
+        (attr_p->type == SDP_ATTR_SQN) ||
+        (attr_p->type == SDP_ATTR_CDSC) ||
+        (attr_p->type == SDP_ATTR_CPAR)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Invalid attribute (%s) for %s"
+            " attribute, unable to parse.", sdp_p->debug_str, tmp,
+            sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Parse the attribute. */
+    result = sdp_attr[attr_p->type].parse_func(sdp_p, attr_p, ptr);
+    if (result != SDP_SUCCESS) {
+        return (result);
+    }
+
+    /* Hook the attribute into the capability structure. */
+    if (cap_p->media_attrs_p == NULL) {
+        cap_p->media_attrs_p = attr_p;
+    } else {
+        for (prev_attr_p = cap_p->media_attrs_p;
+             prev_attr_p->next_p != NULL;
+             prev_attr_p = prev_attr_p->next_p) {
+            ; /* Empty for */
+        }
+        prev_attr_p->next_p = attr_p;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_cpar (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                  flex_string *fs)
+{
+    sdp_result_e  result;
+    const char  *cpar_name;
+
+    /* Determine whether to use cpar or X-cpar */
+    if (sdp_p->last_cap_type == SDP_ATTR_CDSC) {
+       cpar_name = sdp_get_attr_name(SDP_ATTR_CPAR);
+    } else {
+       /*
+        * Default to X-CPAR if anything else. This is the backward
+        * compatible value.
+        */
+       cpar_name = sdp_get_attr_name(SDP_ATTR_X_CPAR);
+    }
+
+    while (attr_p != NULL) {
+        if (attr_p->type >= SDP_MAX_ATTR_TYPES) {
+            CSFLogDebug(logTag, "%s Invalid attribute type to build (%u)",
+                sdp_p->debug_str, attr_p->type);
+        } else {
+            flex_string_sprintf(fs, "a=%s: ", cpar_name);
+
+            result = sdp_attr[attr_p->type].build_func(sdp_p, attr_p, fs);
+
+            if (result == SDP_SUCCESS) {
+                if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+                    SDP_PRINT("%s Built %s a=%s attribute line",
+                              sdp_p->debug_str, cpar_name,
+                              sdp_get_attr_name(attr_p->type));
+                }
+            }
+        }
+        attr_p = attr_p->next_p;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_attr_rtr (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                           const char *ptr)
+{
+    sdp_result_e  result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsing a=%s, %s", sdp_p->debug_str,
+                     sdp_get_attr_name(attr_p->type),
+                     tmp);
+    }
+    /*Default confirm to FALSE. */
+    attr_p->attr.rtr.confirm = FALSE;
+
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS){ // No confirm tag specified is not an error
+        return (SDP_SUCCESS);
+    } else {
+       /* See if confirm was specified.  Defaults to FALSE. */
+       if (cpr_strncasecmp(tmp, "confirm", sizeof("confirm")) == 0) {
+           attr_p->attr.rtr.confirm = TRUE;
+       }
+       if (attr_p->attr.rtr.confirm == FALSE) {
+          sdp_parse_error(sdp_p->peerconnection,
+              "%s Warning: RTR confirm parameter invalid (%s)",
+              sdp_p->debug_str, tmp);
+           sdp_p->conf_p->num_invalid_param++;
+           return (SDP_INVALID_PARAMETER);
+       }
+       if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+           SDP_PRINT("%s Parsed a=%s, %s", sdp_p->debug_str,
+                     sdp_get_attr_name(attr_p->type),
+                     tmp);
+       }
+       return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_build_attr_rtr (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                           flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s%s\r\n",
+    sdp_attr[attr_p->type].name,
+    attr_p->attr.rtr.confirm ? ":confirm" : "");
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_comediadir (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                        const char *ptr)
+{
+    int i;
+    sdp_result_e  result;
+    tinybool      type_found = FALSE;
+    char          tmp[SDP_MAX_STRING_LEN];
+
+    attr_p->attr.comediadir.role = SDP_MEDIADIR_ROLE_PASSIVE;
+    attr_p->attr.comediadir.conn_info_present = FALSE;
+    attr_p->attr.comediadir.conn_info.nettype = SDP_NT_INVALID;
+    attr_p->attr.comediadir.src_port = 0;
+
+    /* Find the media direction role. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), ": \t", &result);
+
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No role parameter specified for "
+            "comediadir attribute.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.comediadir.role = SDP_MEDIADIR_ROLE_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_MEDIADIR_ROLES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_mediadir_role[i].name,
+                        sdp_mediadir_role[i].strlen) == 0) {
+            type_found = TRUE;
+            attr_p->attr.comediadir.role = (sdp_mediadir_role_e)i;
+            break;
+        }
+    }
+    if (attr_p->attr.comediadir.role == SDP_MEDIADIR_ROLE_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Invalid role type specified for "
+            "comediadir attribute (%s).", sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* If the role is passive, we don't expect any more params. */
+    if (attr_p->attr.comediadir.role == SDP_MEDIADIR_ROLE_PASSIVE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Parsed a=%s, passive",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        }
+        return (SDP_SUCCESS);
+    }
+
+    /* Find the connection information if present */
+    /* parse to get the nettype */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No network type specified in comediadir "
+            "attribute.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_SUCCESS); /* as the optional parameters are not there */
+    }
+    attr_p->attr.comediadir.conn_info.nettype = SDP_NT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_NETWORK_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_nettype[i].name,
+                        sdp_nettype[i].strlen) == 0) {
+            type_found = TRUE;
+        }
+        if (type_found == TRUE) {
+            if (sdp_p->conf_p->nettype_supported[i] == TRUE) {
+                attr_p->attr.comediadir.conn_info.nettype = (sdp_nettype_e)i;
+            }
+            type_found = FALSE;
+        }
+    }
+    if (attr_p->attr.comediadir.conn_info.nettype == SDP_NT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: ConnInfo in Comediadir: network type "
+            "unsupported (%s).", sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+    }
+
+    /* Find the comedia address type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No address type specified in comediadir"
+            " attribute.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+    }
+    attr_p->attr.comediadir.conn_info.addrtype = SDP_AT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_ADDR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_addrtype[i].name,
+                        sdp_addrtype[i].strlen) == 0) {
+            type_found = TRUE;
+        }
+        if (type_found == TRUE) {
+            if (sdp_p->conf_p->addrtype_supported[i] == TRUE) {
+                attr_p->attr.comediadir.conn_info.addrtype = (sdp_addrtype_e)i;
+            }
+            type_found = FALSE;
+        }
+    }
+    if (attr_p->attr.comediadir.conn_info.addrtype == SDP_AT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Conninfo address type unsupported "
+            "(%s).", sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+    }
+
+    /* Find the conninfo address.  */
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.comediadir.conn_info.conn_addr,
+                            sizeof(attr_p->attr.comediadir.conn_info.conn_addr), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No conninfo address specified in "
+            "comediadir attribute.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+    }
+
+    /* Find the src port info , if any */
+    attr_p->attr.comediadir.src_port  = sdp_getnextnumtok(ptr, &ptr, " \t",
+                                                          &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No src port specified in "
+            "comediadir attribute.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, network %s, addr type %s, address %s "
+                  "srcport %u ",
+                  sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
+                  sdp_get_network_name(attr_p->attr.comediadir.conn_info.nettype),
+                  sdp_get_address_name(attr_p->attr.comediadir.conn_info.addrtype),
+                  attr_p->attr.comediadir.conn_info.conn_addr,
+                  (unsigned int)attr_p->attr.comediadir.src_port);
+    }
+
+    if (sdp_p->conf_p->num_invalid_param > 0) {
+        return (SDP_INVALID_PARAMETER);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e
+sdp_build_attr_comediadir (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                    flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s\r\n",
+    sdp_attr[attr_p->type].name,
+    sdp_get_mediadir_role_name(attr_p->attr.comediadir.role));
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_silencesupp (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                         const char *ptr)
+{
+    int i;
+    sdp_result_e result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    /* Find silenceSuppEnable */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No silenceSupp enable value specified, parse failed.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (cpr_strncasecmp(tmp, "on", sizeof("on")) == 0) {
+        attr_p->attr.silencesupp.enabled = TRUE;
+    } else if (cpr_strncasecmp(tmp, "off", sizeof("off")) == 0) {
+        attr_p->attr.silencesupp.enabled = FALSE;
+    } else if (cpr_strncasecmp(tmp, "-", sizeof("-")) == 0) {
+        attr_p->attr.silencesupp.enabled = FALSE;
+    } else {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: silenceSuppEnable parameter invalid (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find silenceTimer -- u16 or "-" */
+
+    attr_p->attr.silencesupp.timer =
+        (u16)sdp_getnextnumtok_or_null(ptr, &ptr, " \t",
+                                       &attr_p->attr.silencesupp.timer_null,
+                                       &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Invalid timer value specified for "
+            "silenceSupp attribute.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find suppPref */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No silenceSupp pref specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.silencesupp.pref = SDP_SILENCESUPP_PREF_UNKNOWN;
+    for (i=0; i < SDP_MAX_SILENCESUPP_PREF; i++) {
+        if (cpr_strncasecmp(tmp, sdp_silencesupp_pref[i].name,
+                        sdp_silencesupp_pref[i].strlen) == 0) {
+            attr_p->attr.silencesupp.pref = (sdp_silencesupp_pref_e)i;
+        }
+    }
+    if (attr_p->attr.silencesupp.pref == SDP_SILENCESUPP_PREF_UNKNOWN) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: silenceSupp pref unrecognized (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find sidUse */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No silenceSupp sidUse specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.silencesupp.siduse = SDP_SILENCESUPP_SIDUSE_UNKNOWN;
+    for (i=0; i < SDP_MAX_SILENCESUPP_SIDUSE; i++) {
+        if (cpr_strncasecmp(tmp, sdp_silencesupp_siduse[i].name,
+                        sdp_silencesupp_siduse[i].strlen) == 0) {
+            attr_p->attr.silencesupp.siduse = (sdp_silencesupp_siduse_e)i;
+        }
+    }
+    if (attr_p->attr.silencesupp.siduse == SDP_SILENCESUPP_SIDUSE_UNKNOWN) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: silenceSupp sidUse unrecognized (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find fxnslevel -- u8 or "-" */
+    attr_p->attr.silencesupp.fxnslevel =
+        (u8)sdp_getnextnumtok_or_null(ptr, &ptr, " \t",
+                                      &attr_p->attr.silencesupp.fxnslevel_null,
+                                      &result);
+
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Invalid fxnslevel value specified for "
+            "silenceSupp attribute.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, enabled %s",
+                  sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
+                  (attr_p->attr.silencesupp.enabled ? "on" : "off"));
+        if (attr_p->attr.silencesupp.timer_null) {
+            SDP_PRINT(" timer=-");
+        } else {
+            SDP_PRINT(" timer=%u,", attr_p->attr.silencesupp.timer);
+        }
+        SDP_PRINT(" pref=%s, siduse=%s,",
+                  sdp_get_silencesupp_pref_name(attr_p->attr.silencesupp.pref),
+                  sdp_get_silencesupp_siduse_name(
+                                             attr_p->attr.silencesupp.siduse));
+        if (attr_p->attr.silencesupp.fxnslevel_null) {
+            SDP_PRINT(" fxnslevel=-");
+        } else {
+            SDP_PRINT(" fxnslevel=%u,", attr_p->attr.silencesupp.fxnslevel);
+        }
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_silencesupp (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                         flex_string *fs)
+{
+  char temp_timer_string[11];
+  char temp_fxnslevel_string[11];
+
+  if (attr_p->attr.silencesupp.timer_null) {
+    snprintf(temp_timer_string, sizeof(temp_timer_string), "-");
+  } else {
+    snprintf(temp_timer_string, sizeof(temp_timer_string), "%u", attr_p->attr.silencesupp.timer);
+  }
+
+  if (attr_p->attr.silencesupp.fxnslevel_null) {
+    snprintf(temp_fxnslevel_string, sizeof(temp_fxnslevel_string), "-");
+  } else {
+    snprintf(temp_fxnslevel_string, sizeof(temp_fxnslevel_string), "%u", attr_p->attr.silencesupp.fxnslevel);
+  }
+
+  flex_string_sprintf(fs, "a=%s:%s %s %s %s %s\r\n",
+    sdp_attr[attr_p->type].name,
+    (attr_p->attr.silencesupp.enabled ? "on" : "off"),
+    temp_timer_string,
+    sdp_get_silencesupp_pref_name(attr_p->attr.silencesupp.pref),
+    sdp_get_silencesupp_siduse_name(attr_p->attr.silencesupp.siduse),
+    temp_fxnslevel_string);
+
+  return SDP_SUCCESS;
+}
+
+/*
+ * sdp_parse_context_crypto_suite
+ *
+ * This routine parses the crypto suite pointed to by str, stores the crypto suite value into the
+ * srtp context suite component of the LocalConnectionOptions pointed to by lco_node_ptr and stores
+ * pointer to the next crypto parameter in tmp_ptr
+ */
+tinybool sdp_parse_context_crypto_suite(char * str,  sdp_attr_t *attr_p, sdp_t *sdp_p) {
+      /*
+       * Three crypto_suites are defined: (Notice no SPACE between "crypto:" and the <crypto-suite>
+       * AES_CM_128_HMAC_SHA1_80
+       * AES_CM_128_HMAC_SHA1_32
+       * F8_128_HMAC_SHA1_80
+       */
+
+       int i;
+
+       /* Check crypto suites */
+       for(i=0; i<SDP_SRTP_MAX_NUM_CRYPTO_SUITES; i++) {
+        if (!strcasecmp(sdp_srtp_crypto_suite_array[i].crypto_suite_str, str)) {
+          attr_p->attr.srtp_context.suite = sdp_srtp_crypto_suite_array[i].crypto_suite_val;
+          attr_p->attr.srtp_context.master_key_size_bytes =
+              sdp_srtp_crypto_suite_array[i].key_size_bytes;
+          attr_p->attr.srtp_context.master_salt_size_bytes =
+              sdp_srtp_crypto_suite_array[i].salt_size_bytes;
+          return TRUE; /* There is a succesful match so exit */
+        }
+       }
+       /* couldn't find a matching crypto suite */
+       sdp_parse_error(sdp_p->peerconnection,
+            "%s No Matching crypto suite for SRTP Context(%s)-'X-crypto:v1' expected",
+            sdp_p->debug_str, str);
+
+       return FALSE;
+}
+
+
+sdp_result_e sdp_build_attr_srtpcontext (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                    flex_string *fs)
+{
+#define MAX_BASE64_ENCODE_SIZE_BYTES 60
+    int          output_len = MAX_BASE64_ENCODE_SIZE_BYTES;
+    int                 key_size = attr_p->attr.srtp_context.master_key_size_bytes;
+    int                 salt_size = attr_p->attr.srtp_context.master_salt_size_bytes;
+    unsigned char  base64_encoded_data[MAX_BASE64_ENCODE_SIZE_BYTES];
+    unsigned char  base64_encoded_input[MAX_BASE64_ENCODE_SIZE_BYTES];
+    base64_result_t status;
+
+    output_len = MAX_BASE64_ENCODE_SIZE_BYTES;
+
+    /* Append master and salt keys */
+    bcopy(attr_p->attr.srtp_context.master_key, base64_encoded_input,
+           key_size );
+    bcopy(attr_p->attr.srtp_context.master_salt,
+           base64_encoded_input + key_size, salt_size );
+
+    if ((status = base64_encode(base64_encoded_input, key_size + salt_size,
+                     base64_encoded_data, &output_len)) != BASE64_SUCCESS) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Error: Failure to Base64 Encoded data (%s) ",
+                     sdp_p->debug_str, BASE64_RESULT_TO_STRING(status));
+        }
+       return (SDP_INVALID_PARAMETER);
+    }
+
+    *(base64_encoded_data + output_len) = '\0';
+
+    flex_string_sprintf(fs, "a=%s:%s inline:%s||\r\n",
+      sdp_attr[attr_p->type].name,
+      sdp_srtp_context_crypto_suite[attr_p->attr.srtp_context.suite].name,
+      base64_encoded_data);
+
+    return SDP_SUCCESS;
+}
+
+/*
+ * sdp_parse_attr_mptime
+ * This function parses the a=mptime sdp line. This parameter consists of
+ * one or more numbers or hyphens ("-"). The first parameter must be a
+ * number. The number of parameters must match the number of formats specified
+ * on the m= line. This function is liberal in that it does not match against
+ * the m= line or require a number for the first parameter.
+ */
+sdp_result_e sdp_parse_attr_mptime (
+    sdp_t *sdp_p,
+    sdp_attr_t *attr_p,
+    const char *ptr)
+{
+    u16 i;                      /* loop counter for parameters */
+    sdp_result_e result;        /* value returned by this function */
+    tinybool null_ind;          /* true if a parameter is "-" */
+
+    /*
+     * Scan the input line up to the maximum number of parameters supported.
+     * Look for numbers or hyphens and store the resulting values. Hyphens
+     * are stored as zeros.
+     */
+    for (i=0; i<SDP_MAX_PAYLOAD_TYPES; i++) {
+        attr_p->attr.mptime.intervals[i] =
+            (ushort)sdp_getnextnumtok_or_null(ptr,&ptr," \t",&null_ind,&result);
+        if (result != SDP_SUCCESS) {
+            break;
+        }
+        attr_p->attr.mptime.num_intervals++;
+    }
+
+    /*
+     * At least one parameter must be supplied. If not, return an error
+     * and optionally log the failure.
+     */
+    if (attr_p->attr.mptime.num_intervals == 0) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No intervals specified for %s attr.",
+            sdp_p->debug_str, sdp_attr[attr_p->type].name);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /*
+     * Here is some debugging code that helps us track what data
+     * is received and parsed.
+     */
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, num intervals %u, intervals: ",
+                  sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
+                  attr_p->attr.mptime.num_intervals);
+        for (i=0; i < attr_p->attr.mptime.num_intervals; i++) {
+            SDP_PRINT("%u ", attr_p->attr.mptime.intervals[i]);
+        }
+    }
+
+    return SDP_SUCCESS;
+}
+
+/*
+ * sdp_build_attr_mptime
+ * This function builds the a=mptime sdp line. It reads the selected attribute
+ * from the sdp structure. Parameters with a value of zero are replaced by
+ * hyphens.
+ */
+sdp_result_e sdp_build_attr_mptime (
+    sdp_t *sdp_p,
+    sdp_attr_t *attr_p,
+    flex_string *fs)
+{
+  int i;
+
+  flex_string_sprintf(fs, "a=%s:", sdp_attr[attr_p->type].name);
+
+  /*
+   * Run the list of mptime parameter values and write each one
+   * to the sdp line. Replace zeros with hyphens.
+   */
+  for (i=0; i < attr_p->attr.mptime.num_intervals; i++) {
+    if (i > 0) {
+      flex_string_append(fs, " ");
+    }
+
+    if (attr_p->attr.mptime.intervals[i] == 0) {
+      flex_string_append(fs, "-");
+    } else {
+      flex_string_sprintf(fs, "%u", attr_p->attr.mptime.intervals[i]);
+    }
+  }
+
+  flex_string_append(fs, "\r\n");
+
+  return SDP_SUCCESS;
+}
+
+
+
+sdp_result_e sdp_parse_attr_x_sidin (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr)
+{
+    sdp_result_e  result;
+    attr_p->attr.stream_data.x_sidin[0]  = '\0';
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsing a=%s", sdp_p->debug_str,
+                     sdp_get_attr_name(attr_p->type));
+    }
+
+    /* Find the X-sidin value */
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.stream_data.x_sidin,
+                            sizeof(attr_p->attr.stream_data.x_sidin), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No Stream Id incoming specified for X-sidin attribute.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, %s", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  attr_p->attr.stream_data.x_sidin);
+    }
+   return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_x_sidin (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                      flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s\r\n",
+    sdp_attr[attr_p->type].name,
+    attr_p->attr.stream_data.x_sidin);
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_x_sidout (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                      const char *ptr)
+{
+    sdp_result_e  result;
+    attr_p->attr.stream_data.x_sidout[0]  = '\0';
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsing a=%s", sdp_p->debug_str,
+                     sdp_get_attr_name(attr_p->type));
+    }
+
+    /* Find the X-sidout value */
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.stream_data.x_sidout,
+                            sizeof(attr_p->attr.stream_data.x_sidout), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No Stream Id outgoing specified for X-sidout attribute.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, %s", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  attr_p->attr.stream_data.x_sidout);
+    }
+   return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_x_sidout (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                      flex_string *fs)
+{
+  flex_string_sprintf(fs, "a=%s:%s\r\n",
+    sdp_attr[attr_p->type].name,
+    attr_p->attr.stream_data.x_sidout);
+
+  return SDP_SUCCESS;
+}
+
+
+sdp_result_e sdp_parse_attr_x_confid (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                      const char *ptr)
+{
+    sdp_result_e  result;
+    attr_p->attr.stream_data.x_confid[0]  = '\0';
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsing a=%s", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type));
+    }
+
+    /* Find the X-confid value */
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.stream_data.x_confid,
+                            sizeof(attr_p->attr.stream_data.x_confid), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No Conf Id incoming specified for "
+            "X-confid attribute.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, %s", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  attr_p->attr.stream_data.x_confid);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_x_confid (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                      flex_string *fs)
+{
+  if (strlen(attr_p->attr.stream_data.x_confid) <= 0) {
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+      SDP_PRINT("%s X-confid value is not set. Cannot build a=X-confid line\n",
+        sdp_p->debug_str);
+    }
+
+    return SDP_INVALID_PARAMETER;
+  }
+
+  flex_string_sprintf(fs, "a=%s:%s\r\n",
+    sdp_attr[attr_p->type].name,
+    attr_p->attr.stream_data.x_confid);
+
+  return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_group (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                   const char *ptr)
+{
+    sdp_result_e  result;
+    char  tmp[10];
+    int i=0;
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsing a=%s", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type));
+    }
+
+    /* Find the a=group:<attrib> <id1> < id2> ... values */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No group attribute value specified for "
+            "a=group line", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p->attr.stream_data.group_attr = SDP_GROUP_ATTR_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_GROUP_ATTR_VAL; i++) {
+        if (cpr_strncasecmp(tmp, sdp_group_attr_val[i].name,
+                        sdp_group_attr_val[i].strlen) == 0) {
+            attr_p->attr.stream_data.group_attr = (sdp_group_attr_e)i;
+            break;
+        }
+    }
+
+    if (attr_p->attr.stream_data.group_attr == SDP_GROUP_ATTR_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Group attribute type unsupported (%s).",
+            sdp_p->debug_str, tmp);
+    }
+
+
+    /*
+     * Scan the input line up after group:<attr>  to the maximum number
+     * of id available.
+     */
+    attr_p->attr.stream_data.num_group_id =0;
+
+    for (i=0; i<SDP_MAX_GROUP_STREAM_ID; i++) {
+        attr_p->attr.stream_data.group_id_arr[i] =
+            (u16)sdp_getnextnumtok(ptr,&ptr," \t", &result);
+        if (result != SDP_SUCCESS) {
+            break;
+        }
+        attr_p->attr.stream_data.num_group_id++;
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s:%s\n", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  sdp_get_group_attr_name (attr_p->attr.stream_data.group_attr));
+        for (i=0; i < attr_p->attr.stream_data.num_group_id; i++) {
+            SDP_PRINT("%s Parsed group line id : %d\n", sdp_p->debug_str,
+                      attr_p->attr.stream_data.group_id_arr[i]);
+        }
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_group (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                   flex_string *fs)
+{
+  int i;
+
+  flex_string_sprintf(fs, "a=%s:%s",
+    sdp_attr[attr_p->type].name,
+    sdp_get_group_attr_name(attr_p->attr.stream_data.group_attr));
+
+  for (i=0; i < attr_p->attr.stream_data.num_group_id; i++) {
+    if (attr_p->attr.stream_data.group_id_arr[i] > 0) {
+      flex_string_sprintf(fs, " %u",
+        attr_p->attr.stream_data.group_id_arr[i]);
+    }
+  }
+
+  flex_string_append(fs, "\r\n");
+
+  return SDP_SUCCESS;
+}
+
+/* Parse the source-filter attribute
+ * "a=source-filter:<filter-mode><filter-spec>"
+ *  <filter-spec> = <nettype><addrtype><dest-addr><src_addr><src_addr>...
+ */
+sdp_result_e sdp_parse_attr_source_filter (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                           const char *ptr)
+{
+    int i;
+    sdp_result_e result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    attr_p->attr.source_filter.mode = SDP_FILTER_MODE_NOT_PRESENT;
+    attr_p->attr.source_filter.nettype = SDP_NT_UNSUPPORTED;
+    attr_p->attr.source_filter.addrtype = SDP_AT_UNSUPPORTED;
+    attr_p->attr.source_filter.dest_addr[0] = '\0';
+    attr_p->attr.source_filter.num_src_addr = 0;
+
+    /* Find the filter mode */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No src filter attribute value specified for "
+                     "a=source-filter line", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    for (i = 0; i < SDP_MAX_FILTER_MODE; i++) {
+        if (cpr_strncasecmp(tmp, sdp_src_filter_mode_val[i].name,
+                        sdp_src_filter_mode_val[i].strlen) == 0) {
+            attr_p->attr.source_filter.mode = (sdp_src_filter_mode_e)i;
+            break;
+        }
+    }
+    if (attr_p->attr.source_filter.mode == SDP_FILTER_MODE_NOT_PRESENT) {
+        /* No point continuing */
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Invalid src filter mode for a=source-filter "
+            "line", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the network type */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    for (i = 0; i < SDP_MAX_NETWORK_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_nettype[i].name,
+                        sdp_nettype[i].strlen) == 0) {
+            if (sdp_p->conf_p->nettype_supported[i] == TRUE) {
+                attr_p->attr.source_filter.nettype = (sdp_nettype_e)i;
+            }
+        }
+    }
+    if (attr_p->attr.source_filter.nettype == SDP_NT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Network type unsupported "
+            "(%s) for a=source-filter", sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the address type */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    for (i = 0; i < SDP_MAX_ADDR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_addrtype[i].name,
+                        sdp_addrtype[i].strlen) == 0) {
+            if (sdp_p->conf_p->addrtype_supported[i] == TRUE) {
+                attr_p->attr.source_filter.addrtype = (sdp_addrtype_e)i;
+            }
+        }
+    }
+    if (attr_p->attr.source_filter.addrtype == SDP_AT_UNSUPPORTED) {
+        if (strncmp(tmp, "*", 1) == 0) {
+            attr_p->attr.source_filter.addrtype = SDP_AT_FQDN;
+        } else {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: Address type unsupported "
+                "(%s) for a=source-filter", sdp_p->debug_str, tmp);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+    }
+
+    /* Find the destination addr */
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.source_filter.dest_addr,
+                            sizeof(attr_p->attr.source_filter.dest_addr), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No filter destination address specified for "
+            "a=source-filter", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the list of source address to apply the filter */
+    for (i = 0; i < SDP_MAX_SRC_ADDR_LIST; i++) {
+        ptr = sdp_getnextstrtok(ptr, attr_p->attr.source_filter.src_list[i],
+                                sizeof(attr_p->attr.source_filter.src_list[i]), " \t", &result);
+        if (result != SDP_SUCCESS) {
+            break;
+        }
+        attr_p->attr.source_filter.num_src_addr++;
+    }
+    if (attr_p->attr.source_filter.num_src_addr == 0) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No source list provided "
+            "for a=source-filter", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_source_filter (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                      flex_string *fs)
+{
+  int i;
+
+  flex_string_sprintf(fs, "a=%s:%s %s %s %s",
+    sdp_get_attr_name(attr_p->type),
+    sdp_get_src_filter_mode_name(attr_p->attr.source_filter.mode),
+    sdp_get_network_name(attr_p->attr.source_filter.nettype),
+    sdp_get_address_name(attr_p->attr.source_filter.addrtype),
+    attr_p->attr.source_filter.dest_addr);
+
+  for (i = 0; i < attr_p->attr.source_filter.num_src_addr; i++) {
+    flex_string_append(fs, " ");
+    flex_string_append(fs, attr_p->attr.source_filter.src_list[i]);
+  }
+
+  flex_string_append(fs, "\r\n");
+
+  return SDP_SUCCESS;
+}
+
+/* Parse the rtcp-unicast attribute
+ * "a=rtcp-unicast:<reflection|rsi>"
+ */
+sdp_result_e sdp_parse_attr_rtcp_unicast (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                          const char *ptr)
+{
+    sdp_result_e result;
+    u32 i;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    attr_p->attr.u32_val = SDP_RTCP_UNICAST_MODE_NOT_PRESENT;
+
+    memset(tmp, 0, sizeof(tmp));
+
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No rtcp unicast mode specified for "
+            "a=rtcp-unicast line", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    for (i = 0; i < SDP_RTCP_MAX_UNICAST_MODE;  i++) {
+        if (cpr_strncasecmp(tmp, sdp_rtcp_unicast_mode_val[i].name,
+                        sdp_rtcp_unicast_mode_val[i].strlen) == 0) {
+            attr_p->attr.u32_val = i;
+            break;
+        }
+    }
+    if (attr_p->attr.u32_val == SDP_RTCP_UNICAST_MODE_NOT_PRESENT) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Invalid rtcp unicast mode for "
+            "a=rtcp-unicast line", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_attr_rtcp_unicast (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                          flex_string *fs)
+{
+  if (attr_p->attr.u32_val >= SDP_RTCP_MAX_UNICAST_MODE) {
+    return SDP_INVALID_PARAMETER;
+  }
+
+  flex_string_sprintf(fs, "a=%s:%s\r\n",
+    sdp_get_attr_name(attr_p->type),
+    sdp_get_rtcp_unicast_mode_name((sdp_rtcp_unicast_mode_e)attr_p->attr.u32_val));
+
+  return SDP_SUCCESS;
+}
+
+
+/*
+ * store_sdescriptions_mki_or_lifetime
+ *
+ * Verifies the syntax of the MKI or lifetime parameter and stores
+ * it in the sdescriptions attribute struct.
+ *
+ * Inputs:
+ *   buf    - pointer to MKI or lifetime string assumes string is null
+ *            terminated.
+ *   attr_p - pointer to attribute struct
+ *
+ * Outputs:
+ *   Return TRUE all is good otherwise FALSE for error.
+ */
+
+tinybool
+store_sdescriptions_mki_or_lifetime (char *buf, sdp_attr_t *attr_p)
+{
+
+    tinybool  result;
+    u16       mkiLen;
+    char      mkiValue[SDP_SRTP_MAX_MKI_SIZE_BYTES];
+
+    /* MKI has a colon */
+    if (strstr(buf, ":")) {
+        result = verify_sdescriptions_mki(buf, mkiValue, &mkiLen);
+       if (result) {
+           attr_p->attr.srtp_context.mki_size_bytes = mkiLen;
+           sstrncpy((char*)attr_p->attr.srtp_context.mki, mkiValue,
+                    SDP_SRTP_MAX_MKI_SIZE_BYTES);
+       }
+
+    } else {
+        result =  verify_sdescriptions_lifetime(buf);
+       if (result) {
+           sstrncpy((char*)attr_p->attr.srtp_context.master_key_lifetime, buf,
+                    SDP_SRTP_MAX_LIFETIME_BYTES);
+       }
+    }
+
+    return result;
+
+}
+
+/*
+ * sdp_parse_sdescriptions_key_param
+ *
+ * This routine parses the srtp key-params pointed to by str.
+ *
+ * key-params    = <key-method> ":" <key-info>
+ * key-method    = "inline" / key-method-ext [note V9 only supports 'inline']
+ * key-info      = srtp-key-info
+ * srtp-key-info = key-salt ["|" lifetime] ["|" mki]
+ * key-salt      = 1*(base64)   ; binary key and salt values
+ *                              ; concatenated together, and then
+ *                              ; base64 encoded [section 6.8 of
+ *                              ; RFC2046]
+ *
+ * lifetime      = ["2^"] 1*(DIGIT)
+ * mki           = mki-value ":" mki-length
+ * mki-value     = 1*DIGIT
+ * mki-length    = 1*3DIGIT   ; range 1..128.
+ *
+ * Inputs: str - pointer to beginning of key-params and assumes
+ *               null terminated string.
+ */
+
+
+tinybool
+sdp_parse_sdescriptions_key_param (const char *str, sdp_attr_t *attr_p,
+                                   sdp_t *sdp_p)
+{
+    char            buf[SDP_MAX_STRING_LEN],
+                    base64decodeData[SDP_MAX_STRING_LEN];
+    const char      *ptr;
+    sdp_result_e    result = SDP_SUCCESS;
+    tinybool        keyFound = FALSE;
+    int             len,
+                    keySize,
+                   saltSize;
+    base64_result_t status;
+
+    ptr = str;
+    if (cpr_strncasecmp(ptr, "inline:", 7) != 0) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Could not find keyword inline", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return FALSE;
+    }
+
+    /* advance pass the inline key word */
+    ptr = ptr + 7;
+    ptr = sdp_getnextstrtok(ptr, buf, sizeof(buf), "|", &result);
+    while (result == SDP_SUCCESS) {
+        /* the fist time this loop executes, the key is gotten */
+        if (keyFound == FALSE) {
+           keyFound = TRUE;
+           len = SDP_MAX_STRING_LEN;
+           /* The key is base64 encoded composed of the master key concatenated with the
+            * master salt.
+            */
+           status = base64_decode((unsigned char *)buf, strlen(buf),
+                                  (unsigned char *)base64decodeData, &len);
+
+        if (status != BASE64_SUCCESS) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s key-salt error decoding buffer: %s",
+                sdp_p->debug_str, BASE64_RESULT_TO_STRING(status));
+            return FALSE;
+        }
+
+        keySize = attr_p->attr.srtp_context.master_key_size_bytes;
+        saltSize = attr_p->attr.srtp_context.master_salt_size_bytes;
+
+        if (len != keySize + saltSize) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s key-salt size doesn't match: (%d, %d, %d)",
+                sdp_p->debug_str, len, keySize, saltSize);
+            return(FALSE);
+        }
+
+           bcopy(base64decodeData, attr_p->attr.srtp_context.master_key, keySize);
+
+           bcopy(base64decodeData + keySize,
+                 attr_p->attr.srtp_context.master_salt, saltSize);
+
+           /* Used only for MGCP */
+           SDP_SRTP_CONTEXT_SET_MASTER_KEY
+                    (attr_p->attr.srtp_context.selection_flags);
+           SDP_SRTP_CONTEXT_SET_MASTER_SALT
+                    (attr_p->attr.srtp_context.selection_flags);
+
+       } else if (store_sdescriptions_mki_or_lifetime(buf, attr_p) == FALSE) {
+           return FALSE;
+       }
+
+       /* if we haven't reached the end of line, get the next token */
+       ptr = sdp_getnextstrtok(ptr, buf, sizeof(buf), "|", &result);
+    }
+
+    /* if we didn't find the key, error out */
+    if (keyFound == FALSE) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Could not find sdescriptions key", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return FALSE;
+    }
+
+    return TRUE;
+
+}
+
+/*
+ * sdp_build_attr_sdescriptions
+ *
+ * Builds a=crypto line for attribute type SDP_ATTR_SDESCRIPTIONS.
+ *
+ * a=crypto:tag 1*WSP crypto-suite 1*WSP key-params
+ *
+ * Where key-params = inline: <key|salt> ["|"lifetime] ["|" MKI:length]
+ * The key and salt is base64 encoded and lifetime and MKI/length are optional.
+ */
+
+sdp_result_e
+sdp_build_attr_sdescriptions (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                              flex_string *fs)
+{
+
+    unsigned char  base64_encoded_data[MAX_BASE64_STRING_LEN];
+    unsigned char  base64_encoded_input[MAX_BASE64_STRING_LEN];
+    int            keySize,
+                   saltSize,
+                  outputLen;
+    base64_result_t status;
+
+    keySize = attr_p->attr.srtp_context.master_key_size_bytes;
+    saltSize = attr_p->attr.srtp_context.master_salt_size_bytes;
+
+    /* concatenate the master key + salt then base64 encode it */
+    bcopy(attr_p->attr.srtp_context.master_key,
+          base64_encoded_input, keySize);
+
+    bcopy(attr_p->attr.srtp_context.master_salt,
+          base64_encoded_input + keySize, saltSize);
+
+    outputLen = MAX_BASE64_STRING_LEN;
+    status = base64_encode(base64_encoded_input, keySize + saltSize,
+                          base64_encoded_data, &outputLen);
+
+    if (status != BASE64_SUCCESS) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Error: Failure to Base64 Encoded data (%s) ",
+                       sdp_p->debug_str, BASE64_RESULT_TO_STRING(status));
+        }
+       return (SDP_INVALID_PARAMETER);
+
+    }
+
+    base64_encoded_data[outputLen] = 0;
+
+    /* lifetime and MKI parameters are optional. Only inlcude them if
+     * they were set.
+     */
+
+
+    if (attr_p->attr.srtp_context.master_key_lifetime[0] != 0 &&
+        attr_p->attr.srtp_context.mki[0] != 0) {
+      flex_string_sprintf(fs, "a=%s:%d %s inline:%s|%s|%s:%d\r\n",
+        sdp_attr[attr_p->type].name,
+        attr_p->attr.srtp_context.tag,
+        sdp_srtp_context_crypto_suite[attr_p->attr.srtp_context.suite].name,
+        base64_encoded_data,
+        attr_p->attr.srtp_context.master_key_lifetime,
+        attr_p->attr.srtp_context.mki,
+        attr_p->attr.srtp_context.mki_size_bytes);
+
+      return SDP_SUCCESS;
+    }
+
+    /* if we get here, either lifetime is populated and mki and is not or mki is populated
+     * and lifetime is not or neither is populated
+     */
+
+    if (attr_p->attr.srtp_context.master_key_lifetime[0] != 0) {
+      flex_string_sprintf(fs, "a=%s:%d %s inline:%s|%s\r\n",
+        sdp_attr[attr_p->type].name,
+        attr_p->attr.srtp_context.tag,
+        sdp_srtp_context_crypto_suite[attr_p->attr.srtp_context.suite].name,
+        base64_encoded_data,
+        attr_p->attr.srtp_context.master_key_lifetime);
+
+    } else if (attr_p->attr.srtp_context.mki[0] != 0) {
+      flex_string_sprintf(fs, "a=%s:%d %s inline:%s|%s:%d\r\n",
+        sdp_attr[attr_p->type].name,
+        attr_p->attr.srtp_context.tag,
+        sdp_srtp_context_crypto_suite[attr_p->attr.srtp_context.suite].name,
+        base64_encoded_data,
+        attr_p->attr.srtp_context.mki,
+        attr_p->attr.srtp_context.mki_size_bytes);
+
+    } else {
+      flex_string_sprintf(fs, "a=%s:%d %s inline:%s\r\n",
+        sdp_attr[attr_p->type].name,
+        attr_p->attr.srtp_context.tag,
+        sdp_srtp_context_crypto_suite[attr_p->attr.srtp_context.suite].name,
+        base64_encoded_data);
+
+    }
+
+    return SDP_SUCCESS;
+
+}
+
+
+/*
+ * sdp_parse_attr_srtp
+ *
+ * Parses Session Description for Protocol Security Descriptions
+ * version 2 or version 9. Grammar is of the form:
+ *
+ * a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
+ *
+ * Note session-params is not supported and will not be parsed.
+ * Version 2 does not contain a tag.
+ *
+ * Inputs:
+ *   sdp_p  - pointer to sdp handle
+ *   attr_p - pointer to attribute structure
+ *   ptr    - pointer to string to be parsed
+ *   vtype  - version type
+ */
+
+sdp_result_e
+sdp_parse_attr_srtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                     const char *ptr, sdp_attr_e vtype)
+{
+
+    char         tmp[SDP_MAX_STRING_LEN];
+    sdp_result_e result = SDP_FAILURE;
+    int          k = 0;
+
+    /* initialize only the optional parameters */
+    attr_p->attr.srtp_context.master_key_lifetime[0] = 0;
+    attr_p->attr.srtp_context.mki[0] = 0;
+
+     /* used only for MGCP */
+    SDP_SRTP_CONTEXT_SET_ENCRYPT_AUTHENTICATE
+             (attr_p->attr.srtp_context.selection_flags);
+
+    /* get the tag only if we are version 9 */
+    if (vtype == SDP_ATTR_SDESCRIPTIONS) {
+        attr_p->attr.srtp_context.tag =
+                sdp_getnextnumtok(ptr, &ptr, " \t", &result);
+
+        if (result != SDP_SUCCESS) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Could not find sdescriptions tag",
+                sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+
+        }
+    }
+
+    /* get the crypto suite */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Could not find sdescriptions crypto suite", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (!sdp_parse_context_crypto_suite(tmp, attr_p, sdp_p)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Unsupported crypto suite", sdp_p->debug_str);
+           return (SDP_INVALID_PARAMETER);
+    }
+
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Could not find sdescriptions key params", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (!sdp_parse_sdescriptions_key_param(tmp, attr_p, sdp_p)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Failed to parse key-params", sdp_p->debug_str);
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* if there are session parameters, scan the session parameters
+     * into tmp until we reach end of line. Currently the sdp parser
+     * does not parse session parameters but if they are present,
+     * we store them for the application.
+     */
+    /*sa_ignore NO_NULL_CHK
+     *{ptr is valid since the pointer was checked earlier and the
+     * function would have exited if NULL.}
+     */
+    while (*ptr && *ptr != '\n' && *ptr != '\r' && k < SDP_MAX_STRING_LEN) {
+         tmp[k++] = *ptr++;
+    }
+
+    if ((k) && (k < SDP_MAX_STRING_LEN)) {
+        tmp[k] = 0;
+        attr_p->attr.srtp_context.session_parameters = cpr_strdup(tmp);
+    }
+
+    return SDP_SUCCESS;
+
+}
+
+/* Parses crypto attribute based on the sdescriptions version
+ * 9 grammar.
+ *
+ */
+
+sdp_result_e
+sdp_parse_attr_sdescriptions (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                              const char *ptr)
+{
+
+   return sdp_parse_attr_srtp(sdp_p, attr_p, ptr,
+                              SDP_ATTR_SDESCRIPTIONS);
+
+}
+
+/* Parses X-crypto attribute based on the sdescriptions version
+ * 2 grammar.
+ *
+ */
+
+sdp_result_e sdp_parse_attr_srtpcontext (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                         const char *ptr)
+{
+
+    return sdp_parse_attr_srtp(sdp_p, attr_p, ptr,
+                               SDP_ATTR_SRTP_CONTEXT);
+}
+
+
+sdp_result_e sdp_build_attr_ice_attr (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                          flex_string *fs) {
+  flex_string_sprintf(fs, "a=%s\r\n", attr_p->attr.ice_attr);
+
+  return SDP_SUCCESS;
+}
+
+
+sdp_result_e sdp_parse_attr_ice_attr (sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr) {
+    sdp_result_e  result;
+    char tmp[SDP_MAX_STRING_LEN];
+
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), "\r\n", &result);
+    if (result != SDP_SUCCESS){
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: problem parsing ice attribute ", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* We need the attr= here. This is pretty gross. */
+    snprintf(attr_p->attr.ice_attr, sizeof(attr_p->attr.ice_attr),
+      "%s:%s", sdp_get_attr_name(attr_p->type), tmp);
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+      SDP_PRINT("%s Parsed a=%s, %s", sdp_p->debug_str, sdp_get_attr_name(attr_p->type), tmp);
+    }
+    return (SDP_SUCCESS);
+}
+
+
+sdp_result_e sdp_parse_attr_fingerprint_attr (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                           const char *ptr)
+{
+    sdp_result_e  result;
+
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.string_val, sizeof(attr_p->attr.string_val), "\r\n", &result);
+
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No string token found for %s attribute",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Parsed a=%s, %s", sdp_p->debug_str,
+                      sdp_get_attr_name(attr_p->type),
+                      attr_p->attr.string_val);
+        }
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_build_attr_rtcp_mux_attr (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                          flex_string *fs) {
+    flex_string_append(fs, "a=rtcp-mux\r\n");
+
+    return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_parse_attr_rtcp_mux_attr (sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr) {
+    attr_p->attr.boolean_val = TRUE;
+
+    return (SDP_SUCCESS);
+}
diff --git a/libs/sipcc/core/sdp/sdp_attr_access.c b/libs/sipcc/core/sdp/sdp_attr_access.c
new file mode 100644 (file)
index 0000000..1399445
--- /dev/null
@@ -0,0 +1,11872 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp_os_defs.h"
+#include "sdp.h"
+#include "sdp_private.h"
+#include "CSFLog.h"
+
+static const char* logTag = "sdp_attr_access";
+
+/* Attribute access routines are all defined by the following parameters.
+ *
+ * sdp_ptr     The SDP handle returned by sdp_init_description.
+ * level       The level the attribute is defined.  Can be either
+ *             SDP_SESSION_LEVEL or 0-n specifying a media line level.
+ * inst_num    The instance number of the attribute.  Multiple instances
+ *             of a particular attribute may exist at each level and so
+ *             the inst_num determines the particular attribute at that
+ *             level that should be accessed.  Note that this is the
+ *             instance number of the specified type of attribute, not the
+ *             overall attribute number at the level.  Also note that the
+ *             instance number is 1-based.  For example:
+ *             v=0
+ *             o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
+ *             s=SDP Seminar
+ *             c=IN IP4 10.1.0.2
+ *             t=0 0
+ *             m=audio 1234 RTP/AVP 0 101 102
+ *             a=foo 1
+ *             a=foo 2
+ *             a=bar 1   # This is instance 1 of attribute bar.
+ *             a=foo 3   # This is instance 3 of attribute foo.
+ * cap_num     Almost all of the attributes may be defined as X-cpar
+ *             parameters (with the exception of X-sqn, X-cap, and X-cpar).
+ *             If the cap_num is set to zero, then the attribute is not
+ *             an X-cpar parameter attribute.  If the cap_num is any other
+ *             value, it specifies the capability number that the X-cpar
+ *             attribute is specified for.
+ */
+
+/* Attribute handling:
+ *
+ * There are two basic types of attributes handled by the SDP library,
+ * those defined by a= token lines, and those embedded with a=X-cpar lines.
+ * The handling for each of these is described here.
+ *
+ * Simple (non X-cpar attributes):
+ *
+ * Attributes not embedded in a=X-cpar lines are referenced by level and
+ * instance number.  For these attributes the capability number is always
+ * set to zero.
+ *
+ * An application will typically process these attributes in one of two ways.
+ * With the first method, the application can determine the total number
+ * of attributes defined at a given level and process them one at a time.
+ * For each attribute, the application will query the library to find out
+ * what type of attribute it is and which instance within that type. The
+ * application can then process this particular attribute referencing it
+ * by level and instance number.
+ *
+ * A second method of processing attributes is for applications to determine
+ * each type of attribute they are interested in, query the SDP library to
+ * find out how many of that type of attribute exist at a given level, and
+ * process each one at a time.
+ *
+ * X-cpar attribute processing:
+ *
+ * X-cpar attributes can contain embedded attributes.  They are associated
+ * with X-cap attribute lines.  An example of X-cap and X-cpar attributes
+ * found in an SDP is as follows:
+ *
+ * v=0
+ * o=- 25678 753849 IN IP4 128.96.41.1
+ * s=-
+ * t=0 0
+ * c=IN IP4 10.1.0.2
+ * m=audio 3456 RTP/AVP 18 96
+ * a=rtpmap:96 telephone-event/8000
+ * a=fmtp:96 0-15,32-35
+ * a=X-sqn: 0
+ * a=X-cap: 1 audio RTP/AVP 0 18 96 97
+ * a=X-cpar: a=fmtp:96 0-16,32-35
+ * a=X-cpar: a=rtpmap:97 X-NSE/8000
+ * a=X-cpar: a=fmtp:97 195-197
+ * a=X-cap: 5 image udptl t38
+ * a=X-cap: 6 application udp X-tmr
+ * a=X-cap: 7 audio RTP/AVP 100 101
+ * a=X-cpar: a=rtpmap:100 g.711/8000
+ * a=X-cpar: a=rtpmap:101 g.729/8000
+ *
+ * X-cap attributes can be defined at the SESSION_LEVEL or any media level.
+ * An X-cap attr is defined by the level and instance number just like
+ * other attributes.  In the example above, X-cap attrs are defined at
+ * media level 1 and there are four instances at that level.
+ *
+ * The X-cpar attributes can also be referenced by level and instance number.
+ * However, the embedded attribute within an X-cpar attribute must be
+ * referenced by level, instance number, and capability number.  This is
+ * because the X-cpar attribute is associated with a particular X-cap/
+ * capability.
+ * For all attributes that are not embedded within an X-cpar attribute, the
+ * cap_num should be referenced as zero.  But for X-cpar attributes, the
+ * cap_num is specified to be one of the capability numbers of the previous
+ * X-cap line.  The number of capabilities specified in an X-cap line is
+ * equal to the number of payloads.  Thus, in this example, the first X-cap
+ * attr instance specifies capabilities 1-4, the second specifies capability
+ * 5, the third capability 6, and the fourth capabilities 7-8.
+ *
+ * X-cpar attributes can be processed with methods similar to the two
+ * previously mentioned.  For each X-cap attribute, the application can
+ * use one of two methods to process the X-cpar attributes.  First, it
+ * can query the total number of X-cpar attributes associated with a
+ * given X-cap attribute.  The X-cap attribute is here defined by a level
+ * and a capability number.  In the example above, the total number of
+ * attributes defined is as follows:
+ *  level 1, cap_num 1 - total attrs: 3
+ *  level 1, cap_num 5 - total attrs: 0
+ *  level 1, cap_num 6 - total attrs: 0
+ *  level 1, cap_num 7 - total attrs: 2
+ *
+ * Note that if the application queried the number of attributes for
+ * cap_num 2, 3, or 4, it would also return 3 attrs, and for cap_num
+ * 8 the library would return 2.
+ *
+ * Once the application determines the total number of attributes for
+ * that capability, it can again query the embedded attribute type and
+ * instance.  For example, sdp_get_attr_type would return the following:
+ *  level 1, cap_num 1, attr 1 -> attr type fmtp, instance 1
+ *  level 1, cap_num 1, attr 2 -> attr type rtpmap, instance 1
+ *  level 1, cap_num 1, attr 3 -> attr type fmtp, instance 2
+ *  level 1, cap_num 7, attr 1 -> attr type rtpmap, instance 1
+ *  level 1, cap_num 7, attr 2 -> attr type rtpmap, instance 2
+ *
+ * The individual embedded attributes can then be accessed by level,
+ * cap_num, and instance number.
+ *
+ * With the second method for handling X-cpar attributes, the application
+ * determines the types of attributes it is interested in.  It can then
+ * query the SDP library to determine the number of attributes of that
+ * type found for that level and cap_num, and then process each one at
+ * a time.  e.g., calling sdp_attr_num_instances would give:
+ *  level 1, cap_num 1, attr_type fmtp -> two instances
+ *  level 1, cap_num 1, attr_type rtpmap -> one instance
+ *  level 1, cap_num 7, attr_type fmtp -> zero instances
+ *  level 1, cap_num 7, attr_type rtpmap -> two instances
+ */
+
+
+/* Function:    sdp_add_new_attr
+ * Description: Add a new attribute of the specified type at the given
+ *              level and capability level or base attribute if cap_num
+ *              is zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              attr_type   The type of attribute to add.
+ *              inst_num    Pointer to a u16 in which to return the instance
+ *                          number of the newly added attribute.
+ * Returns:     SDP_SUCCESS            Attribute was added successfully.
+ *              SDP_NO_RESOURCE        No memory avail for new attribute.
+ *              SDP_INVALID_PARAMETER  Specified media line is not defined.
+ */
+sdp_result_e sdp_add_new_attr (void *sdp_ptr, u16 level, u8 cap_num,
+                               sdp_attr_e attr_type, u16 *inst_num)
+{
+    u16          i;
+    sdp_mca_t   *mca_p;
+    sdp_mca_t   *cap_p;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_attr_t  *new_attr_p;
+    sdp_attr_t  *prev_attr_p=NULL;
+    sdp_fmtp_t  *fmtp_p;
+    sdp_comediadir_t  *comediadir_p;
+
+    *inst_num = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if ((cap_num != 0) &&
+        ((attr_type == SDP_ATTR_X_CAP) || (attr_type == SDP_ATTR_X_CPAR) ||
+         (attr_type == SDP_ATTR_X_SQN) || (attr_type == SDP_ATTR_CDSC) ||
+        (attr_type == SDP_ATTR_CPAR) || (attr_type == SDP_ATTR_SQN))) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid attribute type for X-cpar/cdsc "
+                     "parameter.", sdp_p->debug_str);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Some attributes are valid only under media level */
+    if (level == SDP_SESSION_LEVEL) {
+        switch (attr_type) {
+            case SDP_ATTR_RTCP:
+            case SDP_ATTR_LABEL:
+                return (SDP_INVALID_MEDIA_LEVEL);
+
+            default:
+                break;
+        }
+    }
+
+    new_attr_p = (sdp_attr_t *)SDP_MALLOC(sizeof(sdp_attr_t));
+    if (new_attr_p == NULL) {
+        sdp_p->conf_p->num_no_resource++;
+        return (SDP_NO_RESOURCE);
+    }
+
+    new_attr_p->type = attr_type;
+    new_attr_p->next_p = NULL;
+
+    /* Initialize the new attribute structure */
+    if ((new_attr_p->type == SDP_ATTR_X_CAP) ||
+       (new_attr_p->type == SDP_ATTR_CDSC)) {
+        new_attr_p->attr.cap_p = (sdp_mca_t *)SDP_MALLOC(sizeof(sdp_mca_t));
+        if (new_attr_p->attr.cap_p == NULL) {
+            sdp_free_attr(new_attr_p);
+            sdp_p->conf_p->num_no_resource++;
+            return (SDP_NO_RESOURCE);
+        }
+    } else if (new_attr_p->type == SDP_ATTR_FMTP) {
+        fmtp_p = &(new_attr_p->attr.fmtp);
+        fmtp_p->fmtp_format = SDP_FMTP_UNKNOWN_TYPE;
+        // set to invalid value
+        fmtp_p->packetization_mode = 0xff;
+        fmtp_p->level_asymmetry_allowed = SDP_INVALID_LEVEL_ASYMMETRY_ALLOWED_VALUE;
+       fmtp_p->annexb_required = FALSE;
+       fmtp_p->annexa_required = FALSE;
+        fmtp_p->maxval = 0;
+       fmtp_p->bitrate = 0;
+       fmtp_p->cif = 0;
+       fmtp_p->qcif = 0;
+        fmtp_p->profile = SDP_INVALID_VALUE;
+        fmtp_p->level = SDP_INVALID_VALUE;
+        fmtp_p->parameter_add = TRUE;
+       for (i=0; i < SDP_NE_NUM_BMAP_WORDS; i++) {
+            fmtp_p->bmap[i] = 0;
+        }
+    } else if ((new_attr_p->type == SDP_ATTR_RTPMAP) ||
+              (new_attr_p->type == SDP_ATTR_SPRTMAP)) {
+        new_attr_p->attr.transport_map.num_chan = 1;
+    } else if (new_attr_p->type == SDP_ATTR_DIRECTION) {
+        comediadir_p = &(new_attr_p->attr.comediadir);
+       comediadir_p->role = SDP_MEDIADIR_ROLE_PASSIVE;
+        comediadir_p->conn_info_present = FALSE;
+    } else if (new_attr_p->type == SDP_ATTR_MPTIME) {
+        sdp_mptime_t *mptime = &(new_attr_p->attr.mptime);
+        mptime->num_intervals = 0;
+    }
+
+    if (cap_num == 0) {
+        /* Add a new attribute. */
+        if (level == SDP_SESSION_LEVEL) {
+            if (sdp_p->sess_attrs_p == NULL) {
+                sdp_p->sess_attrs_p = new_attr_p;
+            } else {
+                for (attr_p = sdp_p->sess_attrs_p;
+                     attr_p != NULL;
+                     prev_attr_p = attr_p, attr_p = attr_p->next_p) {
+                    /* Count the num instances of this type. */
+                    if (attr_p->type == attr_type) {
+                        (*inst_num)++;
+                    }
+                }
+                prev_attr_p->next_p = new_attr_p;
+            }
+        } else {
+            mca_p = sdp_find_media_level(sdp_p, level);
+            if (mca_p == NULL) {
+                sdp_free_attr(new_attr_p);
+                sdp_p->conf_p->num_invalid_param++;
+                return (SDP_INVALID_PARAMETER);
+            }
+            if (mca_p->media_attrs_p == NULL) {
+                mca_p->media_attrs_p = new_attr_p;
+            } else {
+                for (attr_p = mca_p->media_attrs_p;
+                     attr_p != NULL;
+                     prev_attr_p = attr_p, attr_p = attr_p->next_p) {
+                    /* Count the num instances of this type. */
+                    if (attr_p->type == attr_type) {
+                        (*inst_num)++;
+                    }
+                }
+                prev_attr_p->next_p = new_attr_p;
+            }
+        }
+    } else {
+        /* Add a new capability attribute - find the capability attr. */
+        attr_p = sdp_find_capability(sdp_p, level, cap_num);
+        if (attr_p == NULL) {
+            sdp_free_attr(new_attr_p);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        cap_p = attr_p->attr.cap_p;
+        if (cap_p->media_attrs_p == NULL) {
+            cap_p->media_attrs_p = new_attr_p;
+        } else {
+            for (attr_p = cap_p->media_attrs_p;
+                 attr_p != NULL;
+                 prev_attr_p = attr_p, attr_p = attr_p->next_p) {
+                /* Count the num instances of this type. */
+                if (attr_p->type == attr_type) {
+                    (*inst_num)++;
+                }
+            }
+            prev_attr_p->next_p = new_attr_p;
+        }
+    }
+
+    /* Increment the instance num for the attr just added. */
+    (*inst_num)++;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_copy_attr_fields
+ * Description: Copy the fields of an attribute based on attr type.
+ *              This is an INTERNAL SDP routine only.  It will not copy
+ *              X-Cap, X-Cpar, CDSC, or CPAR attrs.
+ * Parameters:  src_attr_p  Ptr to the source attribute.
+ *              dst_attr_p  Ptr to the dst attribute.
+ * Returns:     Nothing.
+ */
+void sdp_copy_attr_fields (sdp_attr_t *src_attr_p, sdp_attr_t *dst_attr_p)
+{
+    u16 i;
+
+    /* Copy over all the attribute information. */
+    dst_attr_p->type = src_attr_p->type;
+    dst_attr_p->next_p = NULL;
+
+    switch (src_attr_p->type) {
+
+    case SDP_ATTR_BEARER:
+    case SDP_ATTR_CALLED:
+    case SDP_ATTR_CONN_TYPE:
+    case SDP_ATTR_DIALED:
+    case SDP_ATTR_DIALING:
+    case SDP_ATTR_FRAMING:
+    case SDP_ATTR_MAXPRATE:
+    case SDP_ATTR_LABEL:
+        sstrncpy(dst_attr_p->attr.string_val, src_attr_p->attr.string_val,
+                SDP_MAX_STRING_LEN+1);
+        break;
+
+    case SDP_ATTR_EECID:
+    case SDP_ATTR_PTIME:
+    case SDP_ATTR_T38_VERSION:
+    case SDP_ATTR_T38_MAXBITRATE:
+    case SDP_ATTR_T38_MAXBUFFER:
+    case SDP_ATTR_T38_MAXDGRAM:
+    case SDP_ATTR_X_SQN:
+    case SDP_ATTR_TC1_PAYLOAD_BYTES:
+    case SDP_ATTR_TC1_WINDOW_SIZE:
+    case SDP_ATTR_TC2_PAYLOAD_BYTES:
+    case SDP_ATTR_TC2_WINDOW_SIZE:
+    case SDP_ATTR_RTCP:
+    case SDP_ATTR_MID:
+    case SDP_ATTR_RTCP_UNICAST:
+        dst_attr_p->attr.u32_val = src_attr_p->attr.u32_val;
+        break;
+
+    case SDP_ATTR_T38_FILLBITREMOVAL:
+    case SDP_ATTR_T38_TRANSCODINGMMR:
+    case SDP_ATTR_T38_TRANSCODINGJBIG:
+    case SDP_ATTR_TMRGWXID:
+        dst_attr_p->attr.boolean_val = src_attr_p->attr.boolean_val;
+        break;
+
+    case SDP_ATTR_QOS:
+    case SDP_ATTR_SECURE:
+    case SDP_ATTR_X_PC_QOS:
+    case SDP_ATTR_X_QOS:
+        dst_attr_p->attr.qos.strength  = src_attr_p->attr.qos.strength;
+        dst_attr_p->attr.qos.direction = src_attr_p->attr.qos.direction;
+        dst_attr_p->attr.qos.confirm   = src_attr_p->attr.qos.confirm;
+        break;
+
+    case SDP_ATTR_CURR:
+        dst_attr_p->attr.curr.type         = src_attr_p->attr.curr.type;
+        dst_attr_p->attr.curr.direction    = src_attr_p->attr.curr.direction;
+        dst_attr_p->attr.curr.status_type  = src_attr_p->attr.curr.status_type;
+        break;
+    case SDP_ATTR_DES:
+        dst_attr_p->attr.des.type         = src_attr_p->attr.des.type;
+        dst_attr_p->attr.des.direction    = src_attr_p->attr.des.direction;
+        dst_attr_p->attr.des.status_type  = src_attr_p->attr.des.status_type;
+        dst_attr_p->attr.des.strength     = src_attr_p->attr.des.strength;
+        break;
+
+
+    case SDP_ATTR_CONF:
+        dst_attr_p->attr.conf.type         = src_attr_p->attr.conf.type;
+        dst_attr_p->attr.conf.direction    = src_attr_p->attr.conf.direction;
+        dst_attr_p->attr.conf.status_type  = src_attr_p->attr.conf.status_type;
+        break;
+
+    case SDP_ATTR_INACTIVE:
+    case SDP_ATTR_RECVONLY:
+    case SDP_ATTR_SENDONLY:
+    case SDP_ATTR_SENDRECV:
+        /* These attrs have no parameters. */
+        break;
+
+    case SDP_ATTR_FMTP:
+        dst_attr_p->attr.fmtp.payload_num = src_attr_p->attr.fmtp.payload_num;
+        dst_attr_p->attr.fmtp.maxval      = src_attr_p->attr.fmtp.maxval;
+        dst_attr_p->attr.fmtp.bitrate     = src_attr_p->attr.fmtp.bitrate;
+        dst_attr_p->attr.fmtp.annexa      = src_attr_p->attr.fmtp.annexa;
+        dst_attr_p->attr.fmtp.annexb      = src_attr_p->attr.fmtp.annexb;
+        dst_attr_p->attr.fmtp.qcif      = src_attr_p->attr.fmtp.qcif;
+        dst_attr_p->attr.fmtp.cif      = src_attr_p->attr.fmtp.cif;
+        dst_attr_p->attr.fmtp.sqcif      = src_attr_p->attr.fmtp.sqcif;
+        dst_attr_p->attr.fmtp.cif4      = src_attr_p->attr.fmtp.cif4;
+        dst_attr_p->attr.fmtp.cif16      = src_attr_p->attr.fmtp.cif16;
+        dst_attr_p->attr.fmtp.maxbr      = src_attr_p->attr.fmtp.maxbr;
+        dst_attr_p->attr.fmtp.custom_x   = src_attr_p->attr.fmtp.custom_x;
+        dst_attr_p->attr.fmtp.custom_y      = src_attr_p->attr.fmtp.custom_y;
+        dst_attr_p->attr.fmtp.custom_mpi      = src_attr_p->attr.fmtp.custom_mpi;
+        dst_attr_p->attr.fmtp.par_width      = src_attr_p->attr.fmtp.par_width;
+        dst_attr_p->attr.fmtp.par_height    = src_attr_p->attr.fmtp.par_height;
+        dst_attr_p->attr.fmtp.cpcf      = src_attr_p->attr.fmtp.cpcf;
+        dst_attr_p->attr.fmtp.bpp      = src_attr_p->attr.fmtp.bpp;
+        dst_attr_p->attr.fmtp.hrd      = src_attr_p->attr.fmtp.hrd;
+
+        dst_attr_p->attr.fmtp.profile      = src_attr_p->attr.fmtp.profile;
+        dst_attr_p->attr.fmtp.level      = src_attr_p->attr.fmtp.level;
+        dst_attr_p->attr.fmtp.is_interlace = src_attr_p->attr.fmtp.is_interlace;
+
+        sstrncpy(dst_attr_p->attr.fmtp.profile_level_id,
+                 src_attr_p->attr.fmtp.profile_level_id,
+                 SDP_MAX_STRING_LEN+1);
+        sstrncpy(dst_attr_p->attr.fmtp.parameter_sets,
+                 src_attr_p->attr.fmtp.parameter_sets,
+                 SDP_MAX_STRING_LEN+1);
+        dst_attr_p->attr.fmtp.deint_buf_req =
+                src_attr_p->attr.fmtp.deint_buf_req;
+        dst_attr_p->attr.fmtp.max_don_diff =
+            src_attr_p->attr.fmtp.max_don_diff;
+        dst_attr_p->attr.fmtp.init_buf_time =
+                src_attr_p->attr.fmtp.init_buf_time;
+        dst_attr_p->attr.fmtp.packetization_mode =
+                src_attr_p->attr.fmtp.packetization_mode;
+        dst_attr_p->attr.fmtp.flag =
+                 src_attr_p->attr.fmtp.flag;
+
+        dst_attr_p->attr.fmtp.max_mbps = src_attr_p->attr.fmtp.max_mbps;
+        dst_attr_p->attr.fmtp.max_fs = src_attr_p->attr.fmtp.max_fs;
+        dst_attr_p->attr.fmtp.max_cpb = src_attr_p->attr.fmtp.max_cpb;
+        dst_attr_p->attr.fmtp.max_dpb = src_attr_p->attr.fmtp.max_dpb;
+        dst_attr_p->attr.fmtp.max_br = src_attr_p->attr.fmtp.max_br;
+        dst_attr_p->attr.fmtp.redundant_pic_cap =
+            src_attr_p->attr.fmtp.redundant_pic_cap;
+        dst_attr_p->attr.fmtp.deint_buf_cap =
+                src_attr_p->attr.fmtp.deint_buf_cap;
+        dst_attr_p->attr.fmtp.max_rcmd_nalu_size =
+                 src_attr_p->attr.fmtp.max_rcmd_nalu_size;
+        dst_attr_p->attr.fmtp.interleaving_depth =
+                src_attr_p->attr.fmtp.interleaving_depth;
+        dst_attr_p->attr.fmtp.parameter_add =
+            src_attr_p->attr.fmtp.parameter_add;
+
+        dst_attr_p->attr.fmtp.annex_d = src_attr_p->attr.fmtp.annex_d;
+        dst_attr_p->attr.fmtp.annex_f = src_attr_p->attr.fmtp.annex_f;
+        dst_attr_p->attr.fmtp.annex_i = src_attr_p->attr.fmtp.annex_i;
+        dst_attr_p->attr.fmtp.annex_j = src_attr_p->attr.fmtp.annex_j;
+        dst_attr_p->attr.fmtp.annex_t = src_attr_p->attr.fmtp.annex_t;
+        dst_attr_p->attr.fmtp.annex_k_val = src_attr_p->attr.fmtp.annex_k_val;
+        dst_attr_p->attr.fmtp.annex_n_val = src_attr_p->attr.fmtp.annex_n_val;
+        dst_attr_p->attr.fmtp.annex_p_val_picture_resize =
+            src_attr_p->attr.fmtp.annex_p_val_picture_resize;
+        dst_attr_p->attr.fmtp.annex_p_val_warp  =
+            src_attr_p->attr.fmtp.annex_p_val_warp;
+
+        dst_attr_p->attr.fmtp.annexb_required  =
+            src_attr_p->attr.fmtp.annexb_required;
+        dst_attr_p->attr.fmtp.annexa_required  =
+            src_attr_p->attr.fmtp.annexa_required;
+        dst_attr_p->attr.fmtp.fmtp_format
+            = src_attr_p->attr.fmtp.fmtp_format;
+
+        for (i=0; i < SDP_NE_NUM_BMAP_WORDS; i++) {
+            dst_attr_p->attr.fmtp.bmap[i] = src_attr_p->attr.fmtp.bmap[i];
+        }
+        break;
+
+    case SDP_ATTR_RTPMAP:
+        dst_attr_p->attr.transport_map.payload_num =
+            src_attr_p->attr.transport_map.payload_num;
+        dst_attr_p->attr.transport_map.clockrate =
+          src_attr_p->attr.transport_map.clockrate;
+        dst_attr_p->attr.transport_map.num_chan  =
+          src_attr_p->attr.transport_map.num_chan;
+        sstrncpy(dst_attr_p->attr.transport_map.encname,
+               src_attr_p->attr.transport_map.encname,
+               SDP_MAX_STRING_LEN+1);
+        break;
+
+    case SDP_ATTR_SUBNET:
+        dst_attr_p->attr.subnet.nettype  = src_attr_p->attr.subnet.nettype;
+        dst_attr_p->attr.subnet.addrtype = src_attr_p->attr.subnet.addrtype;
+        dst_attr_p->attr.subnet.prefix   = src_attr_p->attr.subnet.prefix;
+        sstrncpy(dst_attr_p->attr.subnet.addr, src_attr_p->attr.subnet.addr,
+                SDP_MAX_STRING_LEN+1);
+        break;
+
+    case SDP_ATTR_T38_RATEMGMT:
+        dst_attr_p->attr.t38ratemgmt = src_attr_p->attr.t38ratemgmt;
+        break;
+
+    case SDP_ATTR_T38_UDPEC:
+        dst_attr_p->attr.t38udpec = src_attr_p->attr.t38udpec;
+        break;
+
+    case SDP_ATTR_X_PC_CODEC:
+        dst_attr_p->attr.pccodec.num_payloads =
+            src_attr_p->attr.pccodec.num_payloads;
+        for (i=0; i < src_attr_p->attr.pccodec.num_payloads; i++) {
+            dst_attr_p->attr.pccodec.payload_type[i] =
+                src_attr_p->attr.pccodec.payload_type[i];
+        }
+        break;
+
+    case SDP_ATTR_DIRECTION:
+
+       dst_attr_p->attr.comediadir.role =
+           src_attr_p->attr.comediadir.role;
+
+       if (src_attr_p->attr.comediadir.conn_info.nettype) {
+           dst_attr_p->attr.comediadir.conn_info_present = TRUE;
+           dst_attr_p->attr.comediadir.conn_info.nettype =
+               src_attr_p->attr.comediadir.conn_info.nettype;
+           dst_attr_p->attr.comediadir.conn_info.addrtype =
+               src_attr_p->attr.comediadir.conn_info.addrtype;
+           sstrncpy(dst_attr_p->attr.comediadir.conn_info.conn_addr,
+                    src_attr_p->attr.comediadir.conn_info.conn_addr,
+                    SDP_MAX_STRING_LEN+1);
+           dst_attr_p->attr.comediadir.src_port =
+               src_attr_p->attr.comediadir.src_port;
+       }
+       break;
+
+    case SDP_ATTR_SILENCESUPP:
+        dst_attr_p->attr.silencesupp.enabled =
+            src_attr_p->attr.silencesupp.enabled;
+        dst_attr_p->attr.silencesupp.timer_null =
+            src_attr_p->attr.silencesupp.timer_null;
+        dst_attr_p->attr.silencesupp.timer =
+            src_attr_p->attr.silencesupp.timer;
+        dst_attr_p->attr.silencesupp.pref =
+            src_attr_p->attr.silencesupp.pref;
+        dst_attr_p->attr.silencesupp.siduse =
+            src_attr_p->attr.silencesupp.siduse;
+        dst_attr_p->attr.silencesupp.fxnslevel_null =
+            src_attr_p->attr.silencesupp.fxnslevel_null;
+        dst_attr_p->attr.silencesupp.fxnslevel =
+            src_attr_p->attr.silencesupp.fxnslevel;
+        break;
+
+    case SDP_ATTR_RTR:
+       dst_attr_p->attr.rtr.confirm = src_attr_p->attr.rtr.confirm;
+       break;
+
+    case SDP_ATTR_X_SIDIN:
+    case SDP_ATTR_X_SIDOUT:
+    case SDP_ATTR_X_CONFID:
+        sstrncpy(dst_attr_p->attr.stream_data.x_sidin,
+                 src_attr_p->attr.stream_data.x_sidin,SDP_MAX_STRING_LEN+1);
+        sstrncpy(dst_attr_p->attr.stream_data.x_sidout,
+                 src_attr_p->attr.stream_data.x_sidout,SDP_MAX_STRING_LEN+1);
+        sstrncpy(dst_attr_p->attr.stream_data.x_confid,
+                 src_attr_p->attr.stream_data.x_confid,SDP_MAX_STRING_LEN+1);
+        break;
+
+    case SDP_ATTR_GROUP:
+        dst_attr_p->attr.stream_data.group_attr =
+            src_attr_p->attr.stream_data.group_attr;
+        dst_attr_p->attr.stream_data.num_group_id =
+             src_attr_p->attr.stream_data.num_group_id;
+
+        for (i=0; i < src_attr_p->attr.stream_data.num_group_id; i++) {
+            dst_attr_p->attr.stream_data.group_id_arr[i] =
+                src_attr_p->attr.stream_data.group_id_arr[i];
+        }
+        break;
+
+    case SDP_ATTR_SOURCE_FILTER:
+        dst_attr_p->attr.source_filter.mode =
+                         src_attr_p->attr.source_filter.mode;
+        dst_attr_p->attr.source_filter.nettype =
+                         src_attr_p->attr.source_filter.nettype;
+        dst_attr_p->attr.source_filter.addrtype =
+                         src_attr_p->attr.source_filter.addrtype;
+        sstrncpy(dst_attr_p->attr.source_filter.dest_addr,
+                 src_attr_p->attr.source_filter.dest_addr,
+                 SDP_MAX_STRING_LEN+1);
+        for (i=0; i<src_attr_p->attr.source_filter.num_src_addr; ++i) {
+            sstrncpy(dst_attr_p->attr.source_filter.src_list[i],
+                     src_attr_p->attr.source_filter.src_list[i],
+                     SDP_MAX_STRING_LEN+1);
+        }
+        dst_attr_p->attr.source_filter.num_src_addr =
+                src_attr_p->attr.source_filter.num_src_addr;
+        break;
+
+    case SDP_ATTR_SRTP_CONTEXT:
+    case SDP_ATTR_SDESCRIPTIONS:
+         /* Tag parameter is not valid for version 2 sdescriptions */
+        if (src_attr_p->type == SDP_ATTR_SDESCRIPTIONS) {
+            dst_attr_p->attr.srtp_context.tag =
+               src_attr_p->attr.srtp_context.tag;
+        }
+
+       dst_attr_p->attr.srtp_context.selection_flags =
+                   src_attr_p->attr.srtp_context.selection_flags;
+
+       dst_attr_p->attr.srtp_context.suite = src_attr_p->attr.srtp_context.suite;
+
+       dst_attr_p->attr.srtp_context.master_key_size_bytes =
+                   src_attr_p->attr.srtp_context.master_key_size_bytes;
+
+        dst_attr_p->attr.srtp_context.master_salt_size_bytes =
+                   src_attr_p->attr.srtp_context.master_salt_size_bytes;
+
+        bcopy(src_attr_p->attr.srtp_context.master_key,
+             dst_attr_p->attr.srtp_context.master_key,
+             SDP_SRTP_MAX_KEY_SIZE_BYTES);
+
+       bcopy(src_attr_p->attr.srtp_context.master_salt,
+             dst_attr_p->attr.srtp_context.master_salt,
+             SDP_SRTP_MAX_SALT_SIZE_BYTES);
+
+
+       sstrncpy((char*)dst_attr_p->attr.srtp_context.master_key_lifetime,
+                (char*)src_attr_p->attr.srtp_context.master_key_lifetime,
+                SDP_SRTP_MAX_LIFETIME_BYTES);
+
+       sstrncpy((char*)dst_attr_p->attr.srtp_context.mki,
+                (char*)src_attr_p->attr.srtp_context.mki,
+                SDP_SRTP_MAX_MKI_SIZE_BYTES);
+
+       dst_attr_p->attr.srtp_context.mki_size_bytes =
+                   src_attr_p->attr.srtp_context.mki_size_bytes;
+
+       if (src_attr_p->attr.srtp_context.session_parameters) {
+           dst_attr_p->attr.srtp_context.session_parameters =
+                       cpr_strdup(src_attr_p->attr.srtp_context.session_parameters);
+       }
+
+        break;
+
+    default:
+        break;
+    }
+
+    return;
+}
+
+/* Function:    sdp_copy_attr
+ * Description: Copy an attribute from one SDP/level to another. A new
+ *              attribute is automatically added to the end of the attr
+ *              list at the dst SDP/level and all parameter values are
+ *              copied.
+ * Description: Add a new attribute of the specified type at the given
+ *              level and capability level or base attribute if cap_num
+ *              is zero.
+ * Parameters:  src_sdp_ptr The source SDP handle.
+ *              dst_sdp_ptr The dest SDP handle.
+ *              src_level   The level of the source attribute.
+ *              dst_level   The level of the source attribute.
+ *              src_cap_num The src capability number associated with the
+ *                          attribute if any.
+ *              dst_cap_num The dst capability number associated with the
+ *                          attribute if any. Note that src and dst
+ *                          cap_num must both be zero or both be non-zero.
+ *              src_attr_type  The type of source attribute. This cannot
+ *                             be SDP_ATTR_X_CAP or SDP_ATTR_X_CPAR.
+ *              src_inst_num   The instance number of the source attr.
+ * Returns:     SDP_SUCCESS    Attribute was successfully copied.
+ */
+sdp_result_e sdp_copy_attr (void *src_sdp_ptr, void *dst_sdp_ptr,
+                            u16 src_level, u16 dst_level,
+                            u8 src_cap_num, u8 dst_cap_num,
+                            sdp_attr_e src_attr_type, u16 src_inst_num)
+{
+    u16          i;
+    sdp_mca_t   *mca_p;
+    sdp_mca_t   *cap_p;
+    sdp_t       *src_sdp_p = (sdp_t *)src_sdp_ptr;
+    sdp_t       *dst_sdp_p = (sdp_t *)dst_sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_attr_t  *new_attr_p;
+    sdp_attr_t  *prev_attr_p;
+    sdp_attr_t  *src_attr_p;
+
+    if (sdp_verify_sdp_ptr(src_sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    /* Make sure if one is a capability attribute, then both are. */
+    if (((src_cap_num == 0) && (dst_cap_num != 0)) ||
+        ((src_cap_num != 0) && (dst_cap_num == 0))) {
+        src_sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Cannot copy X_CAP/CDSC attributes directly using this routine.
+     * You also can't copy X_CPAR/CPAR attributes by specifying them directly,
+     * but you can copy them by giving the corresponding cap_num. */
+    if ((src_attr_type == SDP_ATTR_X_CAP) ||
+        (src_attr_type == SDP_ATTR_X_CPAR) ||
+       (src_attr_type == SDP_ATTR_CDSC) ||
+        (src_attr_type == SDP_ATTR_CPAR)) {
+        src_sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    src_attr_p = sdp_find_attr(src_sdp_p, src_level, src_cap_num,
+                               src_attr_type, src_inst_num);
+    if (src_attr_p == NULL) {
+        if (src_sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Error: Source attribute for copy not found.",
+                      src_sdp_p->debug_str);
+        }
+        src_sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    new_attr_p = (sdp_attr_t *)SDP_MALLOC(sizeof(sdp_attr_t));
+    if (new_attr_p == NULL) {
+        src_sdp_p->conf_p->num_no_resource++;
+        return (SDP_NO_RESOURCE);
+    }
+
+    /* Copy over all the attribute information. */
+    new_attr_p->type = src_attr_type;
+    new_attr_p->next_p = NULL;
+
+    switch (src_attr_type) {
+
+    case SDP_ATTR_BEARER:
+    case SDP_ATTR_CALLED:
+    case SDP_ATTR_CONN_TYPE:
+    case SDP_ATTR_DIALED:
+    case SDP_ATTR_DIALING:
+    case SDP_ATTR_FRAMING:
+    case SDP_ATTR_MAXPRATE:
+    case SDP_ATTR_LABEL:
+        sstrncpy(new_attr_p->attr.string_val, src_attr_p->attr.string_val,
+                SDP_MAX_STRING_LEN+1);
+        break;
+
+    case SDP_ATTR_EECID:
+    case SDP_ATTR_PTIME:
+    case SDP_ATTR_T38_VERSION:
+    case SDP_ATTR_T38_MAXBITRATE:
+    case SDP_ATTR_T38_MAXBUFFER:
+    case SDP_ATTR_T38_MAXDGRAM:
+    case SDP_ATTR_X_SQN:
+    case SDP_ATTR_TC1_PAYLOAD_BYTES:
+    case SDP_ATTR_TC1_WINDOW_SIZE:
+    case SDP_ATTR_TC2_PAYLOAD_BYTES:
+    case SDP_ATTR_TC2_WINDOW_SIZE:
+    case SDP_ATTR_RTCP:
+    case SDP_ATTR_MID:
+    case SDP_ATTR_RTCP_UNICAST:
+        new_attr_p->attr.u32_val = src_attr_p->attr.u32_val;
+        break;
+
+    case SDP_ATTR_T38_FILLBITREMOVAL:
+    case SDP_ATTR_T38_TRANSCODINGMMR:
+    case SDP_ATTR_T38_TRANSCODINGJBIG:
+    case SDP_ATTR_TMRGWXID:
+        new_attr_p->attr.boolean_val = src_attr_p->attr.boolean_val;
+        break;
+
+    case SDP_ATTR_QOS:
+    case SDP_ATTR_SECURE:
+    case SDP_ATTR_X_PC_QOS:
+    case SDP_ATTR_X_QOS:
+        new_attr_p->attr.qos.strength  = src_attr_p->attr.qos.strength;
+        new_attr_p->attr.qos.direction = src_attr_p->attr.qos.direction;
+        new_attr_p->attr.qos.confirm   = src_attr_p->attr.qos.confirm;
+        break;
+
+    case SDP_ATTR_CURR:
+        new_attr_p->attr.curr.type         = src_attr_p->attr.curr.type;
+        new_attr_p->attr.curr.direction    = src_attr_p->attr.curr.direction;
+        new_attr_p->attr.curr.status_type  = src_attr_p->attr.curr.status_type;
+        break;
+    case SDP_ATTR_DES:
+        new_attr_p->attr.des.type         = src_attr_p->attr.des.type;
+        new_attr_p->attr.des.direction    = src_attr_p->attr.des.direction;
+        new_attr_p->attr.des.status_type  = src_attr_p->attr.des.status_type;
+        new_attr_p->attr.des.strength     = src_attr_p->attr.des.strength;
+        break;
+
+    case SDP_ATTR_CONF:
+        new_attr_p->attr.conf.type         = src_attr_p->attr.conf.type;
+        new_attr_p->attr.conf.direction    = src_attr_p->attr.conf.direction;
+        new_attr_p->attr.conf.status_type  = src_attr_p->attr.conf.status_type;
+        break;
+
+    case SDP_ATTR_INACTIVE:
+    case SDP_ATTR_RECVONLY:
+    case SDP_ATTR_SENDONLY:
+    case SDP_ATTR_SENDRECV:
+        /* These attrs have no parameters. */
+        break;
+
+    case SDP_ATTR_FMTP:
+        new_attr_p->attr.fmtp.payload_num = src_attr_p->attr.fmtp.payload_num;
+        new_attr_p->attr.fmtp.maxval      = src_attr_p->attr.fmtp.maxval;
+       new_attr_p->attr.fmtp.bitrate     = src_attr_p->attr.fmtp.bitrate;
+       new_attr_p->attr.fmtp.annexa      = src_attr_p->attr.fmtp.annexa;
+       new_attr_p->attr.fmtp.annexb      = src_attr_p->attr.fmtp.annexb;
+       new_attr_p->attr.fmtp.cif      = src_attr_p->attr.fmtp.cif;
+       new_attr_p->attr.fmtp.qcif      = src_attr_p->attr.fmtp.qcif;
+       new_attr_p->attr.fmtp.sqcif      = src_attr_p->attr.fmtp.qcif;
+       new_attr_p->attr.fmtp.cif4      = src_attr_p->attr.fmtp.cif4;
+       new_attr_p->attr.fmtp.cif16      = src_attr_p->attr.fmtp.cif16;
+       new_attr_p->attr.fmtp.maxbr      = src_attr_p->attr.fmtp.maxbr;
+       new_attr_p->attr.fmtp.custom_x      = src_attr_p->attr.fmtp.custom_x;
+       new_attr_p->attr.fmtp.custom_y      = src_attr_p->attr.fmtp.custom_y;
+       new_attr_p->attr.fmtp.custom_mpi    = src_attr_p->attr.fmtp.custom_mpi;
+       new_attr_p->attr.fmtp.par_width      = src_attr_p->attr.fmtp.par_width;
+       new_attr_p->attr.fmtp.par_height      = src_attr_p->attr.fmtp.par_height;
+       new_attr_p->attr.fmtp.cpcf      = src_attr_p->attr.fmtp.cpcf;
+       new_attr_p->attr.fmtp.bpp      = src_attr_p->attr.fmtp.bpp;
+       new_attr_p->attr.fmtp.hrd      = src_attr_p->attr.fmtp.hrd;
+        new_attr_p->attr.fmtp.profile    = src_attr_p->attr.fmtp.profile;
+        new_attr_p->attr.fmtp.level      = src_attr_p->attr.fmtp.level;
+        new_attr_p->attr.fmtp.is_interlace = src_attr_p->attr.fmtp.is_interlace;
+
+        sstrncpy(new_attr_p->attr.fmtp.profile_level_id,
+                src_attr_p->attr.fmtp.profile_level_id,
+                SDP_MAX_STRING_LEN+1);
+        sstrncpy(new_attr_p->attr.fmtp.parameter_sets,
+                src_attr_p->attr.fmtp.parameter_sets,
+                SDP_MAX_STRING_LEN+1);
+        new_attr_p->attr.fmtp.deint_buf_req =
+                src_attr_p->attr.fmtp.deint_buf_req;
+        new_attr_p->attr.fmtp.max_don_diff =
+            src_attr_p->attr.fmtp.max_don_diff;
+        new_attr_p->attr.fmtp.init_buf_time =
+                 src_attr_p->attr.fmtp.init_buf_time;
+        new_attr_p->attr.fmtp.packetization_mode =
+                 src_attr_p->attr.fmtp.packetization_mode;
+        new_attr_p->attr.fmtp.flag =
+                 src_attr_p->attr.fmtp.flag;
+
+        new_attr_p->attr.fmtp.max_mbps = src_attr_p->attr.fmtp.max_mbps;
+        new_attr_p->attr.fmtp.max_fs = src_attr_p->attr.fmtp.max_fs;
+        new_attr_p->attr.fmtp.max_cpb = src_attr_p->attr.fmtp.max_cpb;
+        new_attr_p->attr.fmtp.max_dpb = src_attr_p->attr.fmtp.max_dpb;
+        new_attr_p->attr.fmtp.max_br = src_attr_p->attr.fmtp.max_br;
+        new_attr_p->attr.fmtp.redundant_pic_cap =
+            src_attr_p->attr.fmtp.redundant_pic_cap;
+        new_attr_p->attr.fmtp.deint_buf_cap =
+                src_attr_p->attr.fmtp.deint_buf_cap;
+        new_attr_p->attr.fmtp.max_rcmd_nalu_size =
+                 src_attr_p->attr.fmtp.max_rcmd_nalu_size;
+        new_attr_p->attr.fmtp.interleaving_depth =
+                src_attr_p->attr.fmtp.interleaving_depth;
+        new_attr_p->attr.fmtp.parameter_add =
+            src_attr_p->attr.fmtp.parameter_add;
+
+        new_attr_p->attr.fmtp.annex_d = src_attr_p->attr.fmtp.annex_d;
+        new_attr_p->attr.fmtp.annex_f = src_attr_p->attr.fmtp.annex_f;
+        new_attr_p->attr.fmtp.annex_i = src_attr_p->attr.fmtp.annex_i;
+        new_attr_p->attr.fmtp.annex_j = src_attr_p->attr.fmtp.annex_j;
+        new_attr_p->attr.fmtp.annex_t = src_attr_p->attr.fmtp.annex_t;
+        new_attr_p->attr.fmtp.annex_k_val = src_attr_p->attr.fmtp.annex_k_val;
+        new_attr_p->attr.fmtp.annex_n_val = src_attr_p->attr.fmtp.annex_n_val;
+        new_attr_p->attr.fmtp.annex_p_val_picture_resize =
+            src_attr_p->attr.fmtp.annex_p_val_picture_resize;
+        new_attr_p->attr.fmtp.annex_p_val_warp  =
+            src_attr_p->attr.fmtp.annex_p_val_warp;
+
+       new_attr_p->attr.fmtp.annexb_required  =
+                                         src_attr_p->attr.fmtp.annexb_required;
+        new_attr_p->attr.fmtp.annexa_required  =
+                                         src_attr_p->attr.fmtp.annexa_required;
+       new_attr_p->attr.fmtp.fmtp_format
+                                         = src_attr_p->attr.fmtp.fmtp_format;
+
+        for (i=0; i < SDP_NE_NUM_BMAP_WORDS; i++) {
+            new_attr_p->attr.fmtp.bmap[i] = src_attr_p->attr.fmtp.bmap[i];
+        }
+        break;
+
+    case SDP_ATTR_RTPMAP:
+        new_attr_p->attr.transport_map.payload_num =
+            src_attr_p->attr.transport_map.payload_num;
+        new_attr_p->attr.transport_map.clockrate =
+          src_attr_p->attr.transport_map.clockrate;
+        new_attr_p->attr.transport_map.num_chan  =
+          src_attr_p->attr.transport_map.num_chan;
+        sstrncpy(new_attr_p->attr.transport_map.encname,
+               src_attr_p->attr.transport_map.encname,
+               SDP_MAX_STRING_LEN+1);
+        break;
+
+    case SDP_ATTR_SUBNET:
+        new_attr_p->attr.subnet.nettype  = src_attr_p->attr.subnet.nettype;
+        new_attr_p->attr.subnet.addrtype = src_attr_p->attr.subnet.addrtype;
+        new_attr_p->attr.subnet.prefix   = src_attr_p->attr.subnet.prefix;
+        sstrncpy(new_attr_p->attr.subnet.addr, src_attr_p->attr.subnet.addr,
+                SDP_MAX_STRING_LEN+1);
+        break;
+
+    case SDP_ATTR_T38_RATEMGMT:
+        new_attr_p->attr.t38ratemgmt = src_attr_p->attr.t38ratemgmt;
+        break;
+
+    case SDP_ATTR_T38_UDPEC:
+        new_attr_p->attr.t38udpec = src_attr_p->attr.t38udpec;
+        break;
+
+    case SDP_ATTR_X_PC_CODEC:
+        new_attr_p->attr.pccodec.num_payloads =
+            src_attr_p->attr.pccodec.num_payloads;
+        for (i=0; i < src_attr_p->attr.pccodec.num_payloads; i++) {
+            new_attr_p->attr.pccodec.payload_type[i] =
+                src_attr_p->attr.pccodec.payload_type[i];
+        }
+        break;
+
+    case SDP_ATTR_DIRECTION:
+
+       new_attr_p->attr.comediadir.role =
+           src_attr_p->attr.comediadir.role;
+
+       if (src_attr_p->attr.comediadir.conn_info.nettype) {
+           new_attr_p->attr.comediadir.conn_info_present = TRUE;
+           new_attr_p->attr.comediadir.conn_info.nettype =
+               src_attr_p->attr.comediadir.conn_info.nettype;
+           new_attr_p->attr.comediadir.conn_info.addrtype =
+               src_attr_p->attr.comediadir.conn_info.addrtype;
+           sstrncpy(new_attr_p->attr.comediadir.conn_info.conn_addr,
+                    src_attr_p->attr.comediadir.conn_info.conn_addr,
+                    SDP_MAX_STRING_LEN+1);
+           new_attr_p->attr.comediadir.src_port =
+               src_attr_p->attr.comediadir.src_port;
+       }
+       break;
+
+
+    case SDP_ATTR_SILENCESUPP:
+        new_attr_p->attr.silencesupp.enabled =
+            src_attr_p->attr.silencesupp.enabled;
+        new_attr_p->attr.silencesupp.timer_null =
+            src_attr_p->attr.silencesupp.timer_null;
+        new_attr_p->attr.silencesupp.timer =
+            src_attr_p->attr.silencesupp.timer;
+        new_attr_p->attr.silencesupp.pref =
+            src_attr_p->attr.silencesupp.pref;
+        new_attr_p->attr.silencesupp.siduse =
+            src_attr_p->attr.silencesupp.siduse;
+        new_attr_p->attr.silencesupp.fxnslevel_null =
+            src_attr_p->attr.silencesupp.fxnslevel_null;
+        new_attr_p->attr.silencesupp.fxnslevel =
+            src_attr_p->attr.silencesupp.fxnslevel;
+        break;
+
+    case SDP_ATTR_MPTIME:
+        new_attr_p->attr.mptime.num_intervals =
+            src_attr_p->attr.mptime.num_intervals;
+        for (i=0; i < src_attr_p->attr.mptime.num_intervals; i++) {
+            new_attr_p->attr.mptime.intervals[i] =
+                src_attr_p->attr.mptime.intervals[i];
+        }
+        break;
+
+    case SDP_ATTR_X_SIDIN:
+    case SDP_ATTR_X_SIDOUT:
+    case SDP_ATTR_X_CONFID:
+        sstrncpy(new_attr_p->attr.stream_data.x_sidin,
+                 src_attr_p->attr.stream_data.x_sidin,SDP_MAX_STRING_LEN+1);
+        sstrncpy(new_attr_p->attr.stream_data.x_sidout,
+                 src_attr_p->attr.stream_data.x_sidout,SDP_MAX_STRING_LEN+1);
+        sstrncpy(new_attr_p->attr.stream_data.x_confid,
+                 src_attr_p->attr.stream_data.x_confid,SDP_MAX_STRING_LEN+1);
+       break;
+
+    case SDP_ATTR_GROUP:
+        new_attr_p->attr.stream_data.group_attr =
+            src_attr_p->attr.stream_data.group_attr;
+        new_attr_p->attr.stream_data.num_group_id =
+            src_attr_p->attr.stream_data.num_group_id;
+
+        for (i=0; i < src_attr_p->attr.stream_data.num_group_id; i++) {
+            new_attr_p->attr.stream_data.group_id_arr[i] =
+                src_attr_p->attr.stream_data.group_id_arr[i];
+        }
+        break;
+
+    case SDP_ATTR_SOURCE_FILTER:
+        new_attr_p->attr.source_filter.mode =
+             src_attr_p->attr.source_filter.mode;
+        new_attr_p->attr.source_filter.nettype =
+                         src_attr_p->attr.source_filter.nettype;
+        new_attr_p->attr.source_filter.addrtype =
+                         src_attr_p->attr.source_filter.addrtype;
+        sstrncpy(new_attr_p->attr.source_filter.dest_addr,
+                 src_attr_p->attr.source_filter.dest_addr,
+                 SDP_MAX_STRING_LEN+1);
+        for (i=0; i<src_attr_p->attr.source_filter.num_src_addr; ++i) {
+             sstrncpy(new_attr_p->attr.source_filter.src_list[i],
+                     src_attr_p->attr.source_filter.src_list[i],
+                     SDP_MAX_STRING_LEN+1);
+        }
+        new_attr_p->attr.source_filter.num_src_addr =
+                src_attr_p->attr.source_filter.num_src_addr;
+        break;
+
+    case SDP_ATTR_SRTP_CONTEXT:
+    case SDP_ATTR_SDESCRIPTIONS:
+        /* Tag parameter is only valid for version 9 sdescriptions */
+        if (src_attr_type == SDP_ATTR_SDESCRIPTIONS) {
+            new_attr_p->attr.srtp_context.tag =
+               src_attr_p->attr.srtp_context.tag;
+       }
+
+       new_attr_p->attr.srtp_context.suite =
+                   src_attr_p->attr.srtp_context.suite;
+
+       new_attr_p->attr.srtp_context.selection_flags =
+                   src_attr_p->attr.srtp_context.selection_flags;
+
+       new_attr_p->attr.srtp_context.master_key_size_bytes =
+                   src_attr_p->attr.srtp_context.master_key_size_bytes;
+
+        new_attr_p->attr.srtp_context.master_salt_size_bytes =
+                   src_attr_p->attr.srtp_context.master_salt_size_bytes;
+
+        bcopy(src_attr_p->attr.srtp_context.master_key,
+             new_attr_p->attr.srtp_context.master_key,
+             SDP_SRTP_MAX_KEY_SIZE_BYTES);
+
+       bcopy(src_attr_p->attr.srtp_context.master_salt,
+             new_attr_p->attr.srtp_context.master_salt,
+             SDP_SRTP_MAX_SALT_SIZE_BYTES);
+
+
+       sstrncpy((char*)new_attr_p->attr.srtp_context.master_key_lifetime,
+                (char*)src_attr_p->attr.srtp_context.master_key_lifetime,
+                SDP_SRTP_MAX_LIFETIME_BYTES);
+
+       sstrncpy((char*)new_attr_p->attr.srtp_context.mki,
+                (char*)src_attr_p->attr.srtp_context.mki,
+                SDP_SRTP_MAX_MKI_SIZE_BYTES);
+
+       new_attr_p->attr.srtp_context.mki_size_bytes =
+                   src_attr_p->attr.srtp_context.mki_size_bytes;
+
+       if (src_attr_p->attr.srtp_context.session_parameters) {
+           new_attr_p->attr.srtp_context.session_parameters =
+                       cpr_strdup(src_attr_p->attr.srtp_context.session_parameters);
+       }
+
+        break;
+
+    default:
+        break;
+    }
+
+    if (src_cap_num == 0) {
+        if (dst_level == SDP_SESSION_LEVEL) {
+            if (dst_sdp_p->sess_attrs_p == NULL) {
+                dst_sdp_p->sess_attrs_p = new_attr_p;
+            } else {
+                for (prev_attr_p = dst_sdp_p->sess_attrs_p;
+                     prev_attr_p->next_p != NULL;
+                     prev_attr_p = prev_attr_p->next_p) {
+                    ; /* Empty for */
+                }
+                prev_attr_p->next_p = new_attr_p;
+            }
+        } else {
+            mca_p = sdp_find_media_level(dst_sdp_p, dst_level);
+            if (mca_p == NULL) {
+                sdp_free_attr(new_attr_p);
+                src_sdp_p->conf_p->num_invalid_param++;
+                return (SDP_INVALID_PARAMETER);
+            }
+            if (mca_p->media_attrs_p == NULL) {
+                mca_p->media_attrs_p = new_attr_p;
+            } else {
+                for (prev_attr_p = mca_p->media_attrs_p;
+                     prev_attr_p->next_p != NULL;
+                     prev_attr_p = prev_attr_p->next_p) {
+                    ; /* Empty for */
+                }
+                prev_attr_p->next_p = new_attr_p;
+            }
+        }
+    } else {
+        /* Add a new capability attribute - find the capability attr. */
+        attr_p = sdp_find_capability(dst_sdp_p, dst_level, dst_cap_num);
+        if (attr_p == NULL) {
+            sdp_free_attr(new_attr_p);
+            src_sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        cap_p = attr_p->attr.cap_p;
+        if (cap_p->media_attrs_p == NULL) {
+            cap_p->media_attrs_p = new_attr_p;
+        } else {
+            for (prev_attr_p = cap_p->media_attrs_p;
+                 prev_attr_p->next_p != NULL;
+                 prev_attr_p = prev_attr_p->next_p) {
+                 ; /* Empty for */
+            }
+            prev_attr_p->next_p = new_attr_p;
+        }
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_copy_all_attrs
+ * Description: Copy all attributes from one SDP/level to another.
+ * Parameters:  src_sdp_ptr The source SDP handle.
+ *              dst_sdp_ptr The dest SDP handle.
+ *              src_level   The level of the source attributes.
+ *              dst_level   The level of the source attributes.
+ * Returns:     SDP_SUCCESS    Attributes were successfully copied.
+ */
+sdp_result_e sdp_copy_all_attrs (void *src_sdp_ptr, void *dst_sdp_ptr,
+                                 u16 src_level, u16 dst_level)
+{
+    int i;
+    sdp_mca_t   *mca_p = NULL;
+    sdp_t       *src_sdp_p = (sdp_t *)src_sdp_ptr;
+    sdp_t       *dst_sdp_p = (sdp_t *)dst_sdp_ptr;
+    sdp_mca_t   *src_cap_p;
+    sdp_mca_t   *dst_cap_p;
+    sdp_attr_t  *src_attr_p;
+    sdp_attr_t  *dst_attr_p;
+    sdp_attr_t  *new_attr_p;
+    sdp_attr_t  *src_cap_attr_p;
+    sdp_attr_t  *dst_cap_attr_p;
+    sdp_attr_t  *new_cap_attr_p;
+
+    if (sdp_verify_sdp_ptr(src_sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_verify_sdp_ptr(dst_sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    /* Find src attribute list. */
+    if (src_level == SDP_SESSION_LEVEL) {
+        src_attr_p = src_sdp_p->sess_attrs_p;
+    } else {
+        mca_p = sdp_find_media_level(src_sdp_p, src_level);
+        if (mca_p == NULL) {
+            if (src_sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s Invalid src media level (%u) for copy all "
+                          "attrs ", src_sdp_p->debug_str, src_level);
+            }
+            return (SDP_INVALID_PARAMETER);
+        }
+        src_attr_p = mca_p->media_attrs_p;
+    }
+
+    /* Find dst attribute list. */
+    if (dst_level == SDP_SESSION_LEVEL) {
+        dst_attr_p = dst_sdp_p->sess_attrs_p;
+    } else {
+        mca_p = sdp_find_media_level(dst_sdp_p, dst_level);
+        if (mca_p == NULL) {
+            if (src_sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s Invalid dst media level (%u) for copy all "
+                          "attrs ", src_sdp_p->debug_str, dst_level);
+            }
+            return (SDP_INVALID_PARAMETER);
+        }
+        dst_attr_p = mca_p->media_attrs_p;
+    }
+
+    /* Now find the end of the dst attr list.  This is where we will
+     * add new attributes. */
+    while ((dst_attr_p != NULL) && (dst_attr_p->next_p != NULL)) {
+        dst_attr_p = dst_attr_p->next_p;
+    }
+
+    /* For each src attribute, allocate a new dst attr and copy the info */
+    while (src_attr_p != NULL) {
+
+        /* Allocate the new attr. */
+        new_attr_p = (sdp_attr_t *)SDP_MALLOC(sizeof(sdp_attr_t));
+        if (new_attr_p == NULL) {
+            src_sdp_p->conf_p->num_no_resource++;
+            return (SDP_NO_RESOURCE);
+        }
+
+        if ((src_attr_p->type != SDP_ATTR_X_CAP) &&
+           (src_attr_p->type != SDP_ATTR_CDSC)) {
+            /* Simple attr type - copy over all the attr info. */
+            sdp_copy_attr_fields(src_attr_p, new_attr_p);
+        } else {
+            /* X-cap/cdsc attrs must be handled differently. Allocate an
+             * mca structure and copy over any X-cpar/cdsc attrs. */
+
+            new_attr_p->attr.cap_p =
+                (sdp_mca_t *)SDP_MALLOC(sizeof(sdp_mca_t));
+            if (new_attr_p->attr.cap_p == NULL) {
+               sdp_free_attr(new_attr_p);
+                return (SDP_NO_RESOURCE);
+            }
+
+            new_attr_p->type = src_attr_p->type;
+
+            src_cap_p = src_attr_p->attr.cap_p;
+            dst_cap_p = new_attr_p->attr.cap_p;
+
+            dst_cap_p->media         = src_cap_p->media;
+            dst_cap_p->conn.nettype  = src_cap_p->conn.nettype;
+            dst_cap_p->conn.addrtype = src_cap_p->conn.addrtype;
+            sstrncpy(dst_cap_p->conn.conn_addr, src_cap_p->conn.conn_addr,
+                     SDP_MAX_LINE_LEN+1);
+            dst_cap_p->transport     = src_cap_p->transport;
+            dst_cap_p->port_format   = src_cap_p->port_format;
+            dst_cap_p->port          = src_cap_p->port;
+            dst_cap_p->num_ports     = src_cap_p->num_ports;
+            dst_cap_p->vpi           = src_cap_p->vpi;
+            dst_cap_p->vci           = src_cap_p->vci;
+            dst_cap_p->vcci          = src_cap_p->vcci;
+            dst_cap_p->cid           = src_cap_p->cid;
+            dst_cap_p->num_payloads  = src_cap_p->num_payloads;
+            dst_cap_p->mid           = src_cap_p->mid;
+
+            for (i=0; i < SDP_MAX_PAYLOAD_TYPES; i++) {
+                new_attr_p->attr.cap_p->payload_indicator[i] =
+                    src_attr_p->attr.cap_p->payload_indicator[i];
+                new_attr_p->attr.cap_p->payload_type[i] =
+                    src_attr_p->attr.cap_p->payload_type[i];
+            }
+
+            src_cap_attr_p = src_attr_p->attr.cap_p->media_attrs_p;
+            dst_cap_attr_p = NULL;
+
+            /* Copy all of the X-cpar/cpar attrs from the src. */
+            while (src_cap_attr_p != NULL) {
+
+                new_cap_attr_p = (sdp_attr_t *)SDP_MALLOC(sizeof(sdp_attr_t));
+                if (new_cap_attr_p == NULL) {
+                   sdp_free_attr (new_attr_p);
+                    return (SDP_NO_RESOURCE);
+                }
+
+                /* Copy X-cpar/cpar attribute info. */
+                sdp_copy_attr_fields(src_cap_attr_p, new_cap_attr_p);
+
+                /* Now add the new X-cpar/cpar attr in the right place. */
+                if (dst_cap_attr_p == NULL) {
+                    new_attr_p->attr.cap_p->media_attrs_p = new_cap_attr_p;
+                    dst_cap_attr_p = new_cap_attr_p;
+                } else {
+                    dst_cap_attr_p->next_p = new_cap_attr_p;
+                    dst_cap_attr_p = new_cap_attr_p;
+                }
+
+                /* Move to the next X-cpar/cpar attr. */
+                src_cap_attr_p = src_cap_attr_p->next_p;
+            }
+
+        }
+
+        /* New add the new base attr at the correct place. */
+        if (dst_attr_p == NULL) {
+            if (dst_level == SDP_SESSION_LEVEL) {
+                dst_sdp_p->sess_attrs_p = new_attr_p;
+                dst_attr_p = dst_sdp_p->sess_attrs_p;
+            } else {
+                mca_p->media_attrs_p = new_attr_p;
+                dst_attr_p = mca_p->media_attrs_p;
+            }
+        } else {
+            dst_attr_p->next_p = new_attr_p;
+            dst_attr_p = dst_attr_p->next_p;
+        }
+
+        /* Now move on to the next src attr. */
+        src_attr_p = src_attr_p->next_p;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_num_instances
+ * Description: Get the number of attributes of the specified type at
+ *              the given level and capability level.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              attr_type   The type of attribute to add.
+ *              num_attr_inst Pointer to a u16 in which to return the
+ *                          number of attributes.
+ * Returns:     SDP_SUCCESS            Attribute was added successfully.
+ *              SDP_INVALID_PARAMETER  Specified media line is not defined.
+ */
+sdp_result_e sdp_attr_num_instances (void *sdp_ptr, u16 level, u8 cap_num,
+                                     sdp_attr_e attr_type, u16 *num_attr_inst)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_result_e rc;
+    static char  fname[] = "attr_num_instances";
+
+    *num_attr_inst = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    rc = sdp_find_attr_list(sdp_p, level, cap_num, &attr_p, fname);
+    if (rc == SDP_SUCCESS) {
+        /* Found the attr list. Count the number of attrs of the given
+         * type at this level. */
+        for (; attr_p != NULL; attr_p = attr_p->next_p) {
+            if (attr_p->type == attr_type) {
+                (*num_attr_inst)++;
+            }
+        }
+
+    }
+
+    return (rc);
+}
+
+
+/* Function:    sdp_get_total_attrs
+ * Description: Get the total number of attributes at the given level and
+ *              capability level.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              num_attrs   Pointer to a u16 in which to return the
+ *                          number of attributes.
+ * Returns:     SDP_SUCCESS            Attribute was added successfully.
+ *              SDP_INVALID_PARAMETER  Specified media line is not defined.
+ */
+sdp_result_e sdp_get_total_attrs (void *sdp_ptr, u16 level, u8 cap_num,
+                                  u16 *num_attrs)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_result_e rc;
+    static char  fname[] = "get_total_attrs";
+
+    *num_attrs = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    rc = sdp_find_attr_list(sdp_p, level, cap_num, &attr_p, fname);
+    if (rc == SDP_SUCCESS) {
+        /* Found the attr list. Count the total number of attrs
+         * at this level. */
+        for (; attr_p != NULL; attr_p = attr_p->next_p) {
+            (*num_attrs)++;
+        }
+
+    }
+
+    return (rc);
+}
+
+
+/* Function:    sdp_get_attr_type
+ * Description: Given an overall attribute number at a specified level, i.e.,
+ *              attr number is not specific to the type of attribute, return
+ *              the attribute type and the instance number of that particular
+ *              type of attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              attr_num    Attribute number to retrieve. This is an overall
+ *                          attribute number over all attrs at this level.
+ *                          Range is (1 - max attrs at this level).
+ * Returns:     SDP_SUCCESS            Attribute was added successfully.
+ *              SDP_INVALID_PARAMETER  Specified media line is not defined.
+ */
+sdp_result_e sdp_get_attr_type (void *sdp_ptr, u16 level, u8 cap_num,
+                           u16 attr_num, sdp_attr_e *attr_type, u16 *inst_num)
+{
+    int          i;
+    u16          attr_total_count=0;
+    u16          attr_count[SDP_MAX_ATTR_TYPES];
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_result_e rc;
+    static char  fname[] = "get_attr_type";
+
+    *attr_type = SDP_ATTR_INVALID;
+    *inst_num = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (attr_num < 1) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s, invalid attr num specified (%u) at level %u",
+                      sdp_p->debug_str, fname, attr_num, level);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    for (i=0; i < SDP_MAX_ATTR_TYPES; i++) {
+        attr_count[i] = 0;
+    }
+
+    rc = sdp_find_attr_list(sdp_p, level, cap_num, &attr_p, fname);
+    if (rc == SDP_SUCCESS) {
+        /* Found the attr list. Now find the particular attribute
+         * at the given level. */
+        for (; attr_p != NULL; attr_p = attr_p->next_p) {
+            attr_count[attr_p->type]++;
+            if (++attr_total_count == attr_num) {
+                *attr_type = attr_p->type;
+                *inst_num = attr_count[attr_p->type];
+                break;
+            }
+        }
+
+    }
+
+    return (rc);
+}
+
+
+/* Internal routine to free the memory associated with an attribute.
+ * Certain attributes allocate additional memory.  Free this and then
+ * free the attribute itself.
+ * Note that this routine may be called at any point (i.e., may be
+ * called due to a failure case) and so the additional memory
+ * associated with an attribute may or may not have been already
+ * allocated. This routine should check this carefully.
+ */
+void sdp_free_attr (sdp_attr_t *attr_p)
+{
+    sdp_mca_t   *cap_p;
+    sdp_attr_t  *cpar_p;
+    sdp_attr_t  *next_cpar_p;
+
+    /* If this is an X-cap/cdsc attr, free the cap_p structure and
+     * all X-cpar/cpar attributes. */
+    if ((attr_p->type == SDP_ATTR_X_CAP) ||
+       (attr_p->type == SDP_ATTR_CDSC)) {
+        cap_p = attr_p->attr.cap_p;
+        if (cap_p != NULL) {
+            for (cpar_p = cap_p->media_attrs_p; cpar_p != NULL;) {
+                next_cpar_p = cpar_p->next_p;
+                sdp_free_attr(cpar_p);
+                cpar_p = next_cpar_p;
+            }
+            SDP_FREE(cap_p);
+        }
+    } else if ((attr_p->type == SDP_ATTR_SDESCRIPTIONS) ||
+              (attr_p->type == SDP_ATTR_SRTP_CONTEXT)) {
+              SDP_FREE(attr_p->attr.srtp_context.session_parameters);
+    }
+
+    /* Now free the actual attribute memory. */
+    SDP_FREE(attr_p);
+
+}
+
+
+/* Function:    sdp_delete_attr
+ * Description: Delete the specified attribute from the SDP structure.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              attr_type   The type of attribute to delete.
+ *              inst_num    The instance num of the attribute to delete.
+ * Returns:     SDP_SUCCESS            Attribute was deleted successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_delete_attr (void *sdp_ptr, u16 level, u8 cap_num,
+                              sdp_attr_e attr_type, u16 inst_num)
+{
+    u16          attr_count=0;
+    sdp_mca_t   *mca_p;
+    sdp_mca_t   *cap_p;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_attr_t  *prev_attr_p = NULL;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (cap_num == 0) {
+        /* Find and delete the specified instance. */
+        if (level == SDP_SESSION_LEVEL) {
+            for (attr_p = sdp_p->sess_attrs_p; attr_p != NULL;
+                 prev_attr_p = attr_p, attr_p = attr_p->next_p) {
+                if (attr_p->type == attr_type) {
+                    attr_count++;
+                    if (attr_count == inst_num) {
+                        break;
+                    }
+                }
+            }
+            if (attr_p == NULL) {
+                if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                    CSFLogError(logTag, "%s Delete attribute (%s) instance %d not "
+                              "found.", sdp_p->debug_str,
+                              sdp_get_attr_name(attr_type), inst_num);
+                }
+                sdp_p->conf_p->num_invalid_param++;
+                return (SDP_INVALID_PARAMETER);
+            }
+            if (prev_attr_p == NULL) {
+                sdp_p->sess_attrs_p = attr_p->next_p;
+            } else {
+                prev_attr_p->next_p = attr_p->next_p;
+            }
+            sdp_free_attr(attr_p);
+        } else {  /* Attr is at a media level */
+            mca_p = sdp_find_media_level(sdp_p, level);
+            if (mca_p == NULL) {
+                sdp_p->conf_p->num_invalid_param++;
+                return (SDP_INVALID_PARAMETER);
+            }
+            for (attr_p = mca_p->media_attrs_p; attr_p != NULL;
+                 prev_attr_p = attr_p, attr_p = attr_p->next_p) {
+                if (attr_p->type == attr_type) {
+                    attr_count++;
+                    if (attr_count == inst_num) {
+                        break;
+                    }
+                }
+            }
+            if (attr_p == NULL) {
+                if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                    CSFLogError(logTag, "%s Delete attribute (%s) instance %d "
+                              "not found.", sdp_p->debug_str,
+                              sdp_get_attr_name(attr_type), inst_num);
+                }
+                sdp_p->conf_p->num_invalid_param++;
+                return (SDP_INVALID_PARAMETER);
+            }
+            if (prev_attr_p == NULL) {
+                mca_p->media_attrs_p = attr_p->next_p;
+            } else {
+                prev_attr_p->next_p = attr_p->next_p;
+            }
+            sdp_free_attr(attr_p);
+        }  /* Attr is at a media level */
+    } else {
+        /* Attr is a capability X-cpar/cpar attribute, find the capability. */
+        attr_p = sdp_find_capability(sdp_p, level, cap_num);
+        if (attr_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        cap_p = attr_p->attr.cap_p;
+        /* Now find the specific attribute to delete. */
+        for (attr_p = cap_p->media_attrs_p; attr_p != NULL;
+             prev_attr_p = attr_p, attr_p = attr_p->next_p) {
+            if (attr_p->type == attr_type) {
+                attr_count++;
+                if (attr_count == inst_num) {
+                    break;
+                }
+            }
+        }
+        if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s Delete X-cpar/cpar attribute (%s) cap_num %u, "
+                          "instance %d not found.", sdp_p->debug_str,
+                          sdp_get_attr_name(attr_type), cap_num, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        if (prev_attr_p == NULL) {
+            cap_p->media_attrs_p = attr_p->next_p;
+        } else {
+            prev_attr_p->next_p = attr_p->next_p;
+        }
+        sdp_free_attr(attr_p);
+    }
+
+    return (SDP_SUCCESS);
+}
+
+
+/* Function:    sdp_delete_all_attrs
+ * Description: Delete all attributes at the specified level from
+ *              the SDP structure.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ * Returns:     SDP_SUCCESS            Attributes were deleted successfully.
+ */
+sdp_result_e sdp_delete_all_attrs (void *sdp_ptr, u16 level, u8 cap_num)
+{
+    sdp_mca_t   *mca_p;
+    sdp_mca_t   *cap_p;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_attr_t  *next_attr_p = NULL;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (cap_num == 0) {
+        if (level == SDP_SESSION_LEVEL) {
+            attr_p = sdp_p->sess_attrs_p;
+            while (attr_p != NULL) {
+                next_attr_p = attr_p->next_p;
+                sdp_free_attr(attr_p);
+                attr_p = next_attr_p;
+            }
+            sdp_p->sess_attrs_p = NULL;
+        } else {  /* Attr is at a media level */
+            mca_p = sdp_find_media_level(sdp_p, level);
+            if (mca_p == NULL) {
+                sdp_p->conf_p->num_invalid_param++;
+                return (SDP_INVALID_PARAMETER);
+            }
+            attr_p = mca_p->media_attrs_p;
+            while (attr_p != NULL) {
+                next_attr_p = attr_p->next_p;
+                sdp_free_attr(attr_p);
+                attr_p = next_attr_p;
+            }
+            mca_p->media_attrs_p = NULL;
+        }
+    } else {
+        /* Attr is a capability X-cpar/cpar attribute, find the capability. */
+        attr_p = sdp_find_capability(sdp_p, level, cap_num);
+        if (attr_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        cap_p = attr_p->attr.cap_p;
+        /* Now find the specific attribute to delete. */
+        attr_p = cap_p->media_attrs_p;
+        while (attr_p != NULL) {
+            next_attr_p = attr_p->next_p;
+            sdp_free_attr(attr_p);
+            attr_p = next_attr_p;
+        }
+        cap_p->media_attrs_p = NULL;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+
+/* Function:    sdp_find_attr_list
+ * Description: Find the attribute list for the specified level and cap_num.
+ *              Note: This is not an API for the application but an internal
+ *              routine used by the SDP library.
+ * Parameters:  sdp_p       Pointer to the SDP to search.
+ *              level       The level to check for the attribute list.
+ *              cap_num     The capability number associated with the
+ *                          attribute list.  If none, should be zero.
+ *              attr_p      Pointer to the attr list pointer. Will be
+ *                          filled in on return if successful.
+ *              fname       String function name calling this routine.
+ *                          Use for printing debug.
+ * Returns:     SDP_SUCCESS
+ *              SDP_INVALID_MEDIA_LEVEL
+ *              SDP_INVALID_CAPABILITY
+ *              SDP_FAILURE
+ */
+sdp_result_e sdp_find_attr_list (sdp_t *sdp_p, u16 level, u8 cap_num,
+                                 sdp_attr_t **attr_p, char *fname)
+{
+    sdp_mca_t   *mca_p;
+    sdp_mca_t   *cap_p;
+    sdp_attr_t  *cap_attr_p;
+
+    /* Initialize the attr pointer. */
+    *attr_p = NULL;
+
+    if (cap_num == 0) {
+        /* Find attribute list at the specified level. */
+        if (level == SDP_SESSION_LEVEL) {
+            *attr_p = sdp_p->sess_attrs_p;
+        } else {
+            mca_p = sdp_find_media_level(sdp_p, level);
+            if (mca_p == NULL) {
+                sdp_p->conf_p->num_invalid_param++;
+                return (SDP_INVALID_PARAMETER);
+            }
+            *attr_p = mca_p->media_attrs_p;
+        }
+    } else {
+        /* Find the attr list for the capability specified. */
+        cap_attr_p = sdp_find_capability(sdp_p, level, cap_num);
+        if (cap_attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s %s, invalid capability %u at "
+                          "level %u specified.", sdp_p->debug_str, fname,
+                          cap_num, level);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_CAPABILITY);
+        }
+        cap_p = cap_attr_p->attr.cap_p;
+        *attr_p = cap_p->media_attrs_p;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_find_attr
+ * Description: Find the specified attribute in an SDP structure.
+ *              Note: This is not an API for the application but an internal
+ *              routine used by the SDP library.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              attr_type   The type of attribute to find.
+ *              inst_num    The instance num of the attribute to delete.
+ *                          Range should be (1 - max num insts of this
+ *                          particular type of attribute at this level).
+ * Returns:     Pointer to the attribute or NULL if not found.
+ */
+sdp_attr_t *sdp_find_attr (sdp_t *sdp_p, u16 level, u8 cap_num,
+                           sdp_attr_e attr_type, u16 inst_num)
+{
+    u16          attr_count=0;
+    sdp_mca_t   *mca_p;
+    sdp_mca_t   *cap_p;
+    sdp_attr_t  *attr_p;
+
+    if (inst_num < 1) {
+        return (NULL);
+    }
+
+    if (cap_num == 0) {
+        if (level == SDP_SESSION_LEVEL) {
+            for (attr_p = sdp_p->sess_attrs_p; attr_p != NULL;
+                 attr_p = attr_p->next_p) {
+                if (attr_p->type == attr_type) {
+                    attr_count++;
+                    if (attr_count == inst_num) {
+                        return (attr_p);
+                    }
+                }
+            }
+        } else {  /* Attr is at a media level */
+            mca_p = sdp_find_media_level(sdp_p, level);
+            if (mca_p == NULL) {
+                return (NULL);
+            }
+            for (attr_p = mca_p->media_attrs_p; attr_p != NULL;
+                 attr_p = attr_p->next_p) {
+                if (attr_p->type == attr_type) {
+                    attr_count++;
+                    if (attr_count == inst_num) {
+                        return (attr_p);
+                    }
+                }
+            }
+        }  /* Attr is at a media level */
+    } else {
+        /* Attr is a capability X-cpar/cpar attribute. */
+        attr_p = sdp_find_capability(sdp_p, level, cap_num);
+        if (attr_p == NULL) {
+            return (NULL);
+        }
+        cap_p = attr_p->attr.cap_p;
+        /* Now find the specific attribute. */
+        for (attr_p = cap_p->media_attrs_p; attr_p != NULL;
+             attr_p = attr_p->next_p) {
+            if (attr_p->type == attr_type) {
+                attr_count++;
+                if (attr_count == inst_num) {
+                    return (attr_p);
+                }
+            }
+        }
+    }
+
+    return (NULL);
+}
+
+/* Function:    sdp_find_capability
+ * Description: Find the specified capability attribute in an SDP structure.
+ *              Note: This is not an API for the application but an internal
+ *              routine used by the SDP library.
+ * Parameters:  sdp_p       The SDP handle.
+ *              level       The level to check for the capability.
+ *              cap_num     The capability number to locate.
+ * Returns:     Pointer to the capability attribute or NULL if not found.
+ */
+sdp_attr_t *sdp_find_capability (sdp_t *sdp_p, u16 level, u8 cap_num)
+{
+    u8           cur_cap_num=0;
+    sdp_mca_t   *mca_p;
+    sdp_mca_t   *cap_p;
+    sdp_attr_t  *attr_p;
+
+    if (level == SDP_SESSION_LEVEL) {
+        for (attr_p = sdp_p->sess_attrs_p; attr_p != NULL;
+             attr_p = attr_p->next_p) {
+            if ((attr_p->type == SDP_ATTR_X_CAP) ||
+               (attr_p->type == SDP_ATTR_CDSC)) {
+                cap_p = attr_p->attr.cap_p;
+                cur_cap_num += cap_p->num_payloads;
+                if (cap_num <= cur_cap_num) {
+                    /* This is the right capability */
+                    return (attr_p);
+                }
+            }
+        }
+    } else {  /* Capability is at a media level */
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (NULL);
+        }
+        for (attr_p = mca_p->media_attrs_p; attr_p != NULL;
+             attr_p = attr_p->next_p) {
+            if ((attr_p->type == SDP_ATTR_X_CAP) ||
+               (attr_p->type == SDP_ATTR_CDSC)) {
+                cap_p = attr_p->attr.cap_p;
+                cur_cap_num += cap_p->num_payloads;
+                if (cap_num <= cur_cap_num) {
+                    /* This is the right capability */
+                    return (attr_p);
+                }
+            }
+        }
+    }
+
+    /* We didn't find the specified capability. */
+    if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+        CSFLogError(logTag, "%s Unable to find specified capability (level %u, "
+                  "cap_num %u).", sdp_p->debug_str, level, cap_num);
+    }
+    sdp_p->conf_p->num_invalid_param++;
+    return (NULL);
+}
+
+/* Function:    sdp_attr_valid(void *sdp_ptr)
+ * Description: Returns true or false depending on whether the specified
+ *              instance of the given attribute has been defined at the
+ *              given level.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              attr_type   The attribute type to validate.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_attr_valid (void *sdp_ptr, sdp_attr_e attr_type, u16 level,
+                         u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (sdp_find_attr(sdp_p, level, cap_num, attr_type, inst_num) == NULL) {
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/* Function:    sdp_attr_get_simple_string
+ * Description: Returns a pointer to a string attribute parameter.  This
+ *              routine can only be called for attributes that have just
+ *              one string parameter.  The value is returned as a const
+ *              ptr and so cannot be modified by the application.  If the
+ *              given attribute is not defined, NULL will be returned.
+ *              Attributes with a simple string parameter currently include:
+ *              bearer, called, connection_type, dialed, dialing, direction
+ *              and framing.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              attr_type   The simple string attribute type.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Pointer to the parameter value.
+ */
+const char *sdp_attr_get_simple_string (void *sdp_ptr, sdp_attr_e attr_type,
+                                        u16 level, u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    if ((attr_type != SDP_ATTR_BEARER) &&
+        (attr_type != SDP_ATTR_CALLED) &&
+        (attr_type != SDP_ATTR_CONN_TYPE) &&
+        (attr_type != SDP_ATTR_DIALED) &&
+        (attr_type != SDP_ATTR_DIALING) &&
+        (attr_type != SDP_ATTR_FRAMING) &&
+        (attr_type != SDP_ATTR_X_SIDIN) &&
+        (attr_type != SDP_ATTR_X_SIDOUT)&&
+        (attr_type != SDP_ATTR_X_CONFID) &&
+        (attr_type != SDP_ATTR_LABEL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute type is not a simple string (%s)",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type));
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, attr_type, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute %s, level %u instance %u not found.",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type),
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    } else {
+        return (attr_p->attr.string_val);
+    }
+}
+
+/* Function:    sdp_attr_set_simple_string
+ * Description: Sets the value of a string attribute parameter.  This
+ *              routine can only be called for attributes that have just
+ *              one string parameter.  The string is copied into the SDP
+ *              structure so application memory will not be referenced by
+ *              the SDP library.
+ *              Attributes with a simple string parameter currently include:
+ *              bearer, called, connection_type, dialed, dialing, and
+ *              framing.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              attr_type   The simple string attribute type.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              string_parm Ptr to new string value.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_simple_string (void *sdp_ptr, sdp_attr_e attr_type,
+                                         u16 level, u8 cap_num,
+                                         u16 inst_num, const char *string_parm)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if ((attr_type != SDP_ATTR_BEARER) &&
+        (attr_type != SDP_ATTR_CALLED) &&
+        (attr_type != SDP_ATTR_CONN_TYPE) &&
+        (attr_type != SDP_ATTR_DIALED) &&
+        (attr_type != SDP_ATTR_DIALING) &&
+        (attr_type != SDP_ATTR_FRAMING) &&
+        (attr_type != SDP_ATTR_X_SIDIN) &&
+        (attr_type != SDP_ATTR_X_SIDOUT)&&
+        (attr_type != SDP_ATTR_X_CONFID) &&
+        (attr_type != SDP_ATTR_LABEL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute type is not a simple string (%s)",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type));
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, attr_type, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute %s, level %u instance %u not found.",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type),
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        sstrncpy(attr_p->attr.string_val, string_parm,
+                 sizeof(attr_p->attr.string_val));
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_simple_u32
+ * Description: Returns an unsigned 32-bit attribute parameter.  This
+ *              routine can only be called for attributes that have just
+ *              one u32 parameter.  If the given attribute is not defined,
+ *              zero will be returned.  There is no way for the application
+ *              to determine if zero is the actual value or the attribute
+ *              wasn't defined, so the application must use the
+ *              sdp_attr_valid function to determine this.
+ *              Attributes with a simple u32 parameter currently include:
+ *              eecid, ptime, T38FaxVersion, T38maxBitRate, T38FaxMaxBuffer,
+ *              T38FaxMaxDatagram, X-sqn, TC1PayloadBytes, TC1WindowSize,
+ *              TC2PayloadBytes, TC2WindowSize, rtcp.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              attr_type   The simple u32 attribute type.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     u32 parameter value.
+ */
+u32 sdp_attr_get_simple_u32 (void *sdp_ptr, sdp_attr_e attr_type, u16 level,
+                             u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    if ((attr_type != SDP_ATTR_EECID) &&
+        (attr_type != SDP_ATTR_PTIME) &&
+        (attr_type != SDP_ATTR_MAXPTIME) &&
+        (attr_type != SDP_ATTR_T38_VERSION) &&
+        (attr_type != SDP_ATTR_T38_MAXBITRATE) &&
+        (attr_type != SDP_ATTR_T38_MAXBUFFER) &&
+        (attr_type != SDP_ATTR_T38_MAXDGRAM) &&
+        (attr_type != SDP_ATTR_X_SQN) &&
+        (attr_type != SDP_ATTR_TC1_PAYLOAD_BYTES) &&
+        (attr_type != SDP_ATTR_TC1_WINDOW_SIZE) &&
+        (attr_type != SDP_ATTR_TC2_PAYLOAD_BYTES) &&
+        (attr_type != SDP_ATTR_TC2_WINDOW_SIZE) &&
+        (attr_type != SDP_ATTR_RTCP) &&
+        (attr_type != SDP_ATTR_MID) &&
+        (attr_type != SDP_ATTR_FRAMERATE)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute type is not a simple u32 (%s)",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type));
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, attr_type, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute %s, level %u instance %u not found.",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type),
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.u32_val);
+    }
+}
+
+/* Function:    sdp_attr_set_simple_u32
+ * Description: Sets the value of an unsigned 32-bit attribute parameter.
+ *              This routine can only be called for attributes that have just
+ *              one u32 parameter.
+ *              Attributes with a simple u32 parameter currently include:
+ *              eecid, ptime, T38FaxVersion, T38maxBitRate, T38FaxMaxBuffer,
+ *              T38FaxMaxDatagram, X-sqn, TC1PayloadBytes, TC1WindowSize,
+ *              TC2PayloadBytes, TC2WindowSize, rtcp.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              attr_type   The simple u32 attribute type.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              num_parm    New u32 parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_simple_u32 (void *sdp_ptr, sdp_attr_e attr_type,
+                           u16 level, u8 cap_num, u16 inst_num, u32 num_parm)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if ((attr_type != SDP_ATTR_EECID) &&
+        (attr_type != SDP_ATTR_PTIME) &&
+        (attr_type != SDP_ATTR_MAXPTIME) &&
+        (attr_type != SDP_ATTR_T38_VERSION) &&
+        (attr_type != SDP_ATTR_T38_MAXBITRATE) &&
+        (attr_type != SDP_ATTR_T38_MAXBUFFER) &&
+        (attr_type != SDP_ATTR_T38_MAXDGRAM) &&
+        (attr_type != SDP_ATTR_X_SQN) &&
+        (attr_type != SDP_ATTR_TC1_PAYLOAD_BYTES) &&
+        (attr_type != SDP_ATTR_TC1_WINDOW_SIZE) &&
+        (attr_type != SDP_ATTR_TC2_PAYLOAD_BYTES) &&
+        (attr_type != SDP_ATTR_TC2_WINDOW_SIZE) &&
+        (attr_type != SDP_ATTR_RTCP) &&
+        (attr_type != SDP_ATTR_MID) &&
+        (attr_type != SDP_ATTR_FRAMERATE)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute type is not a simple u32 (%s)",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type));
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, attr_type, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute %s, level %u instance %u not found.",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type),
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.u32_val = num_parm;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_attr_get_simple_boolean
+ * Description: Returns a boolean attribute parameter.  This
+ *              routine can only be called for attributes that have just
+ *              one boolean parameter.  If the given attribute is not defined,
+ *              FALSE will be returned.  There is no way for the application
+ *              to determine if FALSE is the actual value or the attribute
+ *              wasn't defined, so the application must use the
+ *              sdp_attr_valid function to determine this.
+ *              Attributes with a simple boolean parameter currently include:
+ *              T38FaxFillBitRemoval, T38FaxTranscodingMMR,
+ *              T38FaxTranscodingJBIG, and TMRGwXid.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              attr_type   The simple boolean attribute type.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Boolean value.
+ */
+tinybool sdp_attr_get_simple_boolean (void *sdp_ptr, sdp_attr_e attr_type,
+                                      u16 level, u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if ((attr_type != SDP_ATTR_T38_FILLBITREMOVAL) &&
+        (attr_type != SDP_ATTR_T38_TRANSCODINGMMR) &&
+        (attr_type != SDP_ATTR_T38_TRANSCODINGJBIG) &&
+        (attr_type != SDP_ATTR_TMRGWXID)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute type is not a simple boolean (%s)",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type));
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, attr_type, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute %s, level %u instance %u not found.",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type),
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.boolean_val);
+    }
+}
+
+/* Function:    sdp_attr_set_simple_boolean
+ * Description: Sets the value of a boolean attribute parameter.
+ *              This routine can only be called for attributes that have just
+ *              one boolean parameter.
+ *              Attributes with a simple boolean parameter currently include:
+ *              T38FaxFillBitRemoval, T38FaxTranscodingMMR,
+ *              T38FaxTranscodingJBIG, and TMRGwXid.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              attr_type   The simple boolean attribute type.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              bool_parm   New boolean parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_simple_boolean (void *sdp_ptr, sdp_attr_e attr_type,
+                                          u16 level, u8 cap_num,
+                                          u16 inst_num, u32 bool_parm)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if ((attr_type != SDP_ATTR_T38_FILLBITREMOVAL) &&
+        (attr_type != SDP_ATTR_T38_TRANSCODINGMMR) &&
+        (attr_type != SDP_ATTR_T38_TRANSCODINGJBIG) &&
+        (attr_type != SDP_ATTR_TMRGWXID)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute type is not a simple boolean (%s)",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type));
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, attr_type, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute %s, level %u instance %u not found.",
+                      sdp_p->debug_str, sdp_get_attr_name(attr_type),
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.boolean_val = (tinybool)bool_parm;
+        return (SDP_SUCCESS);
+    }
+}
+
+/*
+ * sdp_attr_get_maxprate
+ *
+ * This function is used by the application layer to get the packet-rate
+ * within the maxprate attribute.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to set.
+ *
+ * Returns a pointer to a constant char array that stores the packet-rate,
+ * OR null if the attribute does not exist.
+ */
+const char*
+sdp_attr_get_maxprate (void *sdp_ptr, u16 level, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_MAXPRATE, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute %s, level %u instance %u not found.",
+                      sdp_p->debug_str, sdp_get_attr_name(SDP_ATTR_MAXPRATE),
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    } else {
+        return (attr_p->attr.string_val);
+    }
+}
+
+/*
+ * sdp_attr_set_maxprate
+ *
+ * This function is used by the application layer to set the packet rate of
+ * the maxprate attribute line appropriately. The maxprate attribute is
+ * defined as follows:
+ *
+ *    max-p-rate-def = "a" "=" "maxprate" ":" packet-rate CRLF
+ *    packet-rate = 1*DIGIT ["." 1*DIGIT]
+ *
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to set.
+ *              string_parm A string that contains the value of packet-rate
+ *                          that needs to be advertised.
+ *
+ * Note that we use a string to set the packet-rate, so the application layer
+ * must format a string, as per the packet-rate format and pass it to this
+ * function.
+ *
+ * The decision to use a string to communicate the packet-rate was
+ * made because some operating systems do not support floating point
+ * operations.
+ *
+ * Returns:
+ * SDP_INVALID_SDP_PTR - If an invalid sdp handle is passed in.
+ * SDP_INVALID_PARAMETER - If the maxprate attribute is not defined at the
+ *                         specified level OR if the packet-rate is not
+ *                         formatted correctly in string_parm.
+ * SDP_SUCCESS - If we are successfully able to set the maxprate attribute.
+ */
+sdp_result_e
+sdp_attr_set_maxprate (void *sdp_ptr, u16 level, u16 inst_num,
+                       const char *string_parm)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_MAXPRATE, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Attribute %s, level %u instance %u not found.",
+                      sdp_p->debug_str, sdp_get_attr_name(SDP_ATTR_MAXPRATE),
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (!sdp_validate_maxprate(string_parm)) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s is not a valid maxprate value.", string_parm);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+
+        sstrncpy(attr_p->attr.string_val, string_parm,
+                 sizeof(attr_p->attr.string_val));
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_t38ratemgmt
+ * Description: Returns the value of the t38ratemgmt attribute
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_T38_UNKNOWN_RATE is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Ratemgmt value.
+ */
+sdp_t38_ratemgmt_e sdp_attr_get_t38ratemgmt (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_T38_UNKNOWN_RATE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_T38_RATEMGMT, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s t38ratemgmt attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_T38_UNKNOWN_RATE);
+    } else {
+        return (attr_p->attr.t38ratemgmt);
+    }
+}
+
+/* Function:    sdp_attr_set_t38ratemgmt
+ * Description: Sets the value of the t38ratemgmt attribute parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              t38ratemgmt New t38ratemgmt parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_t38ratemgmt (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       sdp_t38_ratemgmt_e t38ratemgmt)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_T38_RATEMGMT, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s t38ratemgmt attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.t38ratemgmt = t38ratemgmt;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_attr_get_t38udpec
+ * Description: Returns the value of the t38udpec attribute
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_T38_UDPEC_UNKNOWN is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     UDP EC value.
+ */
+sdp_t38_udpec_e sdp_attr_get_t38udpec (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_T38_UDPEC_UNKNOWN);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_T38_UDPEC, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s t38udpec attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_T38_UDPEC_UNKNOWN);
+    } else {
+        return (attr_p->attr.t38udpec);
+    }
+}
+
+/* Function:    sdp_attr_set_t38udpec
+ * Description: Sets the value of the t38ratemgmt attribute parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              t38udpec    New t38udpec parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_t38udpec (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num,
+                                    sdp_t38_udpec_e t38udpec)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_T38_UDPEC, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s t38udpec attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.t38udpec = t38udpec;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_get_media_direction
+ * Description: Determines the direction defined for a given level.  The
+ *              direction will be inactive, sendonly, recvonly, or sendrecv
+ *              as determined by the last of these attributes specified at
+ *              the given level.  If none of these attributes are specified,
+ *              the direction will be sendrecv by default.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ * Returns:     An SDP direction enum value.
+ */
+sdp_direction_e sdp_get_media_direction (void *sdp_ptr, u16 level,
+                                         u8 cap_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t   *mca_p;
+    sdp_attr_t  *attr_p;
+    sdp_direction_e direction = SDP_DIRECTION_SENDRECV;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (direction);
+    }
+
+    if (cap_num == 0) {
+        /* Find the pointer to the attr list for this level. */
+        if (level == SDP_SESSION_LEVEL) {
+            attr_p = sdp_p->sess_attrs_p;
+        } else {  /* Attr is at a media level */
+            mca_p = sdp_find_media_level(sdp_p, level);
+            if (mca_p == NULL) {
+                return (direction);
+            }
+            attr_p = mca_p->media_attrs_p;
+        }
+
+        /* Scan for direction oriented attributes.  Last one wins. */
+        for (; attr_p != NULL; attr_p = attr_p->next_p) {
+            if (attr_p->type == SDP_ATTR_INACTIVE) {
+                direction = SDP_DIRECTION_INACTIVE;
+            } else if (attr_p->type == SDP_ATTR_SENDONLY) {
+                direction = SDP_DIRECTION_SENDONLY;
+            } else if (attr_p->type == SDP_ATTR_RECVONLY) {
+                direction = SDP_DIRECTION_RECVONLY;
+            } else if (attr_p->type == SDP_ATTR_SENDRECV) {
+                direction = SDP_DIRECTION_SENDRECV;
+            }
+        }
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid cap_num for media direction.",
+                     sdp_p->debug_str);
+        }
+    }
+
+    return (direction);
+}
+
+/*
+ * sdp_delete_all_media_direction_attrs
+ *
+ * Deletes all the media direction attributes from a given SDP level.
+ * Media direction attributes include:
+ * a=sendonly
+ * a=recvonly
+ * a=sendrecv
+ * a=inactive
+ *
+ * This function can be used in conjunction with sdp_add_new_attr, to ensure
+ * that there is only one direction attribute per level.
+ * It can also be used to delete all attributes when the client wants to
+ * advertise the default direction, i.e. a=sendrecv.
+ */
+sdp_result_e sdp_delete_all_media_direction_attrs (void *sdp_ptr, u16 level)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_mca_t   *mca_p;
+    sdp_attr_t  *attr_p;
+    sdp_attr_t  *prev_attr_p = NULL;
+    sdp_attr_t  *tmp_attr_p = NULL;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    /* Find the pointer to the attr list for this level. */
+    if (level == SDP_SESSION_LEVEL) {
+        attr_p = sdp_p->sess_attrs_p;
+        while (attr_p != NULL) {
+            if ((attr_p->type == SDP_ATTR_INACTIVE) ||
+                (attr_p->type == SDP_ATTR_SENDONLY) ||
+                (attr_p->type == SDP_ATTR_RECVONLY) ||
+                (attr_p->type == SDP_ATTR_SENDRECV)) {
+
+                tmp_attr_p = attr_p;
+
+                if (prev_attr_p == NULL) {
+                    sdp_p->sess_attrs_p = attr_p->next_p;
+                } else {
+                    prev_attr_p->next_p = attr_p->next_p;
+                }
+                attr_p = attr_p->next_p;
+
+                sdp_free_attr(tmp_attr_p);
+            } else {
+                prev_attr_p = attr_p;
+                attr_p = attr_p->next_p;
+            }
+        }
+    } else {  /* Attr is at a media level */
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_INVALID_MEDIA_LEVEL);
+        }
+
+        attr_p = mca_p->media_attrs_p;
+        while (attr_p != NULL) {
+            if ((attr_p->type == SDP_ATTR_INACTIVE) ||
+                (attr_p->type == SDP_ATTR_SENDONLY) ||
+                (attr_p->type == SDP_ATTR_RECVONLY) ||
+                (attr_p->type == SDP_ATTR_SENDRECV)) {
+
+                tmp_attr_p = attr_p;
+
+                if (prev_attr_p == NULL) {
+                    mca_p->media_attrs_p = attr_p->next_p;
+                } else {
+                    prev_attr_p->next_p = attr_p->next_p;
+                }
+                attr_p = attr_p->next_p;
+
+                sdp_free_attr(tmp_attr_p);
+            } else {
+                prev_attr_p = attr_p;
+                attr_p = attr_p->next_p;
+            }
+        }
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Since there are four different attribute names which all have the same
+ * qos parameters, all of these attributes are accessed through this same
+ * set of APIs.  To distinguish between specific attributes, the application
+ * must also pass the attribute type.  The attribute must be one of:
+ * SDP_ATTR_QOS, SDP_ATTR_SECURE, SDP_ATTR_X_PC_QOS, and SDP_ATTR_X_QOS.
+ */
+tinybool sdp_validate_qos_attr (sdp_attr_e qos_attr)
+{
+    if ((qos_attr == SDP_ATTR_QOS) ||
+        (qos_attr == SDP_ATTR_SECURE) ||
+        (qos_attr == SDP_ATTR_X_PC_QOS) ||
+        (qos_attr == SDP_ATTR_X_QOS) ||
+        (qos_attr == SDP_ATTR_CURR) ||
+        (qos_attr == SDP_ATTR_DES) ||
+        (qos_attr == SDP_ATTR_CONF)){
+        return (TRUE);
+    } else {
+        return (FALSE);
+    }
+}
+
+/* Function:    sdp_attr_get_qos_strength
+ * Description: Returns the value of the qos attribute strength
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_QOS_STRENGTH_UNKNOWN is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Qos strength value.
+ */
+sdp_qos_strength_e sdp_attr_get_qos_strength (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_QOS_STRENGTH_UNKNOWN);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid QOS attribute specified for"
+                     "get qos strength.", sdp_p->debug_str);
+        }
+        return (SDP_QOS_STRENGTH_UNKNOWN);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_QOS_STRENGTH_UNKNOWN);
+    } else {
+        switch (qos_attr) {
+            case SDP_ATTR_QOS:
+                return (attr_p->attr.qos.strength);
+            case SDP_ATTR_DES:
+                return (attr_p->attr.des.strength);
+            default:
+                return SDP_QOS_STRENGTH_UNKNOWN;
+
+        }
+    }
+}
+
+/* Function:    sdp_attr_get_qos_direction
+ * Description: Returns the value of the qos attribute direction
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_QOS_DIR_UNKNOWN is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Qos direction value.
+ */
+sdp_qos_dir_e sdp_attr_get_qos_direction (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_QOS_DIR_UNKNOWN);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid QOS attribute specified "
+                     "for get qos direction.", sdp_p->debug_str);
+        }
+        return (SDP_QOS_DIR_UNKNOWN);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_QOS_DIR_UNKNOWN);
+    } else {
+         switch (qos_attr) {
+            case SDP_ATTR_QOS:
+                 return (attr_p->attr.qos.direction);
+            case SDP_ATTR_CURR:
+                 return (attr_p->attr.curr.direction);
+            case SDP_ATTR_DES:
+                 return (attr_p->attr.des.direction);
+            case SDP_ATTR_CONF:
+                 return (attr_p->attr.conf.direction);
+            default:
+                return SDP_QOS_DIR_UNKNOWN;
+
+        }
+    }
+}
+
+/* Function:    sdp_attr_get_qos_status_type
+ * Description: Returns the value of the qos attribute status_type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_QOS_STATUS_TYPE_UNKNOWN is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Qos direction value.
+ */
+sdp_qos_status_types_e sdp_attr_get_qos_status_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_QOS_STATUS_TYPE_UNKNOWN);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid QOS attribute specified "
+                     "for get qos status_type.", sdp_p->debug_str);
+        }
+        return (SDP_QOS_STATUS_TYPE_UNKNOWN);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_QOS_STATUS_TYPE_UNKNOWN);
+    } else {
+        switch (qos_attr) {
+            case SDP_ATTR_CURR:
+                return (attr_p->attr.curr.status_type);
+            case SDP_ATTR_DES:
+                return (attr_p->attr.des.status_type);
+            case SDP_ATTR_CONF:
+                return (attr_p->attr.conf.status_type);
+            default:
+                return SDP_QOS_STATUS_TYPE_UNKNOWN;
+
+        }
+    }
+}
+
+/* Function:    sdp_attr_get_qos_confirm
+ * Description: Returns the value of the qos attribute confirm
+ *              parameter specified for the given attribute.  Returns TRUE if
+ *              the confirm parameter is specified.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Boolean value.
+ */
+tinybool sdp_attr_get_qos_confirm (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid QOS attribute specified "
+                     "for get qos confirm.", sdp_p->debug_str);
+        }
+        return (FALSE);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.qos.confirm);
+    }
+}
+
+/* Function:    sdp_attr_set_qos_strength
+ * Description: Sets the qos strength value for the specified qos attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ *              strength    New qos strength parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_qos_strength (void *sdp_ptr, u16 level, u8 cap_num,
+                                        sdp_attr_e qos_attr, u16 inst_num,
+                                        sdp_qos_strength_e strength)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid QOS attribute specified "
+                     "for set qos strength.", sdp_p->debug_str);
+        }
+        return (SDP_FAILURE);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        switch (qos_attr) {
+            case SDP_ATTR_QOS:
+                attr_p->attr.qos.strength = strength;
+                return (SDP_SUCCESS);
+            case SDP_ATTR_DES:
+                attr_p->attr.des.strength = strength;
+                return (SDP_SUCCESS);
+            default:
+                return (SDP_FAILURE);
+
+        }
+    }
+}
+
+/* Function:    sdp_attr_get_curr_type
+ * Description: Returns the value of the curr attribute status_type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_CURR_UNKNOWN_TYPE is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Curr type value.
+ */
+sdp_curr_type_e sdp_attr_get_curr_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_CURR_UNKNOWN_TYPE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_CURR_UNKNOWN_TYPE);
+    } else {
+        return (attr_p->attr.curr.type);
+    }
+}
+
+/* Function:    sdp_attr_get_des_type
+ * Description: Returns the value of the des attribute status_type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_DES_UNKNOWN_TYPE is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     DES type value.
+ */
+sdp_des_type_e sdp_attr_get_des_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_DES_UNKNOWN_TYPE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_DES_UNKNOWN_TYPE);
+    } else {
+        return (attr_p->attr.des.type);
+    }
+}
+
+/* Function:    sdp_attr_get_conf_type
+ * Description: Returns the value of the des attribute status_type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_CONF_UNKNOWN_TYPE is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     CONF type value.
+ */
+sdp_conf_type_e sdp_attr_get_conf_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_CONF_UNKNOWN_TYPE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_CONF_UNKNOWN_TYPE);
+    } else {
+        return (attr_p->attr.conf.type);
+    }
+}
+
+/* Function:    sdp_attr_set_curr_type
+ * Description: Returns the value of the curr attribute status_type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_CURR_UNKNOWN_TYPE is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_curr_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                sdp_curr_type_e curr_type)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid curr attribute specified "
+                     "for set curr type.", sdp_p->debug_str);
+        }
+        return (SDP_FAILURE);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.curr.type = curr_type;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_des_type
+ * Description: Sets the value of the des attribute type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_DES_UNKNOWN_TYPE is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_des_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                sdp_des_type_e des_type)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid des attribute specified "
+                     "for set des type.", sdp_p->debug_str);
+        }
+        return (SDP_FAILURE);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.des.type = des_type;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_attr_set_conf_type
+ * Description: Sets the value of the conf attribute type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_CONF_UNKNOWN_TYPE is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_conf_type (void *sdp_ptr, u16 level,
+                                u8 cap_num, sdp_attr_e qos_attr, u16 inst_num,
+                                sdp_conf_type_e conf_type)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid conf attribute specified "
+                     "for set conf type.", sdp_p->debug_str);
+        }
+        return (SDP_FAILURE);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.conf.type = conf_type;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_qos_direction
+ * Description: Sets the qos direction value for the specified qos attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ *              direction   New qos direction parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_qos_direction (void *sdp_ptr, u16 level, u8 cap_num,
+                                         sdp_attr_e qos_attr, u16 inst_num,
+                                         sdp_qos_dir_e direction)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid QOS attribute specified "
+                     "for set qos direction.", sdp_p->debug_str);
+        }
+        return (SDP_FAILURE);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        switch (qos_attr) {
+            case SDP_ATTR_QOS:
+                attr_p->attr.qos.direction = direction;
+                return (SDP_SUCCESS);
+            case SDP_ATTR_CURR:
+                attr_p->attr.curr.direction = direction;
+                return (SDP_SUCCESS);
+            case SDP_ATTR_DES:
+                 attr_p->attr.des.direction = direction;
+                return (SDP_SUCCESS);
+            case SDP_ATTR_CONF:
+                 attr_p->attr.conf.direction = direction;
+                return (SDP_SUCCESS);
+            default:
+                return (SDP_FAILURE);
+
+        }
+    }
+}
+
+/* Function:    sdp_attr_set_qos_status_type
+ * Description: Sets the qos status_type value for the specified qos attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ *              direction   New qos direction parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_qos_status_type (void *sdp_ptr, u16 level, u8 cap_num,
+                                         sdp_attr_e qos_attr, u16 inst_num,
+                                         sdp_qos_status_types_e status_type)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid QOS attribute specified "
+                     "for set qos status_type.", sdp_p->debug_str);
+        }
+        return (SDP_FAILURE);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        switch (qos_attr) {
+            case SDP_ATTR_CURR:
+                attr_p->attr.curr.status_type = status_type;
+                return (SDP_SUCCESS);
+            case SDP_ATTR_DES:
+                attr_p->attr.des.status_type = status_type;
+                return (SDP_SUCCESS);
+            case SDP_ATTR_CONF:
+                attr_p->attr.conf.status_type = status_type;
+                return (SDP_SUCCESS);
+            default:
+                return (SDP_FAILURE);
+
+        }
+    }
+}
+
+/* Function:    sdp_attr_set_qos_confirm
+ * Description: Sets the qos confirm value for the specified qos attribute.
+ *              If this parameter is TRUE, the confirm parameter will be
+ *              specified when the SDP description is built.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ *              confirm     New qos confirm parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_qos_confirm (void *sdp_ptr, u16 level, u8 cap_num,
+                                       sdp_attr_e qos_attr, u16 inst_num,
+                                       tinybool qos_confirm)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (sdp_validate_qos_attr(qos_attr) == FALSE) {
+        if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
+            CSFLogDebug(logTag, "%s Warning: Invalid QOS attribute specified "
+                     "for set qos confirm.", sdp_p->debug_str);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, qos_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(qos_attr), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.qos.confirm = qos_confirm;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_attr_get_subnet_nettype
+ * Description: Returns the value of the subnet attribute network type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_NT_INVALID is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Nettype value.
+ */
+sdp_nettype_e sdp_attr_get_subnet_nettype (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_NT_INVALID);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SUBNET, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Subnet attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_NT_INVALID);
+    } else {
+        return (attr_p->attr.subnet.nettype);
+    }
+}
+
+/* Function:    sdp_attr_get_subnet_addrtype
+ * Description: Returns the value of the subnet attribute address type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_AT_INVALID is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Addrtype value.
+ */
+sdp_addrtype_e sdp_attr_get_subnet_addrtype (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_AT_INVALID);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SUBNET, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Subnet attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_AT_INVALID);
+    } else {
+        return (attr_p->attr.subnet.addrtype);
+    }
+}
+
+/* Function:    sdp_attr_get_subnet_addr
+ * Description: Returns the value of the subnet attribute address
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, NULL is returned.  Value is
+ *              returned as a const ptr and so cannot be modified by the
+ *              application.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Pointer to address or NULL.
+ */
+const char *sdp_attr_get_subnet_addr (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SUBNET, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Subnet attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    } else {
+        return (attr_p->attr.subnet.addr);
+    }
+}
+
+/* Function:    sdp_attr_get_subnet_prefix
+ * Description: Returns the value of the subnet attribute prefix
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_INVALID_PARAM is returned.
+ *              Note that this is value is defined to be (-2) and is
+ *              different from the return code SDP_INVALID_PARAMETER.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Prefix value or SDP_INVALID_PARAM.
+ */
+int32 sdp_attr_get_subnet_prefix (void *sdp_ptr, u16 level,
+                                  u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SUBNET, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Subnet attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.subnet.prefix);
+    }
+}
+
+/* Function:    sdp_attr_set_subnet_nettype
+ * Description: Sets the value of the subnet attribute nettype parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              nettype     The network type for the attribute.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_subnet_nettype (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num,
+                                          sdp_nettype_e nettype)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SUBNET, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Subnet attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.subnet.nettype = nettype;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_subnet_addrtype
+ * Description: Sets the value of the subnet attribute addrtype parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              addrtype    The address type for the attribute.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_subnet_addrtype (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num,
+                                           sdp_addrtype_e sdp_addrtype)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SUBNET, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Subnet attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.subnet.addrtype = sdp_addrtype;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_subnet_addr
+ * Description: Sets the value of the subnet attribute addr parameter
+ *              for the given attribute. The address is copied into the
+ *              SDP structure so application memory will not be
+ *              referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              addr        Ptr to the address string for the attribute.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_subnet_addr (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       const char *addr)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SUBNET, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Subnet attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        sstrncpy(attr_p->attr.subnet.addr, addr,
+                 sizeof(attr_p->attr.subnet.addr)) ;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_subnet_prefix
+ * Description: Sets the value of the subnet attribute prefix parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              prefix      Prefix value for the attribute.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_subnet_prefix (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num,
+                                         int32 prefix)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SUBNET, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Subnet attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.subnet.prefix = prefix;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_attr_rtpmap_payload_valid
+ * Description: Returns true or false depending on whether an rtpmap
+ *              attribute was specified with the given payload value
+ *              at the given level.  If it was, the instance number of
+ *              that attribute is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number of the attribute
+ *                          found is returned via this param.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_attr_rtpmap_payload_valid (void *sdp_ptr, u16 level, u8 cap_num,
+                                        u16 *inst_num, u16 payload_type)
+{
+    u16          i;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    u16          num_instances;
+
+    *inst_num = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (sdp_attr_num_instances(sdp_ptr, level, cap_num,
+                          SDP_ATTR_RTPMAP, &num_instances) != SDP_SUCCESS) {
+        return (FALSE);
+    }
+
+    for (i=1; i <= num_instances; i++) {
+        attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTPMAP, i);
+        if ((attr_p != NULL) &&
+            (attr_p->attr.transport_map.payload_num == payload_type)) {
+            *inst_num = i;
+            return (TRUE);
+        }
+    }
+
+    return (FALSE);
+}
+
+/* Function:    sdp_attr_get_rtpmap_payload_type
+ * Description: Returns the value of the rtpmap attribute payload type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Payload type value.
+ */
+u16 sdp_attr_get_rtpmap_payload_type (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTPMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s rtpmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.transport_map.payload_num);
+    }
+}
+
+/* Function:    sdp_attr_get_rtpmap_encname
+ * Description: Returns a pointer to the value of the encoding name
+ *              parameter specified for the given attribute.  Value is
+ *              returned as a const ptr and so cannot be modified by the
+ *              application.  If the given attribute is not defined, NULL
+ *              will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Codec value or SDP_CODEC_INVALID.
+ */
+const char *sdp_attr_get_rtpmap_encname (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTPMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s rtpmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    } else {
+        return (attr_p->attr.transport_map.encname);
+    }
+}
+
+/* Function:    sdp_attr_get_rtpmap_clockrate
+ * Description: Returns the value of the rtpmap attribute clockrate
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Clockrate value.
+ */
+u32 sdp_attr_get_rtpmap_clockrate (void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTPMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s rtpmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.transport_map.clockrate);
+    }
+}
+
+/* Function:    sdp_attr_get_rtpmap_num_chan
+ * Description: Returns the value of the rtpmap attribute num_chan
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Number of channels param or zero.
+ */
+u16 sdp_attr_get_rtpmap_num_chan (void *sdp_ptr, u16 level,
+                                  u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTPMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s rtpmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.transport_map.num_chan);
+    }
+}
+
+/* Function:    sdp_attr_set_rtpmap_payload_type
+ * Description: Sets the value of the rtpmap attribute payload type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              payload_num New payload type value.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_rtpmap_payload_type (void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                               u16 payload_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTPMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s rtpmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.transport_map.payload_num = payload_num;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_rtpmap_encname
+ * Description: Sets the value of the rtpmap attribute encname parameter
+ *              for the given attribute.  String is copied into SDP memory.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              encname     Ptr to string encoding name.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_rtpmap_encname (void *sdp_ptr, u16 level, u8 cap_num,
+                                          u16 inst_num, const char *encname)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTPMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s rtpmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (encname) {
+            sstrncpy(attr_p->attr.transport_map.encname, encname,
+                     sizeof(attr_p->attr.transport_map.encname));
+        }
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_ice_attribute
+ * Description: Returns the value of an ice attribute at a given level
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              ice_attrib  Returns an ice attrib string
+ * Returns:
+ *              SDP_SUCCESS           Attribute param was set successfully.
+ *              SDP_INVALID_SDP_PTR   SDP pointer invalid
+ *              SDP_INVALID_PARAMETER Specified attribute is not defined.
+ */
+
+sdp_result_e sdp_attr_get_ice_attribute (void *sdp_ptr, u16 level,
+                                  u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num,
+                                  char **out)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, sdp_attr, inst_num);
+    if (attr_p != NULL) {
+        *out = attr_p->attr.ice_attr;
+        return (SDP_SUCCESS);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s ice attribute, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    return (SDP_FAILURE);
+}
+
+/* Function:    sdp_attr_set_ice_attribute
+ * Description: Sets the value of an ice attribute parameter
+ *              String is copied into SDP memory.
+ * Parameters:  sdp_ptr        The SDP handle returned by sdp_init_description.
+ *              level          The level to set the attribute.
+ *              cap_num        The capability number associated with the
+ *                             attribute if any.  If none, should be zero.
+ *              inst_num       The attribute instance number to check.
+ *              ice_attrib     ice attribute string to set
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_ice_attribute(void *sdp_ptr, u16 level,
+                              u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num, const char *ice_attrib)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, sdp_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s ice attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (!ice_attrib) {
+      return (SDP_INVALID_PARAMETER);
+    }
+
+    sstrncpy(attr_p->attr.ice_attr, ice_attrib, sizeof(attr_p->attr.ice_attr));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_rtcp_mux_attribute
+ * Description: Returns the value of an rtcp-mux attribute at a given level
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              rtcp_mux    Returns an rtcp-mux attrib bool
+ * Returns:
+ *              SDP_SUCCESS           Attribute param was set successfully.
+ *              SDP_INVALID_SDP_PTR   SDP pointer invalid
+ *              SDP_INVALID_PARAMETER Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_get_rtcp_mux_attribute (void *sdp_ptr, u16 level,
+                                  u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num,
+                                  tinybool *rtcp_mux)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, sdp_attr, inst_num);
+    if (attr_p != NULL) {
+       *rtcp_mux = attr_p->attr.boolean_val;
+        return (SDP_SUCCESS);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s rtcp-mux attribute, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    return (SDP_FAILURE);
+}
+
+/* Function:    sdp_attr_set_rtcp_mux_attribute
+ * Description: Sets the value of an rtcp_mux attribute parameter
+ *              String is copied into SDP memory.
+ * Parameters:  sdp_ptr        The SDP handle returned by sdp_init_description.
+ *              level          The level to set the attribute.
+ *              cap_num        The capability number associated with the
+ *                             attribute if any.  If none, should be zero.
+ *              inst_num       The attribute instance number to check.
+ *              rtcp_mux       rtcp-mux attribute bool to set
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_rtcp_mux_attribute(void *sdp_ptr, u16 level,
+                              u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num, const tinybool rtcp_mux)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, sdp_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s rtcp-mux attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p->attr.boolean_val = rtcp_mux;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_dtls_fingerprint_attribute
+ * Description: Returns the value of dtls fingerprint attribute at a given level
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              dtls_fingerprint  Returns an dtls fingerprint attrib string
+ * Returns:
+ *              SDP_SUCCESS           Attribute param was set successfully.
+ *              SDP_INVALID_SDP_PTR   SDP pointer invalid
+ *              SDP_INVALID_PARAMETER Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_get_dtls_fingerprint_attribute (void *sdp_ptr, u16 level,
+                                  u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num,
+                                  char **out)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, sdp_attr, inst_num);
+    if (attr_p != NULL) {
+        *out = attr_p->attr.string_val;
+        return (SDP_SUCCESS);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s dtls fingerprint attribute, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    return (SDP_FAILURE);
+}
+
+/* Function:    sdp_attr_set_dtls_fingerprint_attribute
+ * Description: Sets the value of a dtls fingerprint attribute parameter
+ *              String is copied into SDP memory.
+ * Parameters:  sdp_ptr        The SDP handle returned by sdp_init_description.
+ *              level          The level to set the attribute.
+ *              cap_num        The capability number associated with the
+ *                             attribute if any.  If none, should be zero.
+ *              inst_num       The attribute instance number to check.
+ *              dtls_fingerprint fingerprint attribute string to set
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_dtls_fingerprint_attribute(void *sdp_ptr, u16 level,
+                              u8 cap_num, sdp_attr_e sdp_attr, u16 inst_num, const char *dtls_fingerprint)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, sdp_attr, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s dtls fingerprint attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (!dtls_fingerprint) {
+      return (SDP_INVALID_PARAMETER);
+    }
+
+    sstrncpy(attr_p->attr.string_val, dtls_fingerprint, sizeof(attr_p->attr.string_val));
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_rtpmap_clockrate
+ * Description: Sets the value of the rtpmap attribute clockrate parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              clockrate   New clockrate value.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_rtpmap_clockrate (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num,
+                                            u32 clockrate)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTPMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s rtpmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.transport_map.clockrate = clockrate;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_rtpmap_num_chan
+ * Description: Sets the value of the rtpmap attribute num_chan parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              num_chan    New number of channels value.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_rtpmap_num_chan (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num,
+                                           u16 num_chan)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTPMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s rtpmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.transport_map.num_chan = num_chan;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_attr_sprtmap_payload_valid
+ * Description: Returns true or false depending on whether an sprtmap
+ *              attribute was specified with the given payload value
+ *              at the given level.  If it was, the instance number of
+ *              that attribute is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number of the attribute
+ *                          found is returned via this param.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_attr_sprtmap_payload_valid (void *sdp_ptr, u16 level, u8 cap_num,
+                                        u16 *inst_num, u16 payload_type)
+{
+    u16          i;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    u16          num_instances;
+
+    *inst_num = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (sdp_attr_num_instances(sdp_ptr, level, cap_num,
+                          SDP_ATTR_SPRTMAP, &num_instances) != SDP_SUCCESS) {
+        return (FALSE);
+    }
+
+    for (i=1; i <= num_instances; i++) {
+        attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_SPRTMAP, i);
+        if ((attr_p != NULL) &&
+            (attr_p->attr.transport_map.payload_num == payload_type)) {
+            *inst_num = i;
+            return (TRUE);
+        }
+    }
+
+    return (FALSE);
+}
+
+/* Function:    sdp_attr_get_sprtmap_payload_type
+ * Description: Returns the value of the sprtmap attribute payload type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Payload type value.
+ */
+u16 sdp_attr_get_sprtmap_payload_type (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_SPRTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s sprtmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.transport_map.payload_num);
+    }
+}
+
+/* Function:    sdp_attr_get_sprtmap_encname
+ * Description: Returns a pointer to the value of the encoding name
+ *              parameter specified for the given attribute.  Value is
+ *              returned as a const ptr and so cannot be modified by the
+ *              application.  If the given attribute is not defined, NULL
+ *              will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Codec value or SDP_CODEC_INVALID.
+ */
+const char *sdp_attr_get_sprtmap_encname (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_SPRTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s sprtmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    } else {
+        return (attr_p->attr.transport_map.encname);
+    }
+}
+
+/* Function:    sdp_attr_get_sprtmap_clockrate
+ * Description: Returns the value of the sprtmap attribute clockrate
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Clockrate value.
+ */
+u32 sdp_attr_get_sprtmap_clockrate (void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_SPRTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s sprtmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.transport_map.clockrate);
+    }
+}
+
+/* Function:    sdp_attr_get_sprtmap_num_chan
+ * Description: Returns the value of the sprtmap attribute num_chan
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Number of channels param or zero.
+ */
+u16 sdp_attr_get_sprtmap_num_chan (void *sdp_ptr, u16 level,
+                                  u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_SPRTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s sprtmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.transport_map.num_chan);
+    }
+}
+
+/* Function:    sdp_attr_set_sprtmap_payload_type
+ * Description: Sets the value of the sprtmap attribute payload type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              payload_num New payload type value.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_sprtmap_payload_type (void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                               u16 payload_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_SPRTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s sprtmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.transport_map.payload_num = payload_num;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_sprtmap_encname
+ * Description: Sets the value of the sprtmap attribute encname parameter
+ *              for the given attribute.  String is copied into SDP memory.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              encname     Ptr to string encoding name.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_sprtmap_encname (void *sdp_ptr, u16 level, u8 cap_num,
+                                          u16 inst_num, const char *encname)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_SPRTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s sprtmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        sstrncpy(attr_p->attr.transport_map.encname, encname,
+                 sizeof(attr_p->attr.transport_map.encname));
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_sprtmap_clockrate
+ * Description: Sets the value of the sprtmap attribute clockrate parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              clockrate   New clockrate value.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_sprtmap_clockrate (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num,
+                                            u16 clockrate)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_SPRTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s sprtmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.transport_map.clockrate = clockrate;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_sprtmap_num_chan
+ * Description: Sets the value of the sprtmap attribute num_chan parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              num_chan    New number of channels value.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_sprtmap_num_chan (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num,
+                                           u16 num_chan)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_SPRTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s sprtmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.transport_map.num_chan = num_chan;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Note:  The fmtp attribute formats currently handled are:
+ *        fmtp:<payload type> <event>,<event>...
+ *        fmtp:<payload_type> [annexa=yes/no] [annexb=yes/no] [bitrate=<value>]
+ *        where "value" is a numeric value > 0
+ *        where each event is a single number or a range separated
+ *        by a '-'.
+ *        Example:  fmtp:101 1,3-15,20
+ */
+
+/* Function:    tinybool sdp_attr_fmtp_valid(void *sdp_ptr)
+ * Description: Returns true or false depending on whether an fmtp
+ *              attribute was specified with the given payload value
+ *              at the given level.  If it was, the instance number of
+ *              that attribute is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_attr_fmtp_payload_valid (void *sdp_ptr, u16 level, u8 cap_num,
+                                      u16 *inst_num, u16 payload_type)
+{
+    u16          i;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    u16          num_instances;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    if (sdp_attr_num_instances(sdp_ptr, level, cap_num,
+                               SDP_ATTR_FMTP, &num_instances) != SDP_SUCCESS) {
+        return (FALSE);
+    }
+
+    for (i=1; i <= num_instances; i++) {
+        attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, i);
+        if ((attr_p != NULL) &&
+            (attr_p->attr.fmtp.payload_num == payload_type)) {
+            *inst_num = i;
+            return (TRUE);
+        }
+    }
+
+    return (FALSE);
+}
+
+/* Function:    sdp_attr_get_fmtp_payload_type
+ * Description: Returns the value of the fmtp attribute payload type
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Payload type value.
+ */
+u16 sdp_attr_get_fmtp_payload_type (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.fmtp.payload_num);
+    }
+}
+
+
+/* Function:    sdp_attr_fmtp_is_range_set
+ * Description: Determines if a range of events is set in an fmtp attribute.
+ *              The overall range for events is 0-255.
+ *              This will return either FULL_MATCH, PARTIAL_MATCH, or NO_MATCH
+ *              depending on whether all, some, or none of the specified
+ *              events are defined. If the given attribute is not defined,
+ *              NO_MATCH will be returned.  It is up to the appl to verify
+ *              the validity of the attribute before calling this routine.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              low_val     Low value of the range. Range is 0-255.
+ *              high_val    High value of the range.
+ * Returns:     SDP_FULL_MATCH, SDP_PARTIAL_MATCH, SDP_NO_MATCH
+ */
+sdp_ne_res_e sdp_attr_fmtp_is_range_set (void *sdp_ptr, u16 level, u8 cap_num,
+                                         u16 inst_num, u8 low_val, u8 high_val)
+{
+    u16          i;
+    u32          mapword;
+    u32          bmap;
+    u32          num_vals = 0;
+    u32          num_vals_set = 0;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_NO_MATCH);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_NO_MATCH);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    for (i = low_val; i <= high_val; i++) {
+        num_vals++;
+        mapword = i/SDP_NE_BITS_PER_WORD;
+        bmap = SDP_NE_BIT_0 << (i%32);
+        if (fmtp_p->bmap[ mapword ] & bmap) {
+            num_vals_set++;
+        }
+    }
+
+    if (num_vals == num_vals_set) {
+        return (SDP_FULL_MATCH);
+    } else if (num_vals_set == 0) {
+        return (SDP_NO_MATCH);
+    } else {
+        return (SDP_PARTIAL_MATCH);
+    }
+}
+
+/* Function:    sdp_attr_fmtp_valid
+ * Description: Determines the validity of the events in the fmtp.
+ *              The overall range for events is 0-255.
+ *              The user passes an event list with valid events supported by Appl.
+ *              This routine will do a simple AND comparison and report the result.
+ *
+ *              This will return TRUE if ftmp events are valid, and FALSE otherwise.
+ *              It is up to the appl to verify the validity of the attribute
+ *              before calling this routine.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              appl_maxval Max event value supported by Appl. Range is 0-255.
+ *              evt_array   Bitmap containing events supported by application.
+ * Returns:     TRUE, FALSE
+ */
+tinybool
+sdp_attr_fmtp_valid(void *sdp_ptr, u16 level, u8 cap_num,
+                    u16 inst_num, u16 appl_maxval, u32* evt_array)
+{
+    u16          i;
+    u32          mapword;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return FALSE;
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return FALSE;
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+
+    /* Do quick test. If application max value is lower than fmtp's then error */
+    if (fmtp_p->maxval > appl_maxval)
+      return FALSE;
+
+    /* Ok, events are within range. Now check that only
+     * allowed events have been received
+     */
+    mapword = appl_maxval/SDP_NE_BITS_PER_WORD;
+    for (i=0; i<mapword; i++) {
+      if (fmtp_p->bmap[i] & ~(evt_array[i])) {
+       /* Remote SDP is requesting events not supported by Application */
+       return FALSE;
+      }
+    }
+    return TRUE;
+}
+
+/* Function:    sdp_attr_set_fmtp_payload_type
+ * Description: Sets the value of the fmtp attribute payload type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              payload_type New payload type value.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_payload_type (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             u16 payload_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.fmtp.payload_num = payload_num;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_fmtp_bitmap
+ * Description: Set a range of named events for an fmtp attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              bmap        The 8 word data array holding the bitmap
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_attr_set_fmtp_bitmap(void *sdp_ptr, u16 level,
+                           u8 cap_num, u16 inst_num, u32 *bmap, u32 maxval)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->maxval = maxval;
+    memcpy(fmtp_p->bmap, bmap, SDP_NE_NUM_BMAP_WORDS * sizeof(u32) );
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_fmtp_range
+ * Description: Get a range of named events for an fmtp attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              bmap        The 8 word data array holding the bitmap
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_attr_get_fmtp_range (void *sdp_ptr, u16 level, u8 cap_num,
+                                      u16 inst_num, u32 *bmap)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    memcpy(bmap, fmtp_p->bmap, SDP_NE_NUM_BMAP_WORDS * sizeof(u32) );
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_range
+ * Description: Set a range of named events for an fmtp attribute. The low
+ *              value specified must be <= the high value.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              low_val     The low value of the range. Range is 0-255.
+ *              high_val    The high value of the range.  May be == low.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_attr_set_fmtp_range (void *sdp_ptr, u16 level, u8 cap_num,
+                                      u16 inst_num, u8 low_val, u8 high_val)
+{
+    u16          i;
+    u32          mapword;
+    u32          bmap;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    for (i = low_val; i <= high_val; i++) {
+        mapword = i/SDP_NE_BITS_PER_WORD;
+        bmap = SDP_NE_BIT_0 << (i%32);
+        fmtp_p->bmap[ mapword ] |= bmap;
+    }
+    if (high_val > fmtp_p->maxval) {
+        fmtp_p->maxval = high_val;
+    }
+    fmtp_p->fmtp_format = SDP_FMTP_NTE;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_clear_fmtp_range
+ * Description: Clear a range of named events for an fmtp attribute. The low
+ *              value specified must be <= the high value.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              low_val     The low value of the range. Range is 0-255
+ *              high_val    The high value of the range.  May be == low.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_attr_clear_fmtp_range (void *sdp_ptr, u16 level, u8 cap_num,
+                                        u16 inst_num, u8 low_val, u8 high_val)
+{
+    u16          i;
+    u32          mapword;
+    u32          bmap;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    for (i = low_val; i <= high_val; i++) {
+        mapword = i/SDP_NE_BITS_PER_WORD;
+        bmap = SDP_NE_BIT_0 << (i%32);
+        fmtp_p->bmap[ mapword ] &= ~bmap;
+    }
+    if (high_val > fmtp_p->maxval) {
+        fmtp_p->maxval = high_val;
+    }
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_compare_fmtp_ranges
+ * Description: Compare the named events set of two fmtp attributes. If all
+ *              events are the same (either set or not), FULL_MATCH will be
+ *              returned.  If no events match, NO_MATCH will be returned.
+ *              Otherwise PARTIAL_MATCH will be returned.  If either attr is
+ *              invalid, NO_MATCH will be returned.
+ * Parameters:  src_sdp_ptr The SDP handle returned by sdp_init_description.
+ *              dst_sdp_ptr The SDP handle returned by sdp_init_description.
+ *              src_level   The level of the src fmtp attribute.
+ *              dst_level   The level to the dst fmtp attribute.
+ *              src_cap_num The capability number of the src attr.
+ *              dst_cap_num The capability number of the dst attr.
+ *              src_inst_numh The attribute instance of the src attr.
+ *              dst_inst_numh The attribute instance of the dst attr.
+ * Returns:     SDP_FULL_MATCH, SDP_PARTIAL_MATCH, SDP_NO_MATCH.
+ */
+sdp_ne_res_e sdp_attr_compare_fmtp_ranges (void *src_sdp_ptr,void *dst_sdp_ptr,
+                                           u16 src_level, u16 dst_level,
+                                           u8 src_cap_num, u8 dst_cap_num,
+                                           u16 src_inst_num, u16 dst_inst_num)
+{
+    u16          i,j;
+    u32          bmap;
+    u32          num_vals_match = 0;
+    sdp_t       *src_sdp_p = (sdp_t *)src_sdp_ptr;
+    sdp_t       *dst_sdp_p = (sdp_t *)dst_sdp_ptr;
+    sdp_attr_t  *src_attr_p;
+    sdp_attr_t  *dst_attr_p;
+    sdp_fmtp_t  *src_fmtp_p;
+    sdp_fmtp_t  *dst_fmtp_p;
+
+    if ((sdp_verify_sdp_ptr(src_sdp_p) == FALSE) ||
+        (sdp_verify_sdp_ptr(dst_sdp_p) == FALSE)) {
+        return (SDP_NO_MATCH);
+    }
+
+    src_attr_p = sdp_find_attr(src_sdp_p, src_level, src_cap_num,
+                               SDP_ATTR_FMTP, src_inst_num);
+    dst_attr_p = sdp_find_attr(dst_sdp_p, dst_level, dst_cap_num,
+                               SDP_ATTR_FMTP, dst_inst_num);
+    if ((src_attr_p == NULL) || (dst_attr_p == NULL)) {
+        if (src_sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s source or destination fmtp attribute for "
+                      "compare not found.", src_sdp_p->debug_str);
+        }
+        src_sdp_p->conf_p->num_invalid_param++;
+        return (SDP_NO_MATCH);
+    }
+
+    src_fmtp_p = &(src_attr_p->attr.fmtp);
+    dst_fmtp_p = &(dst_attr_p->attr.fmtp);
+    for (i = 0; i < SDP_NE_NUM_BMAP_WORDS; i++) {
+        for (j = 0; j < SDP_NE_BITS_PER_WORD; j++) {
+            bmap = SDP_NE_BIT_0 << j;
+            if ((src_fmtp_p->bmap[i] & bmap) && (dst_fmtp_p->bmap[i] & bmap)) {
+                num_vals_match++;
+            } else if ((!(src_fmtp_p->bmap[i] & bmap)) &&
+                       (!(dst_fmtp_p->bmap[i] & bmap))) {
+                num_vals_match++;
+            }
+        }
+    }
+
+    if (num_vals_match == (SDP_NE_NUM_BMAP_WORDS * SDP_NE_BITS_PER_WORD)) {
+        return (SDP_FULL_MATCH);
+    } else if (num_vals_match == 0) {
+        return (SDP_NO_MATCH);
+    } else {
+        return (SDP_PARTIAL_MATCH);
+    }
+}
+
+/* Function:    sdp_attr_copy_fmtp_ranges
+ * Description: Copy the named events set for one fmtp attribute to another.
+ * Parameters:  src_sdp_ptr The SDP handle returned by sdp_init_description.
+ *              dst_sdp_ptr The SDP handle returned by sdp_init_description.
+ *              src_level   The level of the src fmtp attribute.
+ *              dst_level   The level to the dst fmtp attribute.
+ *              src_cap_num The capability number of the src attr.
+ *              dst_cap_num The capability number of the dst attr.
+ *              src_inst_numh The attribute instance of the src attr.
+ *              dst_inst_numh The attribute instance of the dst attr.
+ * Returns:     SDP_SUCCESS
+ */
+sdp_result_e sdp_attr_copy_fmtp_ranges (void *src_sdp_ptr, void *dst_sdp_ptr,
+                                        u16 src_level, u16 dst_level,
+                                        u8 src_cap_num, u8 dst_cap_num,
+                                        u16 src_inst_num, u16 dst_inst_num)
+{
+    u16          i;
+    sdp_t       *src_sdp_p = (sdp_t *)src_sdp_ptr;
+    sdp_t       *dst_sdp_p = (sdp_t *)dst_sdp_ptr;
+    sdp_attr_t  *src_attr_p;
+    sdp_attr_t  *dst_attr_p;
+    sdp_fmtp_t  *src_fmtp_p;
+    sdp_fmtp_t  *dst_fmtp_p;
+
+    if ((sdp_verify_sdp_ptr(src_sdp_p) == FALSE) ||
+        (sdp_verify_sdp_ptr(dst_sdp_p) == FALSE)) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    src_attr_p = sdp_find_attr(src_sdp_p, src_level, src_cap_num,
+                               SDP_ATTR_FMTP, src_inst_num);
+    dst_attr_p = sdp_find_attr(dst_sdp_p, dst_level, dst_cap_num,
+                               SDP_ATTR_FMTP, dst_inst_num);
+    if ((src_attr_p == NULL) || (dst_attr_p == NULL)) {
+        if (src_sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s source or destination fmtp attribute for "
+                      "copy not found.", src_sdp_p->debug_str);
+        }
+        src_sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    src_fmtp_p = &(src_attr_p->attr.fmtp);
+    dst_fmtp_p = &(dst_attr_p->attr.fmtp);
+    dst_fmtp_p->maxval = src_fmtp_p->maxval;
+    for (i = 0; i < SDP_NE_NUM_BMAP_WORDS; i++) {
+        dst_fmtp_p->bmap[i] = src_fmtp_p->bmap[i];
+    }
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_annexa
+ * Description: Sets the value of the fmtp attribute annexa type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              annexa       It is either yes or no.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_annexa (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       tinybool annexa)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->annexa_required = TRUE;
+    fmtp_p->annexa = annexa;
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_annexb
+ * Description: Sets the value of the fmtp attribute annexb type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              annexb       It is either yes or no.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_annexb  (void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num,
+                                        tinybool annexb)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+   if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+   }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->annexb_required = TRUE;
+    fmtp_p->annexb = annexb;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_fmtp_mode
+ * Description: Gets the value of the fmtp attribute mode parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              payload_type payload type.
+ * Returns:     mode value
+ */
+u32 sdp_attr_get_fmtp_mode_for_payload_type (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u32 payload_type)
+{
+    u16          num_a_lines = 0;
+    int          i;
+    sdp_t       *sdp_p = sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+    /*
+     * Get number of FMTP attributes for the AUDIO line
+     */
+    (void) sdp_attr_num_instances(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                                  &num_a_lines);
+    for (i = 0; i < num_a_lines; i++) {
+        attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, (uint16_t) (i + 1));
+        if ((attr_p != NULL) &&
+            (attr_p->attr.fmtp.payload_num == (u16)payload_type)) {
+            if (attr_p->attr.fmtp.fmtp_format == SDP_FMTP_MODE) {
+                return attr_p->attr.fmtp.mode;
+            }
+        }
+    }
+   return 0;
+}
+
+/* Function:    sdp_attr_set_fmtp_mode
+ * Description: Sets the value of the fmtp attribute mode type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              mode        in milli seconds
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_mode  (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num,
+                                      u32 mode)
+{
+    sdp_t       *sdp_p = sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_MODE;
+    fmtp_p->mode = mode;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_bitrate_type
+ * Description: Sets the value of the fmtp attribute bitrate type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              bitrate     Sets the bitrate value.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_bitrate_type  (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             u32 bitrate)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (bitrate <= 0) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->bitrate = bitrate;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_cif
+ * Description: Sets the value of the fmtp attribute cif parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              cif         Sets the CIF value for a video codec
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_cif  (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num,
+                                     u16 cif)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if ((cif < SDP_MIN_CIF_VALUE) || ( cif > SDP_MAX_CIF_VALUE)) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->cif = cif;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_qcif
+ * Description: Sets the value of the fmtp attribute qcif parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              qcif        Sets the QCIF value for a video codec
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_qcif  (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num,
+                                     u16 qcif)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if ((qcif < SDP_MIN_CIF_VALUE) || ( qcif > SDP_MAX_CIF_VALUE)) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->qcif = qcif;
+    return (SDP_SUCCESS);
+}
+/* Function:    sdp_attr_set_fmtp_sqcif
+ * Description: Sets the value of the fmtp attribute sqcif parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              sqcif        Sets the SQCIF value for a video codec
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_sqcif  (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       u16 sqcif)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if ((sqcif < SDP_MIN_CIF_VALUE) || (sqcif > SDP_MAX_CIF_VALUE)) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->sqcif = sqcif;
+    return (SDP_SUCCESS);
+}
+
+
+/* Function:    sdp_attr_set_fmtp_cif4
+ * Description: Sets the value of the fmtp attribute cif4 parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              sqcif        Sets the cif4 value for a video codec
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_cif4  (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       u16 cif4)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if ((cif4 < SDP_MIN_CIF_VALUE) || (cif4 > SDP_MAX_CIF_VALUE)) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->cif4 = cif4;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_cif16
+ * Description: Sets the value of the fmtp attribute cif16 parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              cif16        Sets the cif16 value for a video codec
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_fmtp_cif16  (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       u16 cif16)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if ((cif16 < SDP_MIN_CIF_VALUE) || (cif16 > SDP_MAX_CIF_VALUE)) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->cif16 = cif16;
+    return (SDP_SUCCESS);
+}
+
+
+/* Function:    sdp_attr_set_fmtp_maxbr
+ * Description: Sets the value of the fmtp attribute maxbr parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              MAXBR        Sets the MAXBR value for a video codec
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+*/
+
+sdp_result_e sdp_attr_set_fmtp_maxbr  (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       u16 maxbr)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (maxbr <= 0) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->maxbr = maxbr;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_custom
+ * Description: Sets the value of the fmtp attribute custom parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              CUSTOM       Sets the CUSTOM value for a video codec
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+*/
+
+sdp_result_e sdp_attr_set_fmtp_custom  (void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num,
+                                        u16 custom_x, u16 custom_y, u16 custom_mpi)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if ((custom_x <= 0) || (custom_y <= 0) || (custom_mpi <= 0)) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->custom_x = custom_x;
+    fmtp_p->custom_y = custom_y;
+    fmtp_p->custom_mpi = custom_mpi;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_par
+ * Description: Sets the value of the fmtp attribute PAR parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              PAR         Sets the PAR width/height value
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+*/
+
+sdp_result_e sdp_attr_set_fmtp_par  (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num,
+                                     u16 par_width, u16 par_height)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if ((par_width <= 0) || (par_height <= 0)) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->par_width  = par_width;
+    fmtp_p->par_height = par_height;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_cpcf
+ * Description: Sets the value of the fmtp attribute CPCF parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              CPCF        Sets the CPCF value
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+*/
+
+sdp_result_e sdp_attr_set_fmtp_cpcf (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num,
+                                     u16 cpcf)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (cpcf <= 0) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->cpcf  = cpcf;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_bpp
+ * Description: Sets the value of the fmtp attribute BPP parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              BPP        Sets the BPP value
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+*/
+
+sdp_result_e sdp_attr_set_fmtp_bpp (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num,
+                                    u16 bpp)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (bpp <= 0) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->bpp  = bpp;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_fmtp_hrd
+ * Description: Sets the value of the fmtp attribute HRD parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              HRD        Sets the HRD value
+ * Returns:     SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e sdp_attr_set_fmtp_hrd (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num, u16 hrd)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (hrd <= 0) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->hrd  = hrd;
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_h263_num_params (void *sdp_ptr, int16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                int16 profile,
+                                                u16 h263_level,
+                                                tinybool interlace)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if ((profile >= SDP_MIN_PROFILE_LEVEL_VALUE) &&
+             (profile <= SDP_MAX_PROFILE_VALUE)) {
+        fmtp_p->profile  = profile;
+    }
+
+    if ((level >= SDP_MIN_PROFILE_LEVEL_VALUE) &&
+             (level <= SDP_MAX_LEVEL_VALUE)) {
+        fmtp_p->level  = h263_level;
+    }
+
+    if (interlace) {
+        fmtp_p->is_interlace  = TRUE;
+    } else {
+        fmtp_p->is_interlace  = FALSE;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_profile_level_id (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                const char *profile_level_id)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    if (profile_level_id) {
+        sstrncpy(fmtp_p->profile_level_id, profile_level_id,
+          SDP_MAX_STRING_LEN+1);
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_parameter_sets (void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                               const char *parameter_sets)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    if (parameter_sets) {
+        sstrncpy(fmtp_p->parameter_sets, parameter_sets, SDP_MAX_STRING_LEN+1);
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_pack_mode (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num,
+                                          u16 pack_mode)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (pack_mode > SDP_MAX_PACKETIZATION_MODE_VALUE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->packetization_mode  = pack_mode;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_level_asymmetry_allowed (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num,
+                                          u16 asym_allowed)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->level_asymmetry_allowed  = asym_allowed;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_deint_buf_req (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                u32 deint_buf_req)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->deint_buf_req = deint_buf_req;
+    fmtp_p->flag |= SDP_DEINT_BUF_REQ_FLAG;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_init_buf_time (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                u32 init_buf_time)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->init_buf_time = init_buf_time;
+    fmtp_p->flag |= SDP_INIT_BUF_TIME_FLAG;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_max_don_diff (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                u32 max_don_diff)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->max_don_diff  = max_don_diff;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_interleaving_depth (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                u16 interleaving_depth)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->interleaving_depth  = interleaving_depth;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_redundant_pic_cap (void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                              tinybool redundant_pic_cap)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (redundant_pic_cap > 1) {
+        return (SDP_FAILURE);
+    } else {
+        fmtp_p->redundant_pic_cap = redundant_pic_cap;
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_max_mbps (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num,
+                                         u32 max_mbps)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (max_mbps > 0) {
+        fmtp_p->max_mbps  = max_mbps;
+        return (SDP_SUCCESS);
+    } else {
+        return (SDP_FAILURE);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_max_fs (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       u32 max_fs)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (max_fs > 0) {
+        fmtp_p->max_fs  = max_fs;
+        return (SDP_SUCCESS);
+    } else {
+        return (SDP_FAILURE);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_max_br (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       u32 max_br)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (max_br > 0) {
+        fmtp_p->max_br  = max_br;
+        return (SDP_SUCCESS);
+    } else {
+        return (SDP_FAILURE);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_max_average_bitrate (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       u32 maxaveragebitrate)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (maxaveragebitrate > 0) {
+        fmtp_p->maxaveragebitrate = maxaveragebitrate;
+        return (SDP_SUCCESS);
+    } else {
+        return (SDP_FAILURE);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_max_average_bitrate
+ * Description: Gets the value of the fmtp attribute- maxaveragebitrate parameter for the OPUS codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     max-br value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_max_average_bitrate (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, u32* val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, 1);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = attr_p->attr.fmtp.maxaveragebitrate;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+sdp_result_e sdp_attr_set_fmtp_usedtx (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       tinybool usedtx)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (usedtx == TRUE) {
+        fmtp_p->usedtx = 1;
+    } else {
+        fmtp_p->usedtx = 0;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_fmtp_usedtx
+ * Description: Gets the value of the fmtp attribute- usedtx parameter for the OPUS codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     usedtx value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_usedtx (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, tinybool* val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = (tinybool)attr_p->attr.fmtp.usedtx;
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_stereo (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       tinybool stereo)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (stereo == TRUE) {
+        fmtp_p->stereo = 1;
+    } else {
+        fmtp_p->stereo = 0;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_fmtp_usedtx
+ * Description: Gets the value of the fmtp attribute- usedtx parameter for the OPUS codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     stereo value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_stereo (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, tinybool* val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = (tinybool)attr_p->attr.fmtp.stereo;
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_useinbandfec (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       tinybool useinbandfec)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (useinbandfec == TRUE) {
+        fmtp_p->useinbandfec = 1;
+    } else {
+        fmtp_p->useinbandfec = 0;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_fmtp_useinbandfec
+ * Description: Gets the value of the fmtp attribute useinbandfec parameter for the OPUS codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     useinbandfec value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_useinbandfec (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, tinybool* val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = (tinybool)attr_p->attr.fmtp.useinbandfec;
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_maxcodedaudiobandwidth (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                const char *maxcodedaudiobandwidth)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    if (maxcodedaudiobandwidth) {
+        sstrncpy(fmtp_p->maxcodedaudiobandwidth, maxcodedaudiobandwidth,
+          SDP_MAX_STRING_LEN+1);
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_fmtp_maxcodedaudiobandwidth
+ * Description: Gets the value of the fmtp attribute maxcodedaudiobandwidth parameter for OPUS codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     maxcodedaudiobandwidth value.
+ */
+char* sdp_attr_get_fmtp_maxcodedaudiobandwidth (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.fmtp.maxcodedaudiobandwidth);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_cbr (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       tinybool cbr)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (cbr == TRUE) {
+        fmtp_p->cbr = 1;
+    } else {
+        fmtp_p->cbr = 0;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_fmtp_cbr
+ * Description: Gets the value of the fmtp attribute cbr parameter for the OPUS codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     cbr value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_cbr (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, tinybool* val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = (tinybool)attr_p->attr.fmtp.cbr;
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_attr_get_fmtp_streams (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, u32* val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (!attr_p) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = attr_p->attr.fmtp.streams;
+        return (SDP_SUCCESS);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_streams (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       u32 streams)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (!attr_p) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_DATACHANNEL;
+
+    if (streams > 0) {
+        fmtp_p->streams = streams;
+        return (SDP_SUCCESS);
+    } else {
+        return (SDP_FAILURE);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_data_channel_protocol (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                const char *protocol)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (!attr_p) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_DATACHANNEL;
+    if (protocol) {
+        sstrncpy(fmtp_p->protocol, protocol,
+          SDP_MAX_STRING_LEN+1);
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_get_fmtp_data_channel_protocol (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num, char* protocol)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+       return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (!attr_p) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+       sstrncpy(protocol, attr_p->attr.fmtp.protocol, SDP_MAX_STRING_LEN+1);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_max_cpb (void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num,
+                                        u32 max_cpb)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (max_cpb > 0) {
+        fmtp_p->max_cpb  = max_cpb;
+        return (SDP_SUCCESS);
+    } else {
+        return (SDP_FAILURE);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_max_dpb (void *sdp_ptr, u16 level,
+                                        u8 cap_num, u16 inst_num,
+                                        u32 max_dpb)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    if (max_dpb > 0) {
+        fmtp_p->max_dpb  = max_dpb;
+        return (SDP_SUCCESS);
+    } else {
+        return (SDP_FAILURE);
+    }
+}
+
+sdp_result_e sdp_attr_set_fmtp_max_rcmd_nalu_size (void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                              u32 max_rcmd_nalu_size)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->max_rcmd_nalu_size = max_rcmd_nalu_size;
+    fmtp_p->flag |= SDP_MAX_RCMD_NALU_SIZE_FLAG;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_deint_buf_cap (void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                              u32 deint_buf_cap)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->deint_buf_cap = deint_buf_cap;
+    fmtp_p->flag |= SDP_DEINT_BUF_CAP_FLAG;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_h264_parameter_add (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              tinybool parameter_add)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+    fmtp_p->parameter_add = parameter_add;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_h261_annex_params (void *sdp_ptr, u16 level,
+                                                  u8 cap_num, u16 inst_num,
+                                                  tinybool annex_d) {
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    fmtp_p->annex_d = annex_d;
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_attr_set_fmtp_h263_annex_params (void *sdp_ptr, u16 level,
+                                                  u8 cap_num, u16 inst_num,
+                                                  tinybool annex_f,
+                                                  tinybool annex_i,
+                                                  tinybool annex_j,
+                                                  tinybool annex_t,
+                                                  u16 annex_k_val,
+                                                  u16 annex_n_val,
+                                                  u16 annex_p_val_picture_resize,
+                                                  u16 annex_p_val_warp)
+
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_fmtp_t  *fmtp_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    fmtp_p = &(attr_p->attr.fmtp);
+    fmtp_p->fmtp_format = SDP_FMTP_CODEC_INFO;
+
+    fmtp_p->annex_f = annex_f;
+    fmtp_p->annex_i = annex_i;
+    fmtp_p->annex_j = annex_j;
+    fmtp_p->annex_t = annex_t;
+
+    fmtp_p->annex_k_val = annex_k_val;
+    fmtp_p->annex_n_val = annex_n_val;
+
+    fmtp_p->annex_p_val_picture_resize = annex_p_val_picture_resize;
+    fmtp_p->annex_p_val_warp = annex_p_val_warp;
+
+    return (SDP_SUCCESS);
+}
+
+
+
+/* Function:    sdp_attr_fmtp_is_annexb_set
+ * Description: Gives the value of the fmtp attribute annexb type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *
+ *
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_attr_fmtp_is_annexb_set (void *sdp_ptr, u16 level, u8 cap_num,
+                                      u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.fmtp.annexb);
+    }
+}
+
+/* Function:    sdp_attr_fmtp_is_annexa_set
+ * Description: Gives the value of the fmtp attribute annexa type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *
+ *
+ * Returns:     TRUE or FALSE.
+ */
+tinybool sdp_attr_fmtp_is_annexa_set (void *sdp_ptr, u16 level, u8 cap_num,
+                                      u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+   if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+   }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.fmtp.annexa);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_bitrate_type
+ * Description: Gets the value of the fmtp attribute bitrate type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Bitrate type value.
+ */
+int32 sdp_attr_get_fmtp_bitrate_type (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.bitrate);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_qcif
+ * Description: Gets the value of the fmtp attribute QCIF type parameter
+ *              for a given Video codec.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     QCIF value.
+ */
+int32 sdp_attr_get_fmtp_qcif (void *sdp_ptr, u16 level,
+                            u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.qcif);
+    }
+}
+/* Function:    sdp_attr_get_fmtp_cif
+ * Description: Gets the value of the fmtp attribute CIF type parameter
+ *              for a given Video codec.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     CIF value.
+ */
+int32 sdp_attr_get_fmtp_cif (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.cif);
+    }
+}
+
+
+/* Function:    sdp_attr_get_fmtp_sqcif
+ * Description: Gets the value of the fmtp attribute sqcif type parameter
+ *              for a given Video codec.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     sqcif value.
+ */
+int32 sdp_attr_get_fmtp_sqcif (void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.sqcif);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_cif4
+ * Description: Gets the value of the fmtp attribute CIF4 type parameter
+ *              for a given Video codec.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     CIF4 value.
+ */
+int32 sdp_attr_get_fmtp_cif4 (void *sdp_ptr, u16 level,
+                              u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.cif4);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_cif16
+ * Description: Gets the value of the fmtp attribute CIF16 type parameter
+ *              for a given Video codec.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     CIF16 value.
+ */
+
+int32 sdp_attr_get_fmtp_cif16 (void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.cif16);
+    }
+}
+
+
+/* Function:    sdp_attr_get_fmtp_maxbr
+ * Description: Gets the value of the fmtp attribute MAXBR type parameter
+ *              for a given Video codec.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     MAXBR value.
+ */
+int32 sdp_attr_get_fmtp_maxbr (void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.maxbr);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_custom_x
+ * Description: Gets the value of the fmtp attribute CUSTOM type parameter
+ *              for a given Video codec.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     CUSTOM x value.
+ */
+
+int32 sdp_attr_get_fmtp_custom_x (void *sdp_ptr, u16 level,
+                                  u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.custom_x);
+    }
+}
+/* Function:    sdp_attr_get_fmtp_custom_y
+ * Description: Gets the value of the fmtp attribute custom_y type parameter
+ *              for a given Video codec.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     CUSTOM Y-AXIS value.
+ */
+
+int32 sdp_attr_get_fmtp_custom_y (void *sdp_ptr, u16 level,
+                                  u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.custom_y);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_custom_mpi
+ * Description: Gets the value of the fmtp attribute CUSTOM type parameter
+ *              for a given Video codec.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     CUSTOM MPI value.
+ */
+
+int32 sdp_attr_get_fmtp_custom_mpi (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.custom_mpi);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_par_width
+ * Description: Gets the value of the fmtp attribute PAR (width) parameter
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     PAR - width value.
+ */
+int32 sdp_attr_get_fmtp_par_width (void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.par_width);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_par_height
+ * Description: Gets the value of the fmtp attribute PAR (height) parameter
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     PAR - height value.
+ */
+int32 sdp_attr_get_fmtp_par_height (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.par_height);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_cpcf
+ * Description: Gets the value of the fmtp attribute- CPCF parameter
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     CPCF value.
+ */
+int32 sdp_attr_get_fmtp_cpcf (void *sdp_ptr, u16 level,
+                              u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.cpcf);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_bpp
+ * Description: Gets the value of the fmtp attribute- BPP parameter
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     BPP value.
+ */
+int32 sdp_attr_get_fmtp_bpp (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.bpp);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_hrd
+ * Description: Gets the value of the fmtp attribute- HRD parameter
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     HRD value.
+ */
+int32 sdp_attr_get_fmtp_hrd (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.hrd);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_profile
+ * Description: Gets the value of the fmtp attribute- PROFILE parameter
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     PROFILE value.
+ */
+int32 sdp_attr_get_fmtp_profile (void *sdp_ptr, u16 level,
+                                 u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.profile);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_level
+ * Description: Gets the value of the fmtp attribute- LEVEL parameter
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     LEVEL value.
+ */
+int32 sdp_attr_get_fmtp_level (void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.level);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_interlace
+ * Description: Checks if INTERLACE parameter is set.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     TRUE if INTERLACE is present and FALSE if INTERLACE is absent.
+ */
+tinybool sdp_attr_get_fmtp_interlace (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return FALSE;
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return FALSE;
+    } else {
+        return (attr_p->attr.fmtp.is_interlace);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_pack_mode
+ * Description: Gets the value of the fmtp attribute- packetization-mode parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     packetization-mode value in the range 0 - 2.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_pack_mode (void *sdp_ptr, u16 level,
+                                 u8 cap_num, u16 inst_num, u16 *val)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+       *val = attr_p->attr.fmtp.packetization_mode;
+       return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_level_asymmetry_allowed
+ * Description: Gets the value of the fmtp attribute- level-asymmetry-allowed parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     level asymmetry allowed value (0 or 1).
+ */
+
+sdp_result_e sdp_attr_get_fmtp_level_asymmetry_allowed (void *sdp_ptr, u16 level,
+                                 u8 cap_num, u16 inst_num, u16 *val)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+       *val = attr_p->attr.fmtp.level_asymmetry_allowed;
+       return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_profile_id
+ * Description: Gets the value of the fmtp attribute- profile-level-id parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     profile-level-id value.
+ */
+const char* sdp_attr_get_fmtp_profile_id (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.fmtp.profile_level_id);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_param_sets
+ * Description: Gets the value of the fmtp attribute- parameter-sets parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     parameter-sets value.
+ */
+const char* sdp_attr_get_fmtp_param_sets (void *sdp_ptr, u16 level,
+                                          u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.fmtp.parameter_sets);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_interleaving_depth
+ * Description: Gets the value of the fmtp attribute- interleaving_depth parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     interleaving_depth value
+ */
+
+sdp_result_e sdp_attr_get_fmtp_interleaving_depth (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num, u16* val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+       *val = attr_p->attr.fmtp.interleaving_depth;
+       return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_deint_buf_req
+ * Description: Gets the value of the fmtp attribute- deint-buf-req parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     deint-buf-req value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_deint_buf_req (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             u32 *val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (attr_p->attr.fmtp.flag & SDP_DEINT_BUF_REQ_FLAG) {
+           *val = attr_p->attr.fmtp.deint_buf_req;
+           return (SDP_SUCCESS);
+        } else {
+            return (SDP_FAILURE);
+        }
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_max_don_diff
+ * Description: Gets the value of the fmtp attribute- max-don-diff parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     max-don-diff value.
+ */
+sdp_result_e sdp_attr_get_fmtp_max_don_diff (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num,
+                                      u32 *val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+       *val = attr_p->attr.fmtp.max_don_diff;
+       return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_init_buf_time
+ * Description: Gets the value of the fmtp attribute- init-buf-time parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     init-buf-time value.
+ */
+sdp_result_e sdp_attr_get_fmtp_init_buf_time (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             u32 *val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (attr_p->attr.fmtp.flag & SDP_INIT_BUF_TIME_FLAG) {
+           *val = attr_p->attr.fmtp.init_buf_time;
+           return (SDP_SUCCESS);
+        } else {
+            return (SDP_FAILURE);
+        }
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_max_mbps
+ * Description: Gets the value of the fmtp attribute- max-mbps parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     max-mbps value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_max_mbps (void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num,
+                                u32 *val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = attr_p->attr.fmtp.max_mbps;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_max_fs
+ * Description: Gets the value of the fmtp attribute- max-fs parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     max-fs value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_max_fs (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, u32 *val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = attr_p->attr.fmtp.max_fs;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_max_cpb
+ * Description: Gets the value of the fmtp attribute- max-cpb parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     max-cpb value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_max_cpb (void *sdp_ptr, u16 level,
+                                 u8 cap_num, u16 inst_num, u32 *val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = attr_p->attr.fmtp.max_cpb;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_max_dpb
+ * Description: Gets the value of the fmtp attribute- max-dpb parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     max-dpb value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_max_dpb (void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num, u32 *val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = attr_p->attr.fmtp.max_dpb;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_attr_get_fmtp_max_br
+ * Description: Gets the value of the fmtp attribute- max-br parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     max-br value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_max_br (void *sdp_ptr, u16 level,
+                             u8 cap_num, u16 inst_num, u32* val)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        *val = attr_p->attr.fmtp.max_br;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_fmtp_is_redundant_pic_cap
+ * Description: Gets the value of the fmtp attribute- redundant_pic_cap parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     redundant-pic-cap value.
+ */
+tinybool sdp_attr_fmtp_is_redundant_pic_cap (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.fmtp.redundant_pic_cap);
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_deint_buf_cap
+ * Description: Gets the value of the fmtp attribute- deint-buf-cap parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     deint-buf-cap value.
+ */
+
+sdp_result_e sdp_attr_get_fmtp_deint_buf_cap (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             u32 *val)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (attr_p->attr.fmtp.flag & SDP_DEINT_BUF_CAP_FLAG) {
+           *val = attr_p->attr.fmtp.deint_buf_cap;
+           return (SDP_SUCCESS);
+        } else {
+            return (SDP_FAILURE);
+        }
+    }
+}
+
+/* Function:    sdp_attr_get_fmtp_max_rcmd_nalu_size
+ * Description: Gets the value of the fmtp attribute- max-rcmd-nalu-size parameter for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     max-rcmd-nalu-size value.
+ */
+sdp_result_e sdp_attr_get_fmtp_max_rcmd_nalu_size (void *sdp_ptr, u16 level,
+                                                  u8 cap_num, u16 inst_num,
+                                                  u32 *val)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        if (attr_p->attr.fmtp.flag & SDP_MAX_RCMD_NALU_SIZE_FLAG) {
+           *val = attr_p->attr.fmtp.max_rcmd_nalu_size;
+           return (SDP_SUCCESS);
+        } else {
+            return (SDP_FAILURE);
+        }
+    }
+}
+
+/* Function:    sdp_attr_fmtp_is_parameter_add
+ * Description: Gets the value of the fmtp attribute- parameter-add for H.264 codec
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     TRUE/FALSE ( parameter-add is boolean)
+ */
+tinybool sdp_attr_fmtp_is_parameter_add (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.fmtp.parameter_add);
+    }
+}
+
+/****** Following functions are get routines for Annex values
+ * For each Annex support, the get routine will return the boolean TRUE/FALSE
+ * Some Annexures for Video codecs have values defined . In those cases,
+ * (e.g Annex K, P ) , the return values are not boolean.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Annex value
+ */
+
+tinybool sdp_attr_get_fmtp_annex_d (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.fmtp.annex_d);
+    }
+}
+
+tinybool sdp_attr_get_fmtp_annex_f (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.fmtp.annex_f);
+    }
+}
+
+tinybool sdp_attr_get_fmtp_annex_i (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.fmtp.annex_i);
+    }
+}
+
+tinybool sdp_attr_get_fmtp_annex_j (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.fmtp.annex_j);
+    }
+}
+
+tinybool sdp_attr_get_fmtp_annex_t (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.fmtp.annex_t);
+    }
+}
+
+int32 sdp_attr_get_fmtp_annex_k_val (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.annex_k_val);
+    }
+}
+
+int32 sdp_attr_get_fmtp_annex_n_val (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.annex_n_val);
+    }
+}
+
+int32 sdp_attr_get_fmtp_annex_p_picture_resize (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num)
+{
+
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.annex_p_val_picture_resize);
+    }
+}
+
+int32 sdp_attr_get_fmtp_annex_p_warp (void *sdp_ptr, u16 level,
+                                      u8 cap_num, u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        return (attr_p->attr.fmtp.annex_p_val_warp);
+    }
+}
+
+/* Function:    sdp_attr_fmtp_get_fmtp_format
+ * Description: Gives the value of the fmtp attribute fmtp_format
+ *              type parameter
+ *              for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *
+ *
+ * Returns:     Enum type sdp_fmtp_format_type_e
+ */
+sdp_fmtp_format_type_e  sdp_attr_fmtp_get_fmtp_format (void *sdp_ptr,
+                                                       u16 level, u8 cap_num,
+                                                       u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_FMTP_UNKNOWN_TYPE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_FMTP,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s fmtp attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_FMTP_UNKNOWN_TYPE);
+    } else {
+        return (attr_p->attr.fmtp.fmtp_format);
+    }
+}
+
+/* Function:    sdp_attr_get_pccodec_num_payload_types
+ * Description: Returns the number of payload types specified for the
+ *              given X-pc-codec attribute.  If the given attribute is not
+ *              defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Number of payload types.
+ */
+u16 sdp_attr_get_pccodec_num_payload_types (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_X_PC_CODEC,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-pc-codec attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        return (attr_p->attr.pccodec.num_payloads);
+    }
+}
+
+/* Function:    sdp_attr_get_pccodec_payload_type
+ * Description: Returns the value of the specified payload type for the
+ *              given X-pc-codec attribute.  If the given attribute is not
+ *              defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              payload_num The payload number to get.  Range is (1 -
+ *                          max num payloads).
+ * Returns:     Payload type.
+ */
+u16 sdp_attr_get_pccodec_payload_type (void *sdp_ptr, u16 level, u8 cap_num,
+                                       u16 inst_num, u16 payload_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_X_PC_CODEC,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-pc-codec attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        if ((payload_num < 1) ||
+            (payload_num > attr_p->attr.pccodec.num_payloads)) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s X-pc-codec attribute, level %u instance %u, "
+                          "invalid payload number %u requested.",
+                          sdp_p->debug_str, level, inst_num, payload_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return (0);
+        } else {
+            return (attr_p->attr.pccodec.payload_type[payload_num-1]);
+        }
+    }
+}
+
+/* Function:    sdp_attr_add_pccodec_payload_type
+ * Description: Add a new value to the list of payload types specified for
+ *              the given X-pc-codec attribute.  The payload type will be
+ *              added to the end of the list so these values should be added
+ *              in the order they will be displayed within the attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              payload_type The payload type to add.
+ * Returns:     SDP_SUCCESS            Payload type was added successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_add_pccodec_payload_type (void *sdp_ptr, u16 level,
+                                                u8 cap_num, u16 inst_num,
+                                                u16 payload_type)
+{
+    u16          payload_num;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_X_PC_CODEC,
+                           inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-pc-codec attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        payload_num = attr_p->attr.pccodec.num_payloads++;
+        attr_p->attr.pccodec.payload_type[payload_num] = payload_type;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_xcap_first_cap_num
+ * Description: Gets the first capability number valid for the specified
+ *              X-cap attribute instance.  If the capability is not
+ *              defined, zero is returned.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the capability.
+ *              inst_num    The X-cap instance number to check.
+ * Returns:     Capability number or zero.
+ */
+u16 sdp_attr_get_xcap_first_cap_num (void *sdp_ptr, u16 level, u16 inst_num)
+{
+    u16          cap_num=1;
+    u16          attr_count=0;
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        for (attr_p = sdp_p->sess_attrs_p; attr_p != NULL;
+             attr_p = attr_p->next_p) {
+            if (attr_p->type == SDP_ATTR_X_CAP) {
+                attr_count++;
+                if (attr_count == inst_num) {
+                    return (cap_num);
+                } else {
+                    cap_num += attr_p->attr.cap_p->num_payloads;
+                }
+            }
+        }
+    } else {  /* Capability is at a media level */
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (0);
+        }
+        for (attr_p = mca_p->media_attrs_p; attr_p != NULL;
+             attr_p = attr_p->next_p) {
+            if (attr_p->type == SDP_ATTR_X_CAP) {
+                attr_count++;
+                if (attr_count == inst_num) {
+                    return (cap_num);
+                } else {
+                    cap_num += attr_p->attr.cap_p->num_payloads;
+                }
+            }
+        }
+    }  /* Attr is at a media level */
+
+    if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+        CSFLogError(logTag, "%s X-cap attribute, level %u instance %u "
+                  "not found.", sdp_p->debug_str, level, inst_num);
+    }
+    sdp_p->conf_p->num_invalid_param++;
+    return (0);
+}
+
+/* Function:    sdp_attr_get_xcap_media_type
+ * Description: Returns the media type specified for the given X-cap
+ *              attribute.  If the given attribute is not defined,
+ *              SDP_MEDIA_INVALID is returned.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Media type or SDP_MEDIA_INVALID.
+ */
+sdp_media_e sdp_attr_get_xcap_media_type (void *sdp_ptr, u16 level,
+                                          u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cap_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_MEDIA_INVALID);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_X_CAP, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-cap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_MEDIA_INVALID);
+    } else {
+        cap_p = attr_p->attr.cap_p;
+        return (cap_p->media);
+    }
+}
+
+/* Function:    sdp_attr_get_xcap_transport_type
+ * Description: Returns the transport type specified for the given X-cap
+ *              attribute.  If the given attribute is not defined,
+ *              SDP_TRANSPORT_INVALID is returned.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Media type or SDP_TRANSPORT_INVALID.
+ */
+sdp_transport_e sdp_attr_get_xcap_transport_type (void *sdp_ptr, u16 level,
+                                                  u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cap_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_TRANSPORT_INVALID);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_X_CAP,
+                           inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-cap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_TRANSPORT_INVALID);
+    } else {
+        cap_p = attr_p->attr.cap_p;
+        return (cap_p->transport);
+    }
+}
+
+/* Function:    sdp_attr_get_xcap_num_payload_types
+ * Description: Returns the number of payload types associated with the
+ *              specified X-cap attribute.  If the attribute is invalid,
+ *              zero will be returned.  Application must validate the
+ *              attribute line before using this routine.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Number of payload types or zero.
+ */
+u16 sdp_attr_get_xcap_num_payload_types (void *sdp_ptr, u16 level,
+                                         u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cap_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_X_CAP, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-cap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        cap_p = attr_p->attr.cap_p;
+        return (cap_p->num_payloads);
+    }
+}
+
+/* Function:    sdp_attr_get_xcap_payload_type
+ * Description: Returns the payload type of the specified payload for the
+ *              X-cap attribute line.  If the attr line or payload number is
+ *              invalid, zero will be returned.  Application must validate
+ *              the X-cap attr before using this routine.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ *              payload_num The payload number to retrieve.  Range is
+ *                          (1 - max num payloads).
+ * Returns:     Payload type or zero.
+ */
+u16 sdp_attr_get_xcap_payload_type (void *sdp_ptr, u16 level,
+                                    u16 inst_num, u16 payload_num,
+                                    sdp_payload_ind_e *indicator)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cap_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_X_CAP, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-cap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        cap_p = attr_p->attr.cap_p;
+        if ((payload_num < 1) ||
+            (payload_num > cap_p->num_payloads)) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s X-cap attribute, level %u instance %u, "
+                          "payload num %u invalid.", sdp_p->debug_str,
+                          level, inst_num, payload_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return (0);
+        } else {
+            *indicator = cap_p->payload_indicator[payload_num-1];
+            return (cap_p->payload_type[payload_num-1]);
+        }
+    }
+}
+
+
+/* Function:    sdp_attr_set_xcap_media_type
+ * Description: Sets the value of the media type parameter for the X-cap
+ *              attribute line.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ *              media       Media type for the X-cap attribute.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_attr_set_xcap_media_type (void *sdp_ptr, u16 level,
+                                           u16 inst_num, sdp_media_e media)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cap_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_X_CAP, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-cap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    cap_p = attr_p->attr.cap_p;
+    cap_p->media = media;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_xcap_transport_type
+ * Description: Sets the value of the transport type parameter for the X-cap
+ *              attribute line.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ *              transport   Transport type for the X-cap attribute.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_attr_set_xcap_transport_type(void *sdp_ptr, u16 level,
+                                              u16 inst_num,
+                                              sdp_transport_e transport)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cap_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_X_CAP, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-cap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    cap_p = attr_p->attr.cap_p;
+    cap_p->transport = transport;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_add_xcap_payload_type
+ * Description: Add a new payload type for the X-cap attribute line
+ *              specified. The new payload type will be added at the end
+ *              of the payload type list.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ *              payload_type The new payload type.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_attr_add_xcap_payload_type(void *sdp_ptr, u16 level,
+                                            u16 inst_num, u16 payload_type,
+                                            sdp_payload_ind_e indicator)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cap_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_X_CAP, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-cap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    cap_p = attr_p->attr.cap_p;
+    cap_p->payload_indicator[cap_p->num_payloads] = indicator;
+    cap_p->payload_type[cap_p->num_payloads++] = payload_type;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_get_cdsc_first_cap_num
+ * Description: Gets the first capability number valid for the specified
+ *              CDSC attribute instance.  If the capability is not
+ *              defined, zero is returned.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the capability.
+ *              inst_num    The CDSC instance number to check.
+ * Returns:     Capability number or zero.
+ */
+u16 sdp_attr_get_cdsc_first_cap_num(void *sdp_ptr, u16 level, u16 inst_num)
+{
+    u16          cap_num=1;
+    u16          attr_count=0;
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *mca_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    if (level == SDP_SESSION_LEVEL) {
+        for (attr_p = sdp_p->sess_attrs_p; attr_p != NULL;
+             attr_p = attr_p->next_p) {
+            if (attr_p->type == SDP_ATTR_CDSC) {
+                attr_count++;
+                if (attr_count == inst_num) {
+                    return (cap_num);
+                } else {
+                    cap_num += attr_p->attr.cap_p->num_payloads;
+                }
+            }
+        }
+    } else {  /* Capability is at a media level */
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            sdp_p->conf_p->num_invalid_param++;
+            return (0);
+        }
+        for (attr_p = mca_p->media_attrs_p; attr_p != NULL;
+             attr_p = attr_p->next_p) {
+            if (attr_p->type == SDP_ATTR_CDSC) {
+                attr_count++;
+                if (attr_count == inst_num) {
+                    return (cap_num);
+                } else {
+                    cap_num += attr_p->attr.cap_p->num_payloads;
+                }
+            }
+        }
+    }  /* Attr is at a media level */
+
+    if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+        CSFLogError(logTag, "%s CDSC attribute, level %u instance %u "
+                  "not found.", sdp_p->debug_str, level, inst_num);
+    }
+    sdp_p->conf_p->num_invalid_param++;
+    return (0);
+}
+
+/* Function:    sdp_attr_get_cdsc_media_type
+ * Description: Returns the media type specified for the given CDSC
+ *              attribute.  If the given attribute is not defined,
+ *              SDP_MEDIA_INVALID is returned.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Media type or SDP_MEDIA_INVALID.
+ */
+sdp_media_e sdp_attr_get_cdsc_media_type(void *sdp_ptr, u16 level,
+                                         u16 inst_num)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cdsc_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_MEDIA_INVALID);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_CDSC, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s CDSC attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_MEDIA_INVALID);
+    } else {
+        cdsc_p = attr_p->attr.cap_p;
+        return (cdsc_p->media);
+    }
+}
+
+/* Function:    sdp_attr_get_cdsc_transport_type
+ * Description: Returns the transport type specified for the given CDSC
+ *              attribute.  If the given attribute is not defined,
+ *              SDP_TRANSPORT_INVALID is returned.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Media type or SDP_TRANSPORT_INVALID.
+ */
+sdp_transport_e sdp_attr_get_cdsc_transport_type(void *sdp_ptr, u16 level,
+                                                 u16 inst_num)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cdsc_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_TRANSPORT_INVALID);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_CDSC,
+                           inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s CDSC attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_TRANSPORT_INVALID);
+    } else {
+        cdsc_p = attr_p->attr.cap_p;
+        return (cdsc_p->transport);
+    }
+}
+
+/* Function:    sdp_attr_get_cdsc_num_payload_types
+ * Description: Returns the number of payload types associated with the
+ *              specified CDSC attribute.  If the attribute is invalid,
+ *              zero will be returned.  Application must validate the
+ *              attribute line before using this routine.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Number of payload types or zero.
+ */
+u16 sdp_attr_get_cdsc_num_payload_types (void *sdp_ptr, u16 level,
+                                         u16 inst_num)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cdsc_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_CDSC, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s CDSC attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        cdsc_p = attr_p->attr.cap_p;
+        return (cdsc_p->num_payloads);
+    }
+}
+
+/* Function:    sdp_attr_get_cdsc_payload_type
+ * Description: Returns the payload type of the specified payload for the
+ *              CDSC attribute line.  If the attr line or payload number is
+ *              invalid, zero will be returned.  Application must validate
+ *              the CDSC attr before using this routine.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ *              payload_num The payload number to retrieve.  Range is
+ *                          (1 - max num payloads).
+ * Returns:     Payload type or zero.
+ */
+u16 sdp_attr_get_cdsc_payload_type (void *sdp_ptr, u16 level,
+                                    u16 inst_num, u16 payload_num,
+                                    sdp_payload_ind_e *indicator)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cdsc_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_CDSC, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s CDSC attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        cdsc_p = attr_p->attr.cap_p;
+        if ((payload_num < 1) ||
+            (payload_num > cdsc_p->num_payloads)) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s CDSC attribute, level %u instance %u, "
+                          "payload num %u invalid.", sdp_p->debug_str,
+                          level, inst_num, payload_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return (0);
+        } else {
+            *indicator = cdsc_p->payload_indicator[payload_num-1];
+            return (cdsc_p->payload_type[payload_num-1]);
+        }
+    }
+}
+
+/* Function:    sdp_attr_set_cdsc_media_type
+ * Description: Sets the value of the media type parameter for the CDSC
+ *              attribute line.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ *              media       Media type for the CDSC attribute.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_attr_set_cdsc_media_type (void *sdp_ptr, u16 level,
+                                           u16 inst_num, sdp_media_e media)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cdsc_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_CDSC, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s CDSC attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    cdsc_p = attr_p->attr.cap_p;
+    cdsc_p->media = media;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_set_cdsc_transport_type
+ * Description: Sets the value of the transport type parameter for the CDSC
+ *              attribute line.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ *              transport   Transport type for the CDSC attribute.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_attr_set_cdsc_transport_type (void *sdp_ptr, u16 level,
+                                      u16 inst_num, sdp_transport_e transport)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cdsc_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_CDSC, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s CDSC attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    cdsc_p = attr_p->attr.cap_p;
+    cdsc_p->transport = transport;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_attr_add_cdsc_payload_type
+ * Description: Add a new payload type for the CDSC attribute line
+ *              specified. The new payload type will be added at the end
+ *              of the payload type list.
+ *              Note: cap_num is not specified.  It must be zero.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ *              payload_type The new payload type.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER
+ */
+sdp_result_e sdp_attr_add_cdsc_payload_type (void *sdp_ptr, u16 level,
+                                             u16 inst_num, u16 payload_type,
+                                             sdp_payload_ind_e indicator)
+{
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    sdp_mca_t   *cdsc_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_CDSC, inst_num);
+    if ((attr_p == NULL) || (attr_p->attr.cap_p == NULL)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s CDSC attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    cdsc_p = attr_p->attr.cap_p;
+    cdsc_p->payload_indicator[cdsc_p->num_payloads] = indicator;
+    cdsc_p->payload_type[cdsc_p->num_payloads++] = payload_type;
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_media_dynamic_payload_valid
+ * Description: Checks if the dynamic payload type passed in is defined
+ *              on the media line m_line
+ * Parameters:  sdp_ptr      The SDP handle returned by sdp_init_description.
+ *              payload_type  Payload type to be checked
+ *
+ * Returns:     TRUE or FALSE. Returns TRUE if payload type is defined on the
+ *              media line, else returns FALSE
+ */
+
+tinybool sdp_media_dynamic_payload_valid (void *sdp_ptr, u16 payload_type,
+                                          u16 m_line)
+{
+   u16 p_type,m_ptype;
+   ushort num_payload_types;
+   sdp_payload_ind_e ind;
+   tinybool payload_matches = FALSE;
+   tinybool result = TRUE;
+   sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+
+   if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+   }
+
+   if ((payload_type < SDP_MIN_DYNAMIC_PAYLOAD) ||
+       (payload_type > SDP_MAX_DYNAMIC_PAYLOAD)) {
+       return FALSE;
+   }
+
+   num_payload_types =
+       sdp_get_media_num_payload_types(sdp_p, m_line);
+
+   for(p_type=1; p_type <=num_payload_types;p_type++){
+
+       m_ptype = (u16)sdp_get_media_payload_type(sdp_p,
+                                            m_line, p_type, &ind);
+       if (payload_type == m_ptype) {
+           payload_matches = TRUE;
+          break;
+       }
+
+   }
+
+   if (!payload_matches) {
+       return FALSE;
+   }
+
+   return (result);
+
+}
+
+/* Function:    sdp_attr_set_rtr_confirm
+ * Description: Sets the rtr confirm value  a= rtr:confirm.
+ *              If this parameter is TRUE, the confirm parameter will be
+ *              specified when the SDP description is built.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              qos_attr    The specific type of qos attribute.  May be
+ *                          qos, secure, X-pc-qos, or X-qos.
+ *              inst_num    The attribute instance number to check.
+ *              confirm     New qos confirm parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_rtr_confirm (void *sdp_ptr, u16 level, u8 cap_num,
+                                       u16 inst_num,
+                                       tinybool confirm)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTR, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(SDP_ATTR_RTR), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.rtr.confirm = confirm;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_rtr_confirm
+ * Description: Returns the value of the rtr attribute confirm
+ *              parameter specified for the given attribute.  Returns TRUE if
+ *              the confirm parameter is specified.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Boolean value.
+ */
+tinybool sdp_attr_get_rtr_confirm (void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_RTR, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s %s attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str,
+                      sdp_get_attr_name(SDP_ATTR_RTR), level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.rtr.confirm);
+    }
+}
+
+
+
+sdp_mediadir_role_e sdp_attr_get_comediadir_role (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_MEDIADIR_ROLE_UNKNOWN);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_DIRECTION, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Comediadir role attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_MEDIADIR_ROLE_UNKNOWN);
+    } else {
+        return (attr_p->attr.comediadir.role);
+    }
+}
+
+/* Function:    sdp_attr_set_comediadir_role
+ * Description: Sets the value of the comediadir role parameter
+ *              for the direction attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              comediadir_role The role of the comedia direction attribute
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_comediadir_role (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       sdp_mediadir_role_e comediadir_role)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_DIRECTION, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Comediadir role attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.comediadir.role = comediadir_role;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_silencesupp_enabled
+ * Description: Returns the value of the silencesupp attribute enable
+ *              parameter specified for the given attribute.  Returns TRUE if
+ *              the confirm parameter is specified.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Boolean value.
+ */
+tinybool sdp_attr_get_silencesupp_enabled (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (FALSE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silenceSuppEnable attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (FALSE);
+    } else {
+        return (attr_p->attr.silencesupp.enabled);
+    }
+}
+
+/* Function:    sdp_attr_get_silencesupp_timer
+ * Description: Returns the value of the silencesupp attribute timer
+ *              parameter specified for the given attribute.  null_ind
+ *              is set to TRUE if no value was specified, but instead the
+ *              null "-" value was specified.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     16-bit timer value
+ *              boolean null_ind
+ */
+u16 sdp_attr_get_silencesupp_timer (void *sdp_ptr, u16 level,
+                                    u8 cap_num, u16 inst_num,
+                                    tinybool *null_ind)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silenceTimer attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        *null_ind = attr_p->attr.silencesupp.timer_null;
+        return (attr_p->attr.silencesupp.timer);
+    }
+}
+
+/* Function:    sdp_attr_get_silencesupp_pref
+ * Description: Sets the silencesupp supppref value
+ *              If this parameter is TRUE, the confirm parameter will be
+ *              specified when the SDP description is built.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              confirm     New qos confirm parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_silencesupp_pref_e sdp_attr_get_silencesupp_pref (void *sdp_ptr,
+                                                      u16 level, u8 cap_num,
+                                                      u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_SILENCESUPP_PREF_UNKNOWN);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silence suppPref attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_SILENCESUPP_PREF_UNKNOWN);
+    } else {
+        return (attr_p->attr.silencesupp.pref);
+    }
+}
+
+/* Function:    sdp_attr_get_silencesupp_siduse
+ * Description: Returns the value of the silencesupp attribute siduse
+ *              parameter specified for the given attribute.  If the given
+ *              attribute is not defined, SDP_QOS_STRENGTH_UNKNOWN is
+ *              returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     silencesupp siduse enum.
+ */
+sdp_silencesupp_siduse_e sdp_attr_get_silencesupp_siduse (void *sdp_ptr,
+                                                          u16 level,
+                                                          u8 cap_num,
+                                                          u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_SILENCESUPP_SIDUSE_UNKNOWN);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silence sidUse attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_SILENCESUPP_SIDUSE_UNKNOWN);
+    } else {
+        return (attr_p->attr.silencesupp.siduse);
+    }
+}
+
+/* Function:    sdp_attr_get_silencesupp_fxnslevel
+ * Description: Returns the value of the silencesupp attribute fxns
+ *              (fixed noise) parameter specified for the given attribute.
+ *              null_ind is set to TRUE if no value was specified,
+ *              but instead the null "-" value was specified.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     7-bit fxns value
+ *              boolean null_ind
+ */
+u8 sdp_attr_get_silencesupp_fxnslevel (void *sdp_ptr, u16 level,
+                                       u8 cap_num, u16 inst_num,
+                                       tinybool *null_ind)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silence fxnslevel attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        *null_ind = attr_p->attr.silencesupp.fxnslevel_null;
+        return (attr_p->attr.silencesupp.fxnslevel);
+    }
+}
+
+/* Function:    sdp_attr_set_silencesupp_enabled
+ * Description: Sets the silencesupp enable value
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              confirm     New silencesupp enable parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_silencesupp_enabled (void *sdp_ptr, u16 level,
+                                               u8 cap_num, u16 inst_num,
+                                               tinybool enable)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silenceSuppEnable attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.silencesupp.enabled = enable;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_silencesupp_timer
+ * Description: Sets the silencesupp timer value
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              value       New silencesupp timer parameter.
+ *              null_ind    if TRUE, timer value is set to "-" instead of
+ *                          the 16 bit numeric value
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_silencesupp_timer (void *sdp_ptr, u16 level,
+                                             u8 cap_num, u16 inst_num,
+                                             u16 value, tinybool null_ind)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silenceTimer attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.silencesupp.timer = value;
+        attr_p->attr.silencesupp.timer_null = null_ind;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_silencesupp_pref
+ * Description: Sets the silencesupp supppref value
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              pref        New silencesupp supppref parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_silencesupp_pref (void *sdp_ptr, u16 level,
+                                            u8 cap_num, u16 inst_num,
+                                            sdp_silencesupp_pref_e pref)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silence SuppPref attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.silencesupp.pref = pref;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_silencesupp_siduse
+ * Description: Sets the silencesupp supppref value
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              siduse      New silencesupp siduse parameter.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_silencesupp_siduse (void *sdp_ptr, u16 level,
+                                              u8 cap_num, u16 inst_num,
+                                              sdp_silencesupp_siduse_e siduse)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silence sidUse attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.silencesupp.siduse = siduse;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_set_silencesupp_fxnslevel
+ * Description: Sets the silencesupp timer value
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              value       New silencesupp timer parameter.
+ *              null_ind    if TRUE, timer value is set to "-" instead of
+ *                          the 16 bit numeric value
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_silencesupp_fxnslevel (void *sdp_ptr, u16 level,
+                                                 u8 cap_num, u16 inst_num,
+                                                 u16 value, tinybool null_ind)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SILENCESUPP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s silenceTimer attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.silencesupp.fxnslevel = (u8)value;
+        attr_p->attr.silencesupp.fxnslevel_null = null_ind;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_attr_get_mptime_num_intervals
+ * Description: Returns the number of intervals specified for the
+ *              given mptime attribute.  If the given attribute is not
+ *              defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Number of intervals.
+ */
+u16 sdp_attr_get_mptime_num_intervals (
+    void *sdp_ptr,
+    u16 level,
+    u8 cap_num,
+    u16 inst_num) {
+
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return 0;
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_MPTIME, inst_num);
+    if (attr_p != NULL) {
+        return attr_p->attr.mptime.num_intervals;
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+        CSFLogError(logTag, "%s mptime attribute, level %u instance %u not found.",
+                  sdp_p->debug_str, level, inst_num);
+    }
+    sdp_p->conf_p->num_invalid_param++;
+    return 0;
+}
+
+/* Function:    sdp_attr_get_mptime_interval
+ * Description: Returns the value of the specified interval for the
+ *              given mptime attribute.  If the given attribute is not
+ *              defined, zero is returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              interval_num The interval number to get.  Range is (1 -
+ *                          max num payloads).
+ * Returns:     Interval.
+ */
+u16 sdp_attr_get_mptime_interval (
+    void *sdp_ptr,
+    u16 level,
+    u8 cap_num,
+    u16 inst_num,
+    u16 interval_num) {
+
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return 0;
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_MPTIME, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s mptime attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return 0;
+    }
+
+    if ((interval_num<1) || (interval_num>attr_p->attr.mptime.num_intervals)) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s mptime attribute, level %u instance %u, "
+                      "invalid interval number %u requested.",
+                      sdp_p->debug_str, level, inst_num, interval_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return 0;
+    }
+
+    return attr_p->attr.mptime.intervals[interval_num-1];
+}
+
+/* Function:    sdp_attr_add_mptime_interval
+ * Description: Add a new value to the list of intervals specified for
+ *              the given mptime attribute.  The interval will be
+ *              added to the end of the list so these values should be added
+ *              in the order they will be displayed within the attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              mp_interval The interval to add.
+ * Returns:     SDP_SUCCESS            Interval was added successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ *              SDP_INVALID_SDP_PTR    Supplied SDP pointer is invalid
+ */
+sdp_result_e sdp_attr_add_mptime_interval (
+    void *sdp_ptr,
+    u16 level,
+    u8 cap_num,
+    u16 inst_num,
+    u16 mp_interval) {
+
+    u16 interval_num;
+    sdp_t *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_SDP_PTR;
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num, SDP_ATTR_MPTIME, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s mptime attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return SDP_INVALID_PARAMETER;
+    }
+
+    interval_num = attr_p->attr.mptime.num_intervals;
+    if (interval_num>=SDP_MAX_PAYLOAD_TYPES) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s mptime attribute, level %u instance %u "
+                      "exceeds maximum length.",
+                      sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return SDP_INVALID_PARAMETER;
+    }
+
+    attr_p->attr.mptime.intervals[interval_num] = mp_interval;
+    ++attr_p->attr.mptime.num_intervals;
+    return SDP_SUCCESS;
+}
+
+
+
+/* Function:    sdp_get_group_attr
+ * Description: Returns the attribute parameter from the a=group:<>
+ *              line.  If no attrib has been set ,
+ *              SDP_GROUP_ATTR_UNSUPPORTED will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       SDP_SESSION_LEVEL
+ * Returns:     Valid attrib value or SDP_GROUP_ATTR_UNSUPPORTED.
+ */
+sdp_group_attr_e sdp_get_group_attr (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t          *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_GROUP_ATTR_UNSUPPORTED);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_GROUP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Group (a= group line) attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_GROUP_ATTR_UNSUPPORTED);
+    } else {
+       if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+           SDP_PRINT("%s Stream data group attr field is :%s ",
+                    sdp_p->debug_str,
+                    sdp_get_group_attr_name(attr_p->attr.stream_data.group_attr) );
+        }
+        return (attr_p->attr.stream_data.group_attr);
+    }
+}
+
+/* Function:    sdp_set_group_attr
+ * Description: Sets the value of the group attribute for the
+ *              a=group:<val> line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       SDP_SESSION_LEVEL
+ *              group_attr  group attribute value ( LS/FID ).
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER/SDP_INVALID_SDP_PTR
+*/
+
+sdp_result_e sdp_set_group_attr (void *sdp_ptr, u16 level,
+                                 u8 cap_num, u16 inst_num,
+                                 sdp_group_attr_e group_attr)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_GROUP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Group attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.stream_data.group_attr = group_attr;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_get_group_num_id
+ * Description: Returns the number of ids from the a=group:<>  line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       SDP_SESSION_LEVEL
+ * Returns:    Num of group ids present or 0 if there is an error.
+ */
+u16 sdp_get_group_num_id (void *sdp_ptr, u16 level,
+                          u8 cap_num, u16 inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t          *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (0);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_GROUP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s a=group level attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (0);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Stream data group attr - num of ids is :%d ",
+                      sdp_p->debug_str,
+                      attr_p->attr.stream_data.num_group_id);
+        }
+    }
+    return (attr_p->attr.stream_data.num_group_id);
+}
+
+/* Function:    sdp_set_group_num_id
+ * Description: Sets the number og group ids that would be added on
+ *              a=group:<val> <id> <id> ...line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       SDP_SESSION_LEVEL
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER/SDP_INVALID_SDP_PTR
+ * Note:        The application must call this API to set the number of group
+ *              ids to be provided before actually setting the group ids on
+ *              the a=group line.
+*/
+
+sdp_result_e sdp_set_group_num_id (void *sdp_ptr, u16 level,
+                                 u8 cap_num, u16 inst_num,
+                                 u16 group_num_id)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_GROUP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Group attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else if ((group_num_id == 0) || (group_num_id > SDP_MAX_GROUP_STREAM_ID)){
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Number of group id value provided - %u is invalid\n",
+                      sdp_p->debug_str, group_num_id);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.stream_data.num_group_id = group_num_id;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_get_group_id
+ * Description: Returns the number of ids from the a=group:<>  line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       SDP_SESSION_LEVEL
+ *              id_num      Number of the id to retrieve. The range is (1 -
+ *                          SDP_MAX_GROUP_STREAM_ID)
+ * Returns:    Value of the group id at the index specified or
+ *             SDP_INVALID_VALUE if an error
+ */
+int32 sdp_get_group_id (void *sdp_ptr, u16 level,
+                        u8 cap_num, u16 inst_num, u16 id_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t          *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_GROUP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s a=group level attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Stream data group attr - num of ids is :%d ",
+                      sdp_p->debug_str,
+                      attr_p->attr.stream_data.num_group_id);
+        }
+        if ((id_num < 1) || (id_num > attr_p->attr.stream_data.num_group_id)) {
+            return (SDP_INVALID_VALUE);
+        }
+    }
+    return (attr_p->attr.stream_data.group_id_arr[id_num-1]);
+}
+
+/* Function:    sdp_set_group_id
+ * Description: Sets the number og group ids that would be added on
+ *              a=group:<val> <id> <id> ...line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       SDP_SESSION_LEVEL
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER/SDP_INVALID_SDP_PTR
+*/
+
+sdp_result_e sdp_set_group_id (void *sdp_ptr, u16 level,
+                               u8 cap_num, u16 inst_num,
+                               u16 group_id)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    u16 num_group_id;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_GROUP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Group attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+       num_group_id = attr_p->attr.stream_data.num_group_id;
+       if (num_group_id == SDP_MAX_GROUP_STREAM_ID) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s Max number of Group Ids already defined "
+                      "for this group line %u", sdp_p->debug_str, level);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        attr_p->attr.stream_data.group_id_arr[num_group_id] = group_id;
+        attr_p->attr.stream_data.num_group_id++;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_x_sidin
+ * Description: Returns the attribute parameter from the a=X-sidin:<>
+ *              line.  If no attrib has been set NULL will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       media level index
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Pointer to sidin or NULL.
+ */
+const char* sdp_attr_get_x_sidin (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t          *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_X_SIDIN, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-sidin attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Stream X-sidin attr field is :%s ",
+                     sdp_p->debug_str,
+                     attr_p->attr.stream_data.x_sidin);
+        }
+        return (attr_p->attr.stream_data.x_sidin);
+    }
+}
+
+/* Function:    sdp_attr_set_x_sidin
+ * Description: Sets the value of the X-sidin parameter
+ *              for the given attribute. The address is copied into the
+ *              SDP structure so application memory will not be
+ *              referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              sidin       Ptr to the sidin string
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_x_sidin (void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num,
+                                   const char *sidin)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_X_SIDIN, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-sidin attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        sstrncpy(attr_p->attr.stream_data.x_sidin, sidin,
+                 sizeof(attr_p->attr.stream_data.x_sidin)) ;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_x_sidout
+ * Description: Returns the attribute parameter from the a=X-sidout:<>
+ *              line.  If no attrib has been set NULL will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       media level index
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Pointer to sidout or NULL.
+ */
+const char* sdp_attr_get_x_sidout (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t          *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_X_SIDOUT, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-sidout attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Stream X-sidout attr field is :%s ",
+                     sdp_p->debug_str,
+                     attr_p->attr.stream_data.x_sidout);
+        }
+        return (attr_p->attr.stream_data.x_sidout);
+    }
+}
+
+/* Function:    sdp_attr_set_x_sidout
+ * Description: Sets the value of the x-sidout parameter
+ *              for the given attribute. The address is copied into the
+ *              SDP structure so application memory will not be
+ *              referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              sidout      Ptr to the sidout string.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_x_sidout (void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num,
+                                   const char *sidout)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_X_SIDOUT, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-sidout attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        sstrncpy(attr_p->attr.stream_data.x_sidout, sidout,
+                 sizeof(attr_p->attr.stream_data.x_sidout)) ;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_attr_get_x_confid
+ * Description: Returns the attribute parameter from the a=X-confid:<>
+ *              line.  If no attrib has been set NULL will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       media level index
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Pointer to confid or NULL.
+ */
+const char* sdp_attr_get_x_confid (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num)
+{
+    sdp_t               *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t          *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_X_CONFID, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-confid attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    } else {
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Stream X-confid attr field is :%s ",
+                     sdp_p->debug_str,
+                     attr_p->attr.stream_data.x_confid);
+        }
+        return (attr_p->attr.stream_data.x_confid);
+    }
+}
+
+/* Function:    sdp_attr_set_x_confid
+ * Description: Sets the value of the X-confid parameter
+ *              for the given attribute. The address is copied into the
+ *              SDP structure so application memory will not be
+ *              referenced by the SDP lib.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              confid      Ptr to the confid string.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e sdp_attr_set_x_confid (void *sdp_ptr, u16 level,
+                                   u8 cap_num, u16 inst_num,
+                                   const char *confid)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_X_CONFID, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s X-confid attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        sstrncpy(attr_p->attr.stream_data.x_confid, confid,
+                 sizeof(attr_p->attr.stream_data.x_confid)) ;
+        return (SDP_SUCCESS);
+    }
+}
+
+/* Function:    sdp_set_source_filter
+ * Description: Sets the value of the source filter attribute for the
+ *              a=source-filter:<val> line.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       SDP_SESSION_LEVEL/Media level
+ *              mode        Filter-mode (incl/excl)
+ *              nettype     Network type
+ *              addrtype    Address type of the destination
+ *              dest_addr   Destination unicast/multicast address
+ *                          (ip-addr/ fqdn/ *)
+ *              src_addr    One of the source address to which the filter
+ *                          applies, i.e. process/drop the packets.
+ *                          (More source addresses are added using
+ *                           sdp_include_new_filter_src_addr)
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER/SDP_INVALID_SDP_PTR
+ */
+sdp_result_e
+sdp_set_source_filter (void *sdp_ptr, u16 level, u8 cap_num,
+                       u16 inst_num, sdp_src_filter_mode_e mode,
+                       sdp_nettype_e nettype, sdp_addrtype_e addrtype,
+                       const char *dest_addr, const char *src_addr)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    u16 index;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SOURCE_FILTER, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Source filter attribute, level %u instance %u "
+                      "not found", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.source_filter.mode = mode;
+    attr_p->attr.source_filter.nettype = nettype;
+    attr_p->attr.source_filter.addrtype = addrtype;
+    sstrncpy(attr_p->attr.source_filter.dest_addr, dest_addr,
+             SDP_MAX_STRING_LEN+1);
+    if (src_addr) {
+        index = attr_p->attr.source_filter.num_src_addr;
+        sstrncpy(attr_p->attr.source_filter.src_list[index],
+                src_addr,SDP_MAX_STRING_LEN+1);
+        /* Increment source list count if the api was invoked for
+         * first time or else we're basically replacing the index 0
+         * element in src-list.
+         */
+        ++attr_p->attr.source_filter.num_src_addr;
+       SDP_PRINT("%s Source address (%s) number %d added to source filter",
+                 sdp_p->debug_str,src_addr,
+                 attr_p->attr.source_filter.num_src_addr);
+
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_include_new_filter_src_addr
+ * Description: Adds source addresses to the list to which the filter applies
+ *              This is to be invoked only as follow-up to
+ *              sdp_set_source_filter() to include more source addresses
+ * Parameters:  sdp_ptr     The SDP handle to which the filter attributes
+ *                          were added using sdp_set_source_filter
+ *              level       SDP_SESSION_LEVEL/Media level
+ *              src_addr    Source address to be added to the list
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER/SDP_INVALID_SDP_PTR
+ */
+
+sdp_result_e
+sdp_include_new_filter_src_addr (void *sdp_ptr, u16 level, u8 cap_num,
+                                 u16 inst_num, const char *src_addr)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SOURCE_FILTER, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Source filter attribute, level %u instance %u "
+                      "not found", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    if (attr_p->attr.source_filter.num_src_addr >= SDP_MAX_SRC_ADDR_LIST) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Max number of source addresses included for "
+                      "filter for the instance %u", sdp_p->debug_str,
+                       inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_FAILURE);
+    }
+    sstrncpy(attr_p->attr.source_filter.src_list[
+                 attr_p->attr.source_filter.num_src_addr],
+                 src_addr, SDP_MAX_STRING_LEN+1);
+    ++attr_p->attr.source_filter.num_src_addr;
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_get_source_filter_mode
+ * Description: Gets the filter mode in internal representation
+ * Parameters:  sdp_ptr   The SDP handle which contains the attributes
+ *              level     SDP_SESSION_LEVEL/m-line number
+ *              inst_num  The attribute instance number
+ * Returns:     Filter mode (incl/excl/not present)
+ */
+sdp_src_filter_mode_e
+sdp_get_source_filter_mode (void *sdp_ptr, u16 level, u8 cap_num,
+                            u16 inst_num)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_FILTER_MODE_NOT_PRESENT);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SOURCE_FILTER, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Source filter attribute, level %u, "
+                      "instance %u not found", sdp_p->debug_str,
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_FILTER_MODE_NOT_PRESENT);
+    }
+    return (attr_p->attr.source_filter.mode);
+}
+
+/* Function:    sdp_get_filter_destination_attributes
+ * Description: Gets the destination address parameters
+ * Parameters:  Network type (optional), destination address type
+ *              (optional), and destination address (mandatory) variables
+ *              which gets updated.
+ * Returns:     SDP_SUCCESS or SDP_INVALID_PARAMETER/SDP_INVALID_SDP_PTR
+ */
+sdp_result_e
+sdp_get_filter_destination_attributes (void *sdp_ptr, u16 level, u8 cap_num,
+                                       u16 inst_num, sdp_nettype_e *nettype,
+                                       sdp_addrtype_e *addrtype,
+                                       char *dest_addr)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SOURCE_FILTER, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Source filter attribute, level %u instance %u "
+                      "not found", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    if (nettype) {
+        *nettype = attr_p->attr.source_filter.nettype;
+    }
+    if (addrtype) {
+        *addrtype = attr_p->attr.source_filter.addrtype;
+    }
+    sstrncpy(dest_addr, attr_p->attr.source_filter.dest_addr,
+             SDP_MAX_STRING_LEN+1);
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_get_filter_source_address_count
+ * Description: Gets the number of source addresses in the list
+ * Parameters:  sdp_ptr   The SDP handle which contains the attributes
+ *              level     SDP_SESSION_LEVEL/m-line number
+ *              inst_num  The attribute instance number
+ * Returns:     Source-list count
+ */
+
+int32
+sdp_get_filter_source_address_count (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_VALUE);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SOURCE_FILTER, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Source filter attribute, level %u instance %u "
+                      "not found", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_VALUE);
+    }
+    return (attr_p->attr.source_filter.num_src_addr);
+}
+
+/* Function:    sdp_get_filter_source_address
+ * Description: Gets one of the source address that is indexed by the user
+ * Parameters:  sdp_ptr   The SDP handle which contains the attributes
+ *              level     SDP_SESSION_LEVEL/m-line number
+ *              inst_num  The attribute instance number
+ *              src_addr_id User provided index (value in range between
+ *                          0 to (SDP_MAX_SRC_ADDR_LIST-1) which obtains
+ *                          the source addr corresponding to it.
+ *              src_addr  The user provided variable which gets updated
+ *                        with source address corresponding to the index
+ */
+sdp_result_e
+sdp_get_filter_source_address (void *sdp_ptr, u16 level, u8 cap_num,
+                               u16 inst_num, u16 src_addr_id,
+                               char *src_addr)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    src_addr[0] = '\0';
+
+    if (src_addr_id >= SDP_MAX_SRC_ADDR_LIST) {
+        return (SDP_INVALID_PARAMETER);
+    }
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SOURCE_FILTER, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s Source filter attribute, level %u instance %u "
+                      "not found", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    if (src_addr_id >= attr_p->attr.source_filter.num_src_addr) {
+        return (SDP_INVALID_PARAMETER);
+    }
+    sstrncpy(src_addr, attr_p->attr.source_filter.src_list[src_addr_id],
+             SDP_MAX_STRING_LEN+1);
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e
+sdp_set_rtcp_unicast_mode (void *sdp_ptr, u16 level, u8 cap_num,
+                           u16 inst_num, sdp_rtcp_unicast_mode_e mode)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+    if (mode >= SDP_RTCP_MAX_UNICAST_MODE) {
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_RTCP_UNICAST, inst_num);
+
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s RTCP Unicast attribute, level %u instance %u "
+                      "not found", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    attr_p->attr.u32_val = mode;
+
+    return (SDP_SUCCESS);
+}
+
+sdp_rtcp_unicast_mode_e
+sdp_get_rtcp_unicast_mode(void *sdp_ptr, u16 level, u8 cap_num,
+                          u16 inst_num)
+{
+    sdp_t      *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_RTCP_UNICAST_MODE_NOT_PRESENT);
+    }
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_RTCP_UNICAST, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s RTCP Unicast attribute, level %u, "
+                      "instance %u not found", sdp_p->debug_str,
+                      level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_RTCP_UNICAST_MODE_NOT_PRESENT);
+    }
+    return ((sdp_rtcp_unicast_mode_e)attr_p->attr.u32_val);
+}
+
+
+/* Function:    sdp_attr_get_sdescriptions_tag
+ * Description: Returns the value of the sdescriptions tag
+ *              parameter specified for the given attribute.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Tag value or SDP_INVALID_VALUE (-2) if error encountered.
+ */
+
+int32
+sdp_attr_get_sdescriptions_tag (void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_VALUE;
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SDESCRIPTIONS, inst_num);
+
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s srtp attribute tag, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return SDP_INVALID_VALUE;
+    } else {
+        return attr_p->attr.srtp_context.tag;
+    }
+}
+
+/* Function:    sdp_attr_get_sdescriptions_crypto_suite
+ * Description: Returns the value of the sdescriptions crypto suite
+ *              parameter specified for the given attribute. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, return the suite. If it's not,
+ *              try to find the version 9. This assumes you cannot have both
+ *              versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     SDP_SRTP_UNKNOWN_CRYPTO_SUITE is returned if an error was
+ *              encountered otherwise the crypto suite is returned.
+ */
+
+sdp_srtp_crypto_suite_t
+sdp_attr_get_sdescriptions_crypto_suite (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_SRTP_UNKNOWN_CRYPTO_SUITE;
+    }
+
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+
+    if (attr_p == NULL) {
+        /* There's no version 2 so now try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute suite, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_SRTP_UNKNOWN_CRYPTO_SUITE;
+       }
+    }
+
+    return attr_p->attr.srtp_context.suite;
+
+}
+
+/* Function:    sdp_attr_get_sdescriptions_key
+ * Description: Returns the value of the sdescriptions master key
+ *              parameter specified for the given attribute. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, return the key. If it's not,
+ *              try to find the version 9. This assumes you cannot have both
+ *              versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     NULL if error encountered or master key salt string
+ */
+
+const char*
+sdp_attr_get_sdescriptions_key (void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return NULL;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+
+    if (attr_p == NULL) {
+        /* Couldn't find version 2 now try version 9 */
+       attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+
+        if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute key, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return NULL;
+       }
+    }
+
+    return (char*)attr_p->attr.srtp_context.master_key;
+}
+
+
+/* Function:    sdp_attr_get_sdescriptions_salt
+ * Description: Returns the value of the sdescriptions master salt
+ *              parameter specified for the given attribute. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, return the salt. If it's not,
+ *              try to find the version 9. This assumes you cannot have both
+ *              versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     NULL if error encountered or master key salt string
+ */
+
+const char*
+sdp_attr_get_sdescriptions_salt (void *sdp_ptr, u16 level,
+                                 u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return NULL;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+
+    if (attr_p == NULL) {
+        /* Couldn't find version 2 now try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute salt, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return NULL;
+       }
+    }
+
+    return (char*) attr_p->attr.srtp_context.master_salt;
+
+}
+
+
+
+/* Function:    sdp_attr_get_sdescriptions_lifetime
+ * Description: Returns the value of the sdescriptions lifetime
+ *              parameter specified for the given attribute.Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, return the lifetime. If it's
+ *              not, try to find the version 9. This assumes you cannot have
+ *              both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     NULL if error encountered or lifetime string
+ */
+
+const char*
+sdp_attr_get_sdescriptions_lifetime (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return NULL;
+    }
+
+    /* Try version 2 first. */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+
+    if (attr_p == NULL) {
+        /* Couldn't find version 2 now try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute lifetime, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return NULL;
+       }
+    }
+
+    return (char*)attr_p->attr.srtp_context.master_key_lifetime;
+
+}
+
+/* Function:    sdp_attr_get_sdescriptions_mki
+ * Description: Returns the value of the sdescriptions MKI value and length
+ *              parameter of the specified attribute instance. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, return the MKI. If it's
+ *              not, try to find version 9. This assumes you cannot have
+ *              both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              mki_value   application provided pointer that on exit
+ *                          is set to the MKI value string if one exists.
+ *              mki_length application provided pointer that on exit
+ *                         is set to the MKI length if one exists.
+ * Returns:     SDP_SUCCESS no errors encountered otherwise sdp error
+ *              based upon the specific error.
+ */
+
+sdp_result_e
+sdp_attr_get_sdescriptions_mki (void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num,
+                               const char **mki_value,
+                               u16 *mki_length)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    *mki_value = NULL;
+    *mki_length = 0;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_SDP_PTR;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+
+    if (attr_p == NULL) {
+        /* Couldn't find version 2 now try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute MKI, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_INVALID_PARAMETER;
+       }
+    }
+
+    *mki_value = (char*)attr_p->attr.srtp_context.mki;
+    *mki_length = attr_p->attr.srtp_context.mki_size_bytes;
+    return SDP_SUCCESS;
+
+}
+
+
+/* Function:    sdp_attr_get_sdescriptions_session_params
+ * Description: Returns the unparsed session parameters string. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, return session parameters. If
+ *              it's not, try to find version 9. This assumes you cannot have
+ *              both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     NULL if no session parameters were received in the sdp,
+ *              otherwise returns a pointer to the start of the session
+ *              parameters string. Note that the calling function should
+ *              not free the returned pointer.
+ */
+
+const char*
+sdp_attr_get_sdescriptions_session_params (void *sdp_ptr, u16 level,
+                                           u8 cap_num, u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return NULL;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+
+    if (attr_p == NULL) {
+        /* Couldn't find version 2 try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SDESCRIPTIONS, inst_num);
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute session params, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return NULL;
+        }
+    }
+
+    return attr_p->attr.srtp_context.session_parameters;
+}
+
+
+/* Function:    sdp_attr_get_sdescriptions_key_size
+ * Description: Returns the master key size. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, return key size. If
+ *              it's not, try to find version 9. This assumes you cannot have
+ *              both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     0 (SDP_SDESCRIPTIONS_KEY_SIZE_UNKNOWN) if error was
+ *              encountered, otherwise key size.
+ */
+
+unsigned char
+sdp_attr_get_sdescriptions_key_size (void *sdp_ptr,
+                                     u16 level,
+                                    u8 cap_num,
+                                    u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_SDESCRIPTIONS_KEY_SIZE_UNKNOWN;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+
+    if (attr_p == NULL) {
+        /* Couldn't find version 2 now try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute MKI, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_SDESCRIPTIONS_KEY_SIZE_UNKNOWN;
+       }
+    }
+
+    return attr_p->attr.srtp_context.master_key_size_bytes;
+
+}
+
+
+/* Function:    sdp_attr_get_sdescriptions_salt_size
+ * Description: Returns the salt key size. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, return salt size. If
+ *              it's not, try to find version 9. This assumes you cannot have
+ *              both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     0 (SDP_SDESCRIPTIONS_KEY_SIZE_UNKNOWN) if error was
+ *              encountered, otherwise salt size.
+ */
+
+unsigned char
+sdp_attr_get_sdescriptions_salt_size (void *sdp_ptr,
+                                      u16 level,
+                                     u8 cap_num,
+                                     u16 inst_num)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_SDESCRIPTIONS_KEY_SIZE_UNKNOWN;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+
+    if (attr_p == NULL) {
+        /* Couldn't find version 2 now try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute MKI, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_SDESCRIPTIONS_KEY_SIZE_UNKNOWN;
+       }
+    }
+
+    return attr_p->attr.srtp_context.master_salt_size_bytes;
+
+}
+
+
+/* Function:    sdp_attr_get_srtp_crypto_selection_flags
+ * Description: Returns the selection flags. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, return selection flags. If
+ *              it's not, try to find version 9. This assumes you cannot have
+ *              both versions in the same SDP.
+ *              Currently only necessary for MGCP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     0 (SDP_SRTP_CRYPTO_SELECTION_FLAGS_UNKNOWN) if error was
+ *              encountered, otherwise selection flags.
+ */
+
+unsigned long
+sdp_attr_get_srtp_crypto_selection_flags (void *sdp_ptr,
+                                          u16 level,
+                                         u8 cap_num,
+                                         u16 inst_num)
+{
+
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_SRTP_CRYPTO_SELECTION_FLAGS_UNKNOWN;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+
+    if (attr_p == NULL) {
+        /* Couldn't find version 2 now try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute MKI, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_SRTP_CRYPTO_SELECTION_FLAGS_UNKNOWN;
+       }
+    }
+
+    return attr_p->attr.srtp_context.selection_flags;
+
+}
+
+
+
+/* Function:    sdp_attr_set_sdescriptions_tag
+ * Description: Sets the sdescriptions tag parameter
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              tag_num     tag
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e
+sdp_attr_set_sdescriptions_tag (void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num,
+                                int32 tag_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SDESCRIPTIONS, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s srtp attribute tag, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    } else {
+        attr_p->attr.srtp_context.tag = tag_num;
+        return (SDP_SUCCESS);
+    }
+}
+
+
+/* Function:    sdp_attr_set_sdescriptions_crypto_suite
+ * Description: Sets the sdescriptions crypto suite parameter. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, it will set the crypto suite
+ *              for version 2. If it's not, try to find version 9. This assumes
+ *              you cannot have both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              crypto_suite crypto suite
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e
+sdp_attr_set_sdescriptions_crypto_suite (void *sdp_ptr, u16 level,
+                                         u8 cap_num, u16 inst_num,
+                                         sdp_srtp_crypto_suite_t crypto_suite)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+    int         i;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_SDP_PTR;
+    }
+
+    /* Try to find version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+    if (attr_p == NULL) {
+
+        /* Version 2 not found, try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+        if (attr_p == NULL) {
+
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute suite, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_INVALID_PARAMETER;
+       }
+    }
+
+    attr_p->attr.srtp_context.suite = crypto_suite;
+    for (i=0; i < SDP_SRTP_MAX_NUM_CRYPTO_SUITES; i++) {
+         /* For the specified crypto suite, get the size of the
+         * key and salt.
+         */
+        if (sdp_srtp_crypto_suite_array[i].crypto_suite_val ==
+                                           crypto_suite) {
+
+              attr_p->attr.srtp_context.master_key_size_bytes =
+             sdp_srtp_crypto_suite_array[i].key_size_bytes;
+
+             attr_p->attr.srtp_context.master_salt_size_bytes =
+             sdp_srtp_crypto_suite_array[i].salt_size_bytes;
+
+        }
+   }
+
+   return SDP_SUCCESS;
+
+}
+
+
+/* Function:    sdp_attr_set_sdescriptions_key
+ * Description: Sets the sdescriptions key parameter. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, it will set the key for
+ *              version 2. If it's not, try to find version 9. This assumes
+ *              you cannot have both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              key         buffer containing the key assumes null terminated
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e
+sdp_attr_set_sdescriptions_key (void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num,
+                                char *key)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_SDP_PTR;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+    if (attr_p == NULL) {
+        /* Couldn't find version 2 try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SDESCRIPTIONS, inst_num);
+        if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute key, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_INVALID_PARAMETER;
+        }
+
+    }
+
+    bcopy(key, attr_p->attr.srtp_context.master_key,
+         SDP_SRTP_MAX_KEY_SIZE_BYTES);
+
+    return SDP_SUCCESS;
+
+}
+
+
+/* Function:    sdp_attr_set_sdescriptions_salt
+ * Description: Sets the sdescriptions salt parameter. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, it will set the salt for
+ *              version 2. If it's not, try to find version 9. This assumes
+ *              you cannot have both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              salt        buffer containing the salt assumes null terminated
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e
+sdp_attr_set_sdescriptions_salt (void *sdp_ptr, u16 level,
+                                 u8 cap_num, u16 inst_num,
+                                 char *salt)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_SDP_PTR;
+    }
+
+    /* Try to find version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+    if (attr_p == NULL) {
+        /* Couldn't find version 2, try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp attribute salt, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+       }
+
+    }
+
+    bcopy(salt, attr_p->attr.srtp_context.master_salt,
+         SDP_SRTP_MAX_SALT_SIZE_BYTES);
+
+    return SDP_SUCCESS;
+}
+
+
+/* Function:    sdp_attr_set_sdescriptions_lifetime
+ * Description: Sets the sdescriptions lifetime parameter. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, it will set the lifetime for
+ *              version 2. If it's not, try to find version 9. This assumes
+ *              you cannot have both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              lifetime    buffer containing the lifetime assumes null terminated
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e
+sdp_attr_set_sdescriptions_lifetime (void *sdp_ptr, u16 level,
+                                     u8 cap_num, u16 inst_num,
+                                     char *lifetime)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_SDP_PTR;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+    if (attr_p == NULL) {
+        /* Couldn't find version 2, try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+       if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp lifetime attribute, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_INVALID_PARAMETER;
+       }
+
+    }
+
+    sstrncpy((char*)attr_p->attr.srtp_context.master_key_lifetime, lifetime,
+            SDP_SRTP_MAX_LIFETIME_BYTES);
+    return SDP_SUCCESS;
+
+}
+
+
+/* Function:    sdp_attr_set_sdescriptions_mki
+ * Description: Sets the sdescriptions mki parameter compose of the MKI
+ *              value and length. Note that this is a common api for both
+ *              version 2 and version 9 sdescriptions. It has no knowledge
+ *              which version is being used so it will first try to find if
+ *              a version 2 sdescriptions attribute is present. If it is, it will
+ *              set the lifetime for version 2. If it's not, try to find version 9.
+ *              This assumes you cannot have both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              mki_value   buffer containing the mki value. Assumes null
+ *                          terminated buffer.
+ *              mki_length  length of the MKI
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e
+sdp_attr_set_sdescriptions_mki (void *sdp_ptr, u16 level,
+                                u8 cap_num, u16 inst_num,
+                                char *mki_value,
+                               u16 mki_length)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_SDP_PTR;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+    if (attr_p == NULL) {
+        /* Couldn't find version 2, try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+        if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp MKI attribute, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_INVALID_PARAMETER;
+       }
+    }
+
+    sstrncpy((char*)attr_p->attr.srtp_context.mki, mki_value,
+            SDP_SRTP_MAX_MKI_SIZE_BYTES);
+    attr_p->attr.srtp_context.mki_size_bytes = mki_length;
+    return SDP_SUCCESS;
+
+}
+
+
+/* Function:    sdp_attr_set_sdescriptions_key_size
+ * Description: Sets the sdescriptions key size parameter. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, it will set the key for
+ *              version 2. If it's not, try to find version 9. This assumes
+ *              you cannot have both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              key_size    key size
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e
+sdp_attr_set_sdescriptions_key_size (void *sdp_ptr,
+                                     u16 level,
+                                    u8 cap_num,
+                                    u16 inst_num,
+                                    unsigned char key_size)
+
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_SDP_PTR;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+    if (attr_p == NULL) {
+        /* Couldn't find version 2, try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+        if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp MKI attribute, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_INVALID_PARAMETER;
+       }
+    }
+
+    attr_p->attr.srtp_context.master_key_size_bytes = key_size;
+    return SDP_SUCCESS;
+
+}
+
+
+/* Function:    sdp_attr_set_sdescriptions_key_size
+ * Description: Sets the sdescriptions salt size parameter. Note that
+ *              this is a common api for both version 2 and version 9
+ *              sdescriptions. It has no knowledge which version is being
+ *              used so it will first try to find if a version 2 sdescriptions
+ *              attribute is present. If it is, it will set the salt for
+ *              version 2. If it's not, try to find version 9. This assumes
+ *              you cannot have both versions in the same SDP.
+ *
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ *              inst_num    The attribute instance number to check.
+ *              salt_size   salt size
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e
+sdp_attr_set_sdescriptions_salt_size (void *sdp_ptr,
+                                      u16 level,
+                                     u8 cap_num,
+                                     u16 inst_num,
+                                     unsigned char salt_size)
+{
+
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return SDP_INVALID_SDP_PTR;
+    }
+
+    /* Try version 2 first */
+    attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                           SDP_ATTR_SRTP_CONTEXT, inst_num);
+    if (attr_p == NULL) {
+        /* Couldn't find version 2, try version 9 */
+        attr_p = sdp_find_attr(sdp_p, level, cap_num,
+                               SDP_ATTR_SDESCRIPTIONS, inst_num);
+        if (attr_p == NULL) {
+            if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                CSFLogError(logTag, "%s srtp MKI attribute, level %u instance %u "
+                          "not found.", sdp_p->debug_str, level, inst_num);
+            }
+            sdp_p->conf_p->num_invalid_param++;
+            return SDP_INVALID_PARAMETER;
+       }
+    }
+
+    attr_p->attr.srtp_context.master_salt_size_bytes = salt_size;
+    return SDP_SUCCESS;
+
+}
+
diff --git a/libs/sipcc/core/sdp/sdp_base64.c b/libs/sipcc/core/sdp/sdp_base64.c
new file mode 100644 (file)
index 0000000..0cef821
--- /dev/null
@@ -0,0 +1,394 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp_base64.h"
+
+/*
+ * Local definitions for Base64 to Raw table entries.
+ */
+#define INVALID_CHAR 0xFF /* Character not in supported Base64 set */
+#define WHITE_SPACE  0xFE /* Space, tab, newline, etc character */
+#define PADDING      0xFD /* The character '=' */
+
+#define PAD_CHAR     '=' /* The character '=' */
+
+/* Maximum length of a base64 encoded line */
+#define MAX_BASE64_LINE_LENGTH 76
+
+/*
+ * base64_result_table
+ *  String table for translating base64 error codes into human
+ *  understanable strings.
+ */
+char *base64_result_table[BASE64_RESULT_MAX] =
+{
+    "Base64 successful",
+    "Base64 Buffer Overrun",
+    "Base64 Bad Data",
+    "Base64 Bad Padding",
+    "Base64 Bad Block Size"
+};
+
+/*
+ * base64_to_raw_table
+ *  Heart of the Base64 decoding algorithm. Lookup table to convert
+ *  the Base64 characters into their specified representative values.
+ *  Invalid characters are marked with 0xFF, white space characters
+ *  are marked with 0xFE, and the special pading character is marked
+ *  with 0xFD.
+ */
+unsigned char base64_to_raw_table[128] =
+{
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, /* 0-9 */
+    0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 10-19 */
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 20-29 */
+    0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 30-39 */
+    0xFF, 0xFF, 0xFF,   62, 0xFF, 0xFF, 0xFF,   63,   52,   53, /* 40-49 */
+      54,   55,   56,   57,   58,   59,   60,   61, 0xFF, 0xFF, /* 50-59 */
+    0xFF, 0xFD, 0xFF, 0xFF, 0xFF,    0,    1,    2,    3,    4, /* 60-69 */
+       5,    6,    7,    8,    9,   10,   11,   12,   13,   14, /* 70-79 */
+      15,   16,   17,   18,   19,   20,   21,   22,   23,   24, /* 80-89 */
+      25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   26,   27,   28, /* 90-99 */
+      29,   30,   31,   32,   33,   34,   35,   36,   37,   38, /* 100-109 */
+      39,   40,   41,   42,   43,   44,   45,   46,   47,   48, /* 110-119 */
+      49,   50,   51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF              /* 120-127 */
+};
+
+unsigned char raw_to_base64_table[64] =
+{
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0-9 */
+    'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 10-19 */
+    'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', /* 20-29 */
+    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 30-39 */
+    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', /* 40-49 */
+    'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', /* 50-59 */
+    '8', '9', '+', '/'                               /* 60-63 */
+};
+
+/*
+ * base64_encode_size_bytes
+ *
+ * DESCRIPTION
+ *  Estimates the size of buffer required for holding the result of
+ *  encoding data of size raw_size_bytes.
+ *
+ * PARAMETERS
+ *  raw_size_bytes = Estimated size of the un-encoded data in bytes.
+ *
+ * RETURN VALUE
+ *  The size of destination buffer to use for encoding in bytes.
+ */
+int base64_est_encode_size_bytes (int raw_size_bytes)
+{
+    int length;
+
+    /*
+     * Find the number of bytes needed to represent the data
+     * using a 4/3 expansion ratio. That result must be
+     * rounded to the next higher multiple of four to account
+     * for padding. Then add in a term to account for any '\n's
+     * added.
+     */
+    length = ((((raw_size_bytes * 4 + 2)/ 3) + 3) & ~(0x3)) +
+       raw_size_bytes / MAX_BASE64_LINE_LENGTH;
+
+    return length;
+}
+
+/*
+ * base64_decode_size_bytes
+ *
+ * DESCRIPTION
+ *  Estimates the size of buffer required for holding the result of
+ *  decoding data of size base64_size_bytes.
+ *
+ * PARAMETERS
+ *  base64_size_bytes = Estimated size of the Base64 data in bytes.
+ *
+ * RETURN VALUE
+ *  The size of destination buffer to use for decoding in bytes.
+ */
+int base64_est_decode_size_bytes (int base64_size_bytes)
+{
+    int length;
+
+    length = (base64_size_bytes * 3 + 3) / 4;
+    return length;
+}
+
+/*
+ * base64_encode
+ *
+ * DESCRIPTION
+ *  Encode data pointed to by src into the buffer pointer to by dest
+ *  using the Base64 algorithm.
+ *
+ *  NOTE: No trailing '\n' character will be added.
+ *
+ *  NOTE: As per specification, '\n' will be placed every 76 chars.
+ *
+ * PARAMETERS
+ *  src = Pointer to the raw data to base64 encode.
+ *  src_bytes = The number of bytes in the src buffer to encode.
+ *  dest = Pointer to the destination buffer where the converted data
+ *     will reside when complete.
+ *  dest_bytes = Initially holds the size of the destination buffer
+ *     but at completion holds the number of bytes converted.
+ *
+ * RETURN VALUE
+ *  base64_success if the buffer was successfully converted, the
+ *  appropriate error code otherwise.
+ *
+ *  The dest parameter holds the converted data.
+ *
+ *  The dest_bytes parameter holds the actual number of bytes converted.
+ */
+base64_result_t base64_encode(unsigned char *src, int src_bytes, unsigned char *dest, int *dest_bytes)
+{
+    int i, j=0;
+    int line_count = 0;
+    unsigned char index; /* index into base64 lookup table */
+    int smax = src_bytes-2; /* only do full multiples of 3 */
+    int dmax = *dest_bytes; /* destination maximum */
+
+    *dest_bytes = 0;
+
+    /* Do full groups. Base64 must be done in blocks of 3 src bytes */
+    for (i=0; i<smax; i+=3) {
+       /* Check to see if newline should be injected */
+       if (line_count>=MAX_BASE64_LINE_LENGTH) {
+           if (j<dmax){
+               dest[j++] = '\n';
+           } else {
+               return BASE64_BUFFER_OVERRUN;
+           }
+           line_count = 0;
+       }
+
+       line_count += 4;
+
+       if ((j+3) < dmax) {
+
+           /* Find mapping of upper 6 bits */
+           index = (src[i] >> 2) & 0x3F;
+           dest[j++] = raw_to_base64_table[index];
+
+           /* bottom 2 bits of first word, high 4 bits of second word */
+           index = ((src[i] << 4) & 0x30) | ((src[i+1] >> 4) & 0x0F);
+           dest[j++] = raw_to_base64_table[index];
+
+           /* bottom 4 bits of second word, high 2 bits of third word */
+           index = ((src[i+1] << 2) & 0x3C) | ((src[i+2] >> 6) & 0x03);
+           dest[j++] = raw_to_base64_table[index];
+
+           /* bottom 6 bits of third word */
+           index = src[i+2] & 0x3F;
+           dest[j++] = raw_to_base64_table[index];
+       } else {
+           return BASE64_BUFFER_OVERRUN;
+       }
+    }
+
+    /* Check to see if any more work must be done */
+    if (i<src_bytes) {
+
+       /* Check to see if a newline should be output */
+       if (line_count>=MAX_BASE64_LINE_LENGTH) {
+           if (j<dmax){
+               dest[j++] = '\n';
+           } else {
+               return BASE64_BUFFER_OVERRUN;
+           }
+           line_count = 0;
+       }
+
+       line_count += 4;
+
+       /* Must fill another quantum */
+       if (j+4>dmax) {
+           /* No room left in output buffer! */
+           return BASE64_BUFFER_OVERRUN;
+       }
+
+       /* Find mapping of upper 6 bits */
+       index = (src[i] >> 2) & 0x3F;
+       dest[j++] = raw_to_base64_table[index];
+
+       /* check for another stragler */
+       if ((i+1)<src_bytes) {
+           /* bottom 2 bits of first word, high 4 bits of second word */
+           index = ((src[i] << 4) & 0x30) | ((src[i+1] >> 4) & 0x0F);
+           dest[j++] = raw_to_base64_table[index];
+
+           /* bottom 4 bits of second word */
+           index = (src[i+1] << 2) & 0x3C;
+           dest[j++] = raw_to_base64_table[index];
+           dest[j++] = PAD_CHAR;
+       } else {
+           /* bottom 2 bits of first word */
+           index = (src[i] << 4) & 0x30;
+           dest[j++] = raw_to_base64_table[index];
+           dest[j++] = PAD_CHAR;
+           dest[j++] = PAD_CHAR;
+       }
+    }
+
+    *dest_bytes = j;
+
+    return BASE64_SUCCESS;
+}
+
+/*
+ * base64_decode
+ *
+ * DESCRIPTION
+ *  Decode data pointed to by src into the buffer pointer to by dest
+ *  using the Base64 algorithm.
+ *
+ * PARAMETERS
+ *  src = Pointer to the Base64 data to decode.
+ *  src_bytes = The number of bytes in the src buffer to decode.
+ *  dest = Pointer to the destination buffer where the converted data
+ *     will reside when complete.
+ *  dest_bytes = Initially holds the size of the destination buffer
+ *     but at completion holds the number of bytes converted.
+ *
+ * RETURN VALUE
+ *  base64_success if the buffer was successfully converted, the
+ *  appropriate error code otherwise.
+ *
+ *  The dest parameter holds the converted data.
+ *
+ *  The dest_bytes parameter holds the actual number of bytes converted.
+ */
+base64_result_t base64_decode(unsigned char *src, int src_bytes, unsigned char *dest, int *dest_bytes)
+{
+    int i, j = 0;
+    int sindex = 0;                    /* Current NON-whitespace source
+                                        * index */
+    int pad_count=0;                   /* Number of padding characters
+                                        * encountered */
+    int dest_size_bytes = *dest_bytes; /* Save size of destination buffer */
+    unsigned char cindex;              /* The current Base64 character to
+                                        * process */
+    unsigned char val;                 /* The value of the current Base64
+                                        * character */
+
+    *dest_bytes = 0;
+
+    for (i=0; i<src_bytes; i++) {
+       cindex = src[i];
+
+       if ((cindex & 0x80) || /* only have 128 values, MSB must not be set! */
+           ((val = base64_to_raw_table[cindex]) == INVALID_CHAR)) {
+           /* Invalid base64 character */
+           return BASE64_BAD_DATA;
+       }
+
+       if (val == WHITE_SPACE) {
+           /* Ignore white space */
+           continue;
+       }
+
+       if (val == PADDING) {
+           /* we must be at the end-finish up */
+           pad_count++;
+           if (++i<src_bytes) {
+               /* can have up to 2 pad chars */
+               if (base64_to_raw_table[src[i]] != PADDING) {
+                   return BASE64_BAD_PADDING;
+               }
+
+               if (++i<src_bytes) {
+                   /* should not have any more padding! */
+                   return BASE64_BAD_PADDING;
+               }
+
+               pad_count++;
+           }
+
+           /* DONE! */
+           break;
+       }
+
+       /* Determine which portion of the 3 bytes this data will fill */
+       switch (sindex & 0x3) {
+       case 0:
+           /* Fill upper 6 bits */
+           if (j<dest_size_bytes) {
+               dest[j] = val << 2;
+           } else {
+               return BASE64_BUFFER_OVERRUN;
+           }
+           break;
+       case 1:
+           /* Fill Bottom 2 bits */
+           dest[j++] |= val >> 4;
+
+           if (j<dest_size_bytes) {
+               /* Fill Top 4 bits */
+               dest[j] = (val << 4) & 0xF0;
+           } else {
+               /*
+                * Check to see if there is any more data present.
+                * Next base64 character MUST be a pad character and
+                * the rest of this data MUST be zero.
+                *
+                * If this is not the end of data then a buffer overrun
+                * has occurred
+                */
+               if ((val & 0x0F) ||
+                   (i+1>=src_bytes) ||
+                   (base64_to_raw_table[src[i+1]] != PADDING)) {
+                   return BASE64_BUFFER_OVERRUN;
+               }
+           }
+           break;
+       case 2:
+           /* Fill Bottom 4 bits */
+           dest[j++] |= val >> 2;
+
+           if (j<dest_size_bytes) {
+               /* Fill Top 2 bits */
+               dest[j] = (val << 6) & 0xC0;
+           } else {
+               /*
+                * Check to see if there is any more data present.
+                * Next base64 character MUST be a pad character and
+                * the rest of this data MUST be zero.
+                *
+                * If this is not the end of data then a buffer overrun
+                * has occurred
+                */
+               if ((val & 0x03) ||
+                   (i+1>=src_bytes) ||
+                   (base64_to_raw_table[src[i+1]] != PADDING)) {
+                   return BASE64_BUFFER_OVERRUN;
+               }
+           }
+           break;
+       case 3:
+           /*
+            *  No need to check for overrun here since the
+            *  previous case was already checked. If another
+            *  group is present then case 0 will check again.
+            */
+
+           /* Fill Bottom 6 bits */
+           dest[j++] |= val;
+           break;
+       }
+       sindex++;
+    }
+
+    /* Check length for multiple of 3 bytes */
+    if (((j + pad_count)% 3) != 0) {
+       return BASE64_BAD_BLOCK_SIZE;
+    }
+
+    /* Save off the number of bytes converted */
+    *dest_bytes = j;
+
+    return BASE64_SUCCESS;
+}
diff --git a/libs/sipcc/core/sdp/sdp_base64.h b/libs/sipcc/core/sdp/sdp_base64.h
new file mode 100644 (file)
index 0000000..e264245
--- /dev/null
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDP_BASE64_H_
+#define _SDP_BASE64_H_
+
+/*
+ * base64_result_t
+ *  Enumeration of the result codes for Base64 conversion.
+ */
+typedef enum base64_result_t_ {
+    BASE64_INVALID=-1,
+    BASE64_SUCCESS=0,
+    BASE64_BUFFER_OVERRUN,
+    BASE64_BAD_DATA,
+    BASE64_BAD_PADDING,
+    BASE64_BAD_BLOCK_SIZE,
+    BASE64_RESULT_MAX
+} base64_result_t;
+
+#define MAX_BASE64_STRING_LEN 60
+
+/* Result code string table */
+extern char *base64_result_table[];
+
+/*
+ * BASE64_RESULT_TO_STRING
+ *  Macro to convert a Base64 result code into a human readable string.
+ */
+#define BASE64_RESULT_TO_STRING(_result) (((_result)>=0 && (_result)<BASE64_RESULT_MAX)?(base64_result_table[_result]):("UNKNOWN Result Code"))
+
+/* Prototypes */
+
+int base64_est_encode_size_bytes(int raw_size_bytes);
+int base64_est_decode_size_bytes(int base64_size_bytes);
+
+base64_result_t base64_encode(unsigned char *src, int src_bytes, unsigned char *dest, int *dest_bytes);
+
+base64_result_t base64_decode(unsigned char *src, int src_bytes, unsigned char *dest, int *dest_bytes);
+
+#endif /* _SDP_BASE64_H_ */
diff --git a/libs/sipcc/core/sdp/sdp_config.c b/libs/sipcc/core/sdp/sdp_config.c
new file mode 100644 (file)
index 0000000..964fe86
--- /dev/null
@@ -0,0 +1,299 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp_os_defs.h"
+#include "sdp.h"
+#include "sdp_private.h"
+#include "CSFLog.h"
+
+static const char* logTag = "sdp_config";
+
+/* Function:    sdp_verify_conf_ptr
+ * Description: Verify the configuration pointer is valid by checking for
+ *              the SDP magic number.  If not valid, display an error.
+ *              Note that we can't keep a statistic of this because we
+ *              track stats inside the config structure.
+ * Parameters: conf_p    The configuration structure handle.
+ *              str       String containing function name to print.
+ * Returns:    TRUE or FALSE.
+ */
+tinybool sdp_verify_conf_ptr (sdp_conf_options_t *conf_p)
+{
+    if ((conf_p != NULL) && (conf_p->magic_num == SDP_MAGIC_NUM)) {
+        return (TRUE);
+    } else {
+        CSFLogError(logTag, "SDP: Invalid Config pointer.");
+        return (FALSE);
+    }
+}
+
+/* Function:    void *sdp_init_config()
+ * Description: Initialize SDP configuration structure with the
+ *              following defaults:
+ *              All debug levels turned OFF.
+ *              All token lines required per RFC2327.
+ *              No media types supported.
+ *              No network types supported.
+ *              No address types supported.
+ *              No transport types supported.
+ * Parameters: None.
+ * Returns:    A handle for the configuration as a void ptr.
+ */
+sdp_conf_options_t sdp_config_options;
+void *sdp_init_config ()
+{
+    int i;
+    sdp_conf_options_t *conf_p;
+
+    conf_p = & sdp_config_options;
+
+    /* Initialize magic number. */
+    conf_p->magic_num = SDP_MAGIC_NUM;
+
+    /* Set default debug flags. */
+    conf_p->debug_flag[SDP_DEBUG_TRACE]    = FALSE;
+    conf_p->debug_flag[SDP_DEBUG_WARNINGS] = FALSE;
+    conf_p->debug_flag[SDP_DEBUG_ERRORS]   = FALSE;
+
+    /* Set required lines flags.  Note: Only need to set those that */
+    /* are questionable.  Most lines aren't required by default.    */
+    conf_p->version_reqd       = TRUE;
+    conf_p->owner_reqd         = TRUE;
+    conf_p->session_name_reqd  = TRUE;
+    conf_p->timespec_reqd      = TRUE;
+
+    /* No media types supported by default. */
+    for (i=0; i < SDP_MAX_MEDIA_TYPES; i++) {
+        conf_p->media_supported[i] = FALSE;
+    }
+
+    /* No network types supported by default. */
+    for (i=0; i < SDP_MAX_NETWORK_TYPES; i++) {
+        conf_p->nettype_supported[i] = FALSE;
+    }
+
+    /* No address types supported by default. */
+    for (i=0; i < SDP_MAX_ADDR_TYPES; i++) {
+        conf_p->addrtype_supported[i] = FALSE;
+    }
+
+    /* No transport types supported by default. */
+    for (i=0; i < SDP_MAX_TRANSPORT_TYPES; i++) {
+        conf_p->transport_supported[i] = FALSE;
+    }
+
+    /* No choose parameters allowed by default. */
+    for (i=0; i < SDP_MAX_CHOOSE_PARAMS; i++) {
+        conf_p->allow_choose[i] = FALSE;
+    }
+
+    /* Initialize statistics counts */
+    conf_p->num_parses              = 0;
+    conf_p->num_builds              = 0;
+    conf_p->num_not_sdp_desc        = 0;
+    conf_p->num_invalid_token_order = 0;
+    conf_p->num_invalid_param       = 0;
+    conf_p->num_no_resource         = 0;
+
+    return (conf_p);
+}
+
+
+/* Function:    void sdp_appl_debug(void *config_p, sdp_debug_e debug_type,
+ *                                  tinybool my_bool);
+ * Description:        Define the default type of debug for the application.
+ *              Valid debug types are ERRORS, WARNINGS, and TRACE.  Each
+ *              debug type can be turned on/off individually.  The
+ *              default debug level can be redefined at any time.
+ * Parameters: conf_p     The config handle returned by sdp_init_config.
+ *              debug_type Specifies the debug type being enabled/disabled.
+ *              debug_flag  Defines whether the debug should be enabled or not.
+ * Returns:    Nothing.
+ */
+void sdp_appl_debug (void *config_p, sdp_debug_e debug_type,
+                     tinybool debug_flag)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    if (debug_type < SDP_MAX_DEBUG_TYPES)  {
+        conf_p->debug_flag[debug_type] = debug_flag;
+    }
+}
+
+
+/* Functions:  void sdp_require_version
+ *              void sdp_require_owner
+ *              void sdp_require_session_name
+ *              void sdp_require_timespec
+ * Description:        These functions allow the application to not require several
+ *              of the tokens that are specifically required by RFC 2327.
+ * Parameters: conf_p    The config handle returned by sdp_init_config.
+ *              version_required   TRUE or FALSE whether the token should
+ *              be required.
+ * Returns:    Nothing.
+ */
+void sdp_require_version (void *config_p, tinybool version_required)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    conf_p->version_reqd = version_required;
+}
+
+void sdp_require_owner (void *config_p, tinybool owner_required)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    conf_p->owner_reqd = owner_required;
+}
+
+void sdp_require_session_name (void *config_p, tinybool sess_name_required)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    conf_p->session_name_reqd = sess_name_required;
+}
+
+void sdp_require_timespec (void *config_p, tinybool timespec_required)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    conf_p->timespec_reqd = timespec_required;
+}
+
+
+/* Function:    sdp_media_supported
+ * Description: These functions allow the application to specify which
+ *              media types it supports. The application must set any/all
+ *              as required.  No media types are supported by default.
+ * Parameters:  config_p    The config handle returned by sdp_init_config.
+ *              nettype     The network type for which support is being set.
+ *              media_supported TRUE or FALSE whether the support is provided.
+ * Returns:     Nothing.
+ */
+void sdp_media_supported (void *config_p, sdp_media_e media_type,
+                        tinybool media_supported)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    conf_p->media_supported[media_type] = media_supported;
+}
+
+
+/* Function:    sdp_nettype_supported
+ * Description: This function allows the application to specify which
+ *              network types it supports.  The application must set
+ *              any/all as required.  No network types are supported by
+ *              default.
+ * Parameters:  config_p    The config handle returned by sdp_init_config.
+ *              nettype     The network type for which support is being set.
+ *              nettype_supported TRUE or FALSE whether the support is
+ *                          provided.
+ * Returns:     Nothing.
+ */
+void sdp_nettype_supported (void *config_p, sdp_nettype_e nettype,
+                           tinybool nettype_supported)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    conf_p->nettype_supported[nettype] = nettype_supported;
+}
+
+
+/* Function:    sdp_addrtype_supported
+ * Description: This function allows the application to specify which
+ *              address types it supports.  The application must set
+ *              any/all as required.  No address types are supported by
+ *              default.
+ * Parameters:  config_p    The config handle returned by sdp_init_config.
+ *              addrtype    The address type for which support is being set.
+ *              addrtype_supported TRUE or FALSE whether the support is
+ *                          provided.
+ * Returns:     Nothing.
+ */
+void sdp_addrtype_supported (void *config_p, sdp_addrtype_e addrtype,
+                            tinybool addrtype_supported)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    conf_p->addrtype_supported[addrtype] = addrtype_supported;
+}
+
+
+/* Function:    sdp_transport_supported
+ * Description: This function allows the application to specify which
+ *              transport types it supports.  The application must set
+ *              any/all as required.  No transport types are supported
+ *              by default.
+ * Parameters:  config_p    The config handle returned by sdp_init_config.
+ *              transport   The transport type for which support is being set.
+ *              transport_supported  TRUE or FALSE whether the support is
+ *                          provided.
+ * Returns:     Nothing.
+ */
+void sdp_transport_supported (void *config_p, sdp_transport_e transport,
+                             tinybool transport_supported)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    conf_p->transport_supported[transport] = transport_supported;
+}
+
+
+/* Function:    sdp_allow_choose
+ * Description: These functions allow the CHOOSE parameter `$' to be
+ *              specified in place of certain parameters.
+ * Parameters:  config_p        The config handle returned by sdp_init_config.
+ *              param           The param that may or may not be CHOOSE.
+ *              choose_allowed  TRUE or FALSE whether the CHOOSE parameter
+ *                              should be allowed.
+ * Returns:     Nothing.
+ */
+void sdp_allow_choose (void *config_p, sdp_choose_param_e param, tinybool choose_allowed)
+{
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return;
+    }
+
+    if (param < SDP_MAX_CHOOSE_PARAMS) {
+        conf_p->allow_choose[param] = choose_allowed;
+    }
+}
diff --git a/libs/sipcc/core/sdp/sdp_main.c b/libs/sipcc/core/sdp/sdp_main.c
new file mode 100644 (file)
index 0000000..c11c7c8
--- /dev/null
@@ -0,0 +1,1463 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp_os_defs.h"
+#include "sdp.h"
+#include "sdp_private.h"
+#include "CSFLog.h"
+
+static const char* logTag = "sdp_main";
+
+/* Note: These *must* be in the same order as the enum types. */
+const sdp_tokenarray_t sdp_token[SDP_MAX_TOKENS] =
+{
+    {"v=", sdp_parse_version,      sdp_build_version },
+    {"o=", sdp_parse_owner,        sdp_build_owner },
+    {"s=", sdp_parse_sessname,     sdp_build_sessname },
+    {"i=", sdp_parse_sessinfo,     sdp_build_sessinfo },
+    {"u=", sdp_parse_uri,          sdp_build_uri },
+    {"e=", sdp_parse_email,        sdp_build_email },
+    {"p=", sdp_parse_phonenum,     sdp_build_phonenum },
+    {"c=", sdp_parse_connection,   sdp_build_connection },
+    {"b=", sdp_parse_bandwidth,    sdp_build_bandwidth },
+    {"t=", sdp_parse_timespec,     sdp_build_timespec },
+    {"r=", sdp_parse_repeat_time,  sdp_build_repeat_time },
+    {"z=", sdp_parse_timezone_adj, sdp_build_timezone_adj },
+    {"k=", sdp_parse_encryption,   sdp_build_encryption },
+    {"a=", sdp_parse_attribute,    sdp_build_attribute },
+    {"m=", sdp_parse_media,        sdp_build_media }
+};
+
+
+/* Note: These *must* be in the same order as the enum types. */
+const sdp_attrarray_t sdp_attr[SDP_MAX_ATTR_TYPES] =
+{
+    {"bearer", sizeof("bearer"),
+     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
+    {"called", sizeof("called"),
+     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
+    {"connection_type", sizeof("connection_type"),
+     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
+    {"dialed", sizeof("dialed"),
+     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
+    {"dialing", sizeof("dialing"),
+     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
+    {"direction", sizeof("direction"),
+     sdp_parse_attr_comediadir, sdp_build_attr_comediadir },
+    {"eecid", sizeof("eecid"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"fmtp", sizeof("fmtp"),
+     sdp_parse_attr_fmtp, sdp_build_attr_fmtp },
+    {"framing", sizeof("framing"),
+     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
+    {"inactive", sizeof("inactive"),
+     sdp_parse_attr_direction, sdp_build_attr_direction },
+    {"ptime", sizeof("ptime"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"qos", sizeof("qos"),
+     sdp_parse_attr_qos, sdp_build_attr_qos },
+    {"curr", sizeof("curr"),
+     sdp_parse_attr_curr, sdp_build_attr_curr },
+    {"des", sizeof("des"),
+     sdp_parse_attr_des, sdp_build_attr_des},
+    {"conf", sizeof("conf"),
+     sdp_parse_attr_conf, sdp_build_attr_conf},
+    {"recvonly", sizeof("recvonly"),
+     sdp_parse_attr_direction, sdp_build_attr_direction },
+    {"rtpmap", sizeof("rtpmap"),
+     sdp_parse_attr_transport_map, sdp_build_attr_transport_map },
+    {"secure", sizeof("secure"),
+     sdp_parse_attr_qos, sdp_build_attr_qos },
+    {"sendonly", sizeof("sendonly"),
+     sdp_parse_attr_direction, sdp_build_attr_direction },
+    {"sendrecv", sizeof("sendrecv"),
+     sdp_parse_attr_direction, sdp_build_attr_direction },
+    {"subnet", sizeof("subnet"),
+     sdp_parse_attr_subnet, sdp_build_attr_subnet },
+    {"T38FaxVersion", sizeof("T38FaxVersion"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"T38MaxBitRate", sizeof("T38MaxBitRate"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"T38FaxFillBitRemoval", sizeof("T38FaxFillBitRemoval"),
+     sdp_parse_attr_simple_bool, sdp_build_attr_simple_bool },
+    {"T38FaxTranscodingMMR", sizeof("T38FaxTranscodingMMR"),
+     sdp_parse_attr_simple_bool, sdp_build_attr_simple_bool },
+    {"T38FaxTranscodingJBIG", sizeof("T38FaxTranscodingJBIG"),
+     sdp_parse_attr_simple_bool, sdp_build_attr_simple_bool },
+    {"T38FaxRateManagement", sizeof("T38FaxRateManagement"),
+     sdp_parse_attr_t38_ratemgmt, sdp_build_attr_t38_ratemgmt },
+    {"T38FaxMaxBuffer", sizeof("T38FaxMaxBuffer"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"T38FaxMaxDatagram", sizeof("T38FaxMaxDatagram"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"T38FaxUdpEC", sizeof("T38FaxUdpEC"),
+     sdp_parse_attr_t38_udpec, sdp_build_attr_t38_udpec },
+    {"X-cap", sizeof("X-cap"),
+     sdp_parse_attr_cap, sdp_build_attr_cap },
+    {"X-cpar", sizeof("X-cpar"),
+     sdp_parse_attr_cpar, sdp_build_attr_cpar },
+    {"X-pc-codec", sizeof("X-pc-codec"),
+     sdp_parse_attr_pc_codec, sdp_build_attr_pc_codec },
+    {"X-pc-qos", sizeof("X-pc-qos"),
+     sdp_parse_attr_qos, sdp_build_attr_qos },
+    {"X-qos", sizeof("X-qos"),
+     sdp_parse_attr_qos, sdp_build_attr_qos },
+    {"X-sqn", sizeof("X-sqn"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"TMRGwXid", sizeof("TMRGwXid"),
+     sdp_parse_attr_simple_bool, sdp_build_attr_simple_bool },
+    {"TC1PayloadBytes", sizeof("TC1PayloadBytes"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"TC1WindowSize", sizeof("TC1WindowSize"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"TC2PayloadBytes", sizeof("TC2PayloadBytes"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"TC2WindowSize", sizeof("TC2WindowSize"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"rtcp", sizeof("rtcp"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"rtr", sizeof("rtr"),
+     sdp_parse_attr_rtr, sdp_build_attr_rtr},
+    {"silenceSupp", sizeof("silenceSupp"),
+     sdp_parse_attr_silencesupp, sdp_build_attr_silencesupp },
+    {"X-crypto", sizeof("X-crypto"),
+     sdp_parse_attr_srtpcontext, sdp_build_attr_srtpcontext },
+    {"mptime", sizeof("mptime"),
+      sdp_parse_attr_mptime, sdp_build_attr_mptime },
+    {"X-sidin", sizeof("X-sidin"),
+      sdp_parse_attr_x_sidin, sdp_build_attr_x_sidin },
+    {"X-sidout", sizeof("X-sidout"),
+      sdp_parse_attr_x_sidout, sdp_build_attr_x_sidout },
+    {"X-confid", sizeof("X-confid"),
+      sdp_parse_attr_x_confid, sdp_build_attr_x_confid },
+    {"group", sizeof("group"),
+      sdp_parse_attr_group, sdp_build_attr_group },
+    {"mid", sizeof("mid"),
+      sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"source-filter", sizeof("source-filter"),
+      sdp_parse_attr_source_filter, sdp_build_source_filter},
+    {"rtcp-unicast", sizeof("rtcp-unicast"),
+      sdp_parse_attr_rtcp_unicast, sdp_build_attr_rtcp_unicast},
+    {"maxprate", sizeof("maxprate"),
+      sdp_parse_attr_maxprate, sdp_build_attr_simple_string},
+    {"sqn", sizeof("sqn"),
+     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"cdsc", sizeof("cdsc"),
+     sdp_parse_attr_cap, sdp_build_attr_cap },
+    {"cpar", sizeof("cpar"),
+     sdp_parse_attr_cpar, sdp_build_attr_cpar },
+    {"sprtmap", sizeof("sprtmap"),
+     sdp_parse_attr_transport_map, sdp_build_attr_transport_map },
+    {"crypto", sizeof("crypto"),
+     sdp_parse_attr_sdescriptions, sdp_build_attr_sdescriptions },
+    {"label", sizeof("label"),
+      sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
+    {"framerate", sizeof("framerate"),
+      sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
+    {"candidate", sizeof("candidate"),
+      sdp_parse_attr_ice_attr, sdp_build_attr_ice_attr },
+    {"ice-ufrag", sizeof("ice-ufrag"),
+      sdp_parse_attr_ice_attr, sdp_build_attr_ice_attr },
+    {"ice-pwd", sizeof("ice-pwd"),
+      sdp_parse_attr_ice_attr, sdp_build_attr_ice_attr},
+    {"rtcp-mux", sizeof("rtcp-mux"),
+      sdp_parse_attr_rtcp_mux_attr, sdp_build_attr_rtcp_mux_attr},
+    {"fingerprint", sizeof("fingerprint"),
+      sdp_parse_attr_fingerprint_attr, sdp_build_attr_simple_string},
+    {"maxptime", sizeof("maxptime"),
+      sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32}
+};
+
+/* Note: These *must* be in the same order as the enum types. */
+const sdp_namearray_t sdp_media[SDP_MAX_MEDIA_TYPES] =
+{
+    {"audio",        sizeof("audio")},
+    {"video",        sizeof("video")},
+    {"application",  sizeof("application")},
+    {"data",         sizeof("data")},
+    {"control",      sizeof("control")},
+    {"nas/radius",   sizeof("nas/radius")},
+    {"nas/tacacs",   sizeof("nas/tacacs")},
+    {"nas/diameter", sizeof("nas/diameter")},
+    {"nas/l2tp",     sizeof("nas/l2tp")},
+    {"nas/login",    sizeof("nas/login")},
+    {"nas/none",     sizeof("nas/none")},
+    {"image",        sizeof("image")},
+    {"text",         sizeof("text")}
+};
+
+
+/* Note: These *must* be in the same order as the enum types. */
+const sdp_namearray_t sdp_nettype[SDP_MAX_NETWORK_TYPES] =
+{
+    {"IN",           sizeof("IN")},
+    {"ATM",          sizeof("ATM")},
+    {"FR",           sizeof("FR")},
+    {"LOCAL",        sizeof("LOCAL")}
+};
+
+
+/* Note: These *must* be in the same order as the enum types. */
+const sdp_namearray_t sdp_addrtype[SDP_MAX_ADDR_TYPES] =
+{
+    {"IP4",          sizeof("IP4")},
+    {"IP6",          sizeof("IP6")},
+    {"NSAP",         sizeof("NSAP")},
+    {"EPN",          sizeof("EPN")},
+    {"E164",         sizeof("E164")},
+    {"GWID",         sizeof("GWID")}
+};
+
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_transport[SDP_MAX_TRANSPORT_TYPES] =
+{
+    {"RTP/AVP",      sizeof("RTP/AVP")},
+    {"udp",          sizeof("udp")},
+    {"udptl",        sizeof("udptl")},
+    {"ces10",        sizeof("ces10")},
+    {"LOCAL",        sizeof("LOCAL")},
+    {"AAL2/ITU",     sizeof("AAL2/ITU")},
+    {"AAL2/ATMF",    sizeof("AAL2/ATMF")},
+    {"AAL2/custom",  sizeof("AAL2/custom")},
+    {"AAL1/AVP",     sizeof("AAL1/AVP")},
+    {"udpsprt",      sizeof("udpsprt")},
+    {"RTP/SAVP",     sizeof("RTP/SAVP")},
+    {"tcp",          sizeof("tcp")},
+    {"RTP/SAVPF",    sizeof("RTP/SAVPF")},
+    {"SCTP/DTLS",    sizeof("SCTP/DTLS")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_encrypt[SDP_MAX_ENCRYPT_TYPES] =
+{
+    {"clear",        sizeof("clear")},
+    {"base64",       sizeof("base64")},
+    {"uri",          sizeof("uri")},
+    {"prompt",       sizeof("prompt")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_payload[SDP_MAX_STRING_PAYLOAD_TYPES] =
+{
+    {"t38",          sizeof("t38")},
+    {"X-tmr",        sizeof("X-tmr")},
+    {"T120",         sizeof("T120")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_t38_rate[SDP_T38_MAX_RATES] =
+{
+    {"localTCF",        sizeof("localTCF")},
+    {"transferredTCF",  sizeof("transferredTCF")},
+    {"unknown",         sizeof("unknown")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_t38_udpec[SDP_T38_MAX_UDPEC] =
+{
+    {"t38UDPRedundancy",  sizeof("t38UDPRedundancy")},
+    {"t38UDPFEC",         sizeof("t38UDPFEC")},
+    {"unknown",           sizeof("unknown")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_qos_strength[SDP_MAX_QOS_STRENGTH] =
+{
+    {"optional",          sizeof("optional")},
+    {"mandatory",         sizeof("mandatory")},
+    {"success",           sizeof("success")},
+    {"failure",           sizeof("failure")},
+    {"none",              sizeof("none")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_qos_status_type[SDP_MAX_QOS_STATUS_TYPES] =
+{
+    {"local",          sizeof("local")},
+    {"remote",         sizeof("remote")},
+    {"e2e",            sizeof("e2e")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_curr_type[SDP_MAX_CURR_TYPES] =
+{
+    {"qos",            sizeof("qos")},
+    {"unknown",         sizeof("unknown")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_des_type[SDP_MAX_DES_TYPES] =
+{
+    {"qos",            sizeof("qos")},
+    {"unknown",         sizeof("unknown")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_conf_type[SDP_MAX_CONF_TYPES] =
+{
+    {"qos",            sizeof("qos")},
+    {"unknown",         sizeof("unknown")}
+};
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_qos_direction[SDP_MAX_QOS_DIR] =
+{
+    {"send",              sizeof("send")},
+    {"recv",              sizeof("recv")},
+    {"sendrecv",          sizeof("sendrecv")},
+    {"none",              sizeof("none")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_silencesupp_pref[SDP_MAX_SILENCESUPP_PREF] = {
+    {"standard",          sizeof("standard")},
+    {"custom",            sizeof("custom")},
+    {"-",                 sizeof("-")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_silencesupp_siduse[SDP_MAX_SILENCESUPP_SIDUSE] = {
+    {"No SID",            sizeof("No SID")},
+    {"Fixed Noise",       sizeof("Fixed Noise")},
+    {"Sampled Noise",     sizeof("Sampled Noise")},
+    {"-",                 sizeof("-")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_mediadir_role[SDP_MAX_MEDIADIR_ROLES] =
+{
+    {"passive",       sizeof("passive")},
+    {"active",        sizeof("active")},
+    {"both",          sizeof("both")},
+    {"reuse",         sizeof("reuse")},
+    {"unknown",       sizeof("unknown")}
+};
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_fmtp_codec_param[SDP_MAX_FMTP_PARAM] =
+{
+    {"annexa",              sizeof("annexa")}, /* 0 */
+    {"annexb",              sizeof("annexb")}, /* 1 */
+    {"bitrate",             sizeof("bitrate")}, /* 2 */
+    {"QCIF",                sizeof("QCIF")}, /* 3 */
+    {"CIF",                 sizeof("CIF")},  /* 4 */
+    {"MAXBR",               sizeof("MAXBR")}, /* 5 */
+    {"SQCIF",               sizeof("SQCIF")}, /* 6 */
+    {"CIF4",                sizeof("CIF4")}, /* 7 */
+    {"CIF16",               sizeof("CIF16")}, /* 8 */
+    {"CUSTOM",              sizeof("CUSTOM")}, /* 9 */
+    {"PAR",                 sizeof("PAR")}, /* 10 */
+    {"CPCF",                sizeof("CPCF")}, /* 11 */
+    {"BPP",                 sizeof("BPP")}, /* 12 */
+    {"HRD",                 sizeof("HRD")}, /* 13 */
+    {"PROFILE",             sizeof("PROFILE")}, /* 14 */
+    {"LEVEL",               sizeof("LEVEL")}, /* 15 */
+    {"INTERLACE",           sizeof("INTERLACE")}, /* 16 */
+
+    /* H.264 related */
+    {"profile-level-id",      sizeof("profile-level-id")}, /* 17 */
+    {"sprop-parameter-sets",  sizeof("sprop-parameter-sets")}, /* 18 */
+    {"packetization-mode",    sizeof("packetization-mode")}, /* 19 */
+    {"sprop-interleaving-depth",    sizeof("sprop-interleaving-depth")}, /* 20 */
+    {"sprop-deint-buf-req",   sizeof("sprop-deint-buf-req")}, /* 21 */
+    {"sprop-max-don-diff",    sizeof("sprop-max-don-diff")}, /* 22 */
+    {"sprop-init-buf-time",   sizeof("sprop-init-buf-time")}, /* 23 */
+
+    {"max-mbps",              sizeof("max-mbps")}, /* 24 */
+    {"max-fs",                sizeof("max-fs")}, /* 25 */
+    {"max-cpb",               sizeof("max-cpb")}, /* 26 */
+    {"max-dpb",               sizeof("max-dpb")}, /* 27 */
+    {"max-br",                sizeof("max-br")}, /* 28 */
+    {"redundant-pic-cap",     sizeof("redundant-pic-cap")}, /* 29 */
+    {"deint-buf-cap",         sizeof("deint-buf-cap")}, /* 30 */
+    {"max-rcmd-nalu-size",    sizeof("max-rcmd_nali-size")}, /* 31 */
+    {"parameter-add",         sizeof("parameter-add")}, /* 32 */
+
+    /* Annexes - require special handling */
+     {"D", sizeof("D")}, /* 33 */
+     {"F", sizeof("F")}, /* 34 */
+     {"I", sizeof("I")}, /* 35 */
+     {"J", sizeof("J")}, /* 36 */
+     {"T", sizeof("T")}, /* 37 */
+     {"K", sizeof("K")}, /* 38 */
+     {"N", sizeof("N")}, /* 39 */
+     {"P", sizeof("P")}, /* 40 */
+
+     {"mode",                sizeof("mode")},  /* 41 */
+    {"level-asymmetry-allowed",         sizeof("level-asymmetry-allowed")}, /* 42 */
+    {"maxaveragebitrate",               sizeof("maxaveragebitrate")}, /* 43 */
+    {"usedtx",                          sizeof("usedtx")}, /* 44 */
+    {"stereo",                          sizeof("stereo")}, /* 45 */
+    {"useinbandfec",                    sizeof("useinbandfec")}, /* 46 */
+    {"maxcodedaudiobandwidth",          sizeof("maxcodedaudiobandwidth")}, /* 47 */
+    {"cbr",                             sizeof("cbr")}, /* 48 */
+    {"streams",                         sizeof("streams")}, /* 49 */
+    {"protocol",                        sizeof("protocol")} /* 50 */
+
+} ;
+
+/* Note: These *must* be in the same order as the enum type. */
+const sdp_namearray_t sdp_fmtp_codec_param_val[SDP_MAX_FMTP_PARAM_VAL] =
+{
+    {"yes",                 sizeof("yes")},
+    {"no",                  sizeof("no")}
+};
+
+const sdp_namearray_t sdp_bw_modifier_val[SDP_MAX_BW_MODIFIER_VAL] =
+{
+    {"AS",                  sizeof("AS")},
+    {"CT",                  sizeof("CT")},
+    {"TIAS",                sizeof("TIAS")}
+};
+
+const sdp_namearray_t sdp_group_attr_val[SDP_MAX_GROUP_ATTR_VAL] =
+{
+    {"FID",                 sizeof("FID")},
+    {"LS",                  sizeof("LS")},
+    {"ANAT",                sizeof("ANAT")}
+};
+
+const sdp_namearray_t sdp_srtp_context_crypto_suite[SDP_SRTP_MAX_NUM_CRYPTO_SUITES] =
+{
+  {"UNKNOWN_CRYPTO_SUITE",    sizeof("UNKNOWN_CRYPTO_SUITE")},
+  {"AES_CM_128_HMAC_SHA1_32", sizeof("AES_CM_128_HMAC_SHA1_32")},
+  {"AES_CM_128_HMAC_SHA1_80", sizeof("AES_CM_128_HMAC_SHA1_80")},
+  {"F8_128_HMAC_SHA1_80", sizeof("F8_128_HMAC_SHA1_80")}
+};
+
+/* Maintain the same order as defined in typedef sdp_src_filter_mode_e */
+const sdp_namearray_t sdp_src_filter_mode_val[SDP_MAX_FILTER_MODE] =
+{
+    {"incl", sizeof("incl")},
+    {"excl", sizeof("excl")}
+};
+
+/* Maintain the same order as defined in typdef sdp_rtcp_unicast_mode_e */
+const sdp_namearray_t sdp_rtcp_unicast_mode_val[SDP_RTCP_MAX_UNICAST_MODE] =
+{
+    {"reflection", sizeof("reflection")},
+    {"rsi",        sizeof("rsi")}
+};
+
+/*  Maintain same order as defined in typedef sdp_srtp_crypto_suite_t */
+const sdp_srtp_crypto_suite_list sdp_srtp_crypto_suite_array[SDP_SRTP_MAX_NUM_CRYPTO_SUITES] =
+{
+  {SDP_SRTP_UNKNOWN_CRYPTO_SUITE, UNKNOWN_CRYPTO_SUITE, 0, 0},
+  {SDP_SRTP_AES_CM_128_HMAC_SHA1_32, AES_CM_128_HMAC_SHA1_32,
+      SDP_SRTP_AES_CM_128_HMAC_SHA1_32_KEY_BYTES,
+      SDP_SRTP_AES_CM_128_HMAC_SHA1_32_SALT_BYTES},
+  {SDP_SRTP_AES_CM_128_HMAC_SHA1_80, AES_CM_128_HMAC_SHA1_80,
+      SDP_SRTP_AES_CM_128_HMAC_SHA1_80_KEY_BYTES,
+      SDP_SRTP_AES_CM_128_HMAC_SHA1_80_SALT_BYTES},
+  {SDP_SRTP_F8_128_HMAC_SHA1_80, F8_128_HMAC_SHA1_80,
+      SDP_SRTP_F8_128_HMAC_SHA1_80_KEY_BYTES,
+      SDP_SRTP_F8_128_HMAC_SHA1_80_SALT_BYTES}
+};
+
+const char* sdp_result_name[SDP_MAX_RC] =
+    {"SDP_SUCCESS",
+     "SDP_FAILURE",
+     "SDP_INVALID_SDP_PTR",
+     "SDP_NOT_SDP_DESCRIPTION",
+     "SDP_INVALID_TOKEN_ORDERING",
+     "SDP_INVALID_PARAMETER",
+     "SDP_INVALID_MEDIA_LEVEL",
+     "SDP_INVALID_CAPABILITY",
+     "SDP_NO_RESOURCE",
+     "SDP_UNRECOGNIZED_TOKEN",
+     "SDP_NULL_BUF_PTR",
+     "SDP_POTENTIAL_SDP_OVERFLOW"};
+
+const char *sdp_get_result_name ( sdp_result_e rc )
+{
+    if (rc >= SDP_MAX_RC) {
+        return ("Invalid SDP result code");
+    } else {
+        return (sdp_result_name[rc]);
+    }
+}
+
+const char *sdp_get_attr_name ( sdp_attr_e attr_type )
+{
+    if (attr_type >= SDP_MAX_ATTR_TYPES) {
+        return ("Invalid attribute type");
+    } else {
+        return (sdp_attr[attr_type].name);
+    }
+}
+
+const char *sdp_get_media_name ( sdp_media_e media_type )
+{
+    if (media_type == SDP_MEDIA_UNSUPPORTED) {
+        return (SDP_UNSUPPORTED);
+    } else if (media_type >= SDP_MAX_MEDIA_TYPES) {
+        return ("Invalid media type");
+    } else {
+        return (sdp_media[media_type].name);
+    }
+}
+
+const char *sdp_get_network_name ( sdp_nettype_e network_type )
+{
+    if (network_type == SDP_NT_UNSUPPORTED) {
+        return (SDP_UNSUPPORTED);
+    } else if (network_type >= SDP_MAX_NETWORK_TYPES) {
+        return ("Invalid network type");
+    } else {
+        return (sdp_nettype[network_type].name);
+    }
+}
+
+const char *sdp_get_address_name ( sdp_addrtype_e addr_type )
+{
+    if (addr_type == SDP_AT_UNSUPPORTED) {
+        return (SDP_UNSUPPORTED);
+    } else if (addr_type >= SDP_MAX_ADDR_TYPES) {
+        if (addr_type == SDP_AT_FQDN) {
+            return ("*");
+        } else {
+            return ("Invalid address type");
+        }
+    } else {
+        return (sdp_addrtype[addr_type].name);
+    }
+}
+
+const char *sdp_get_transport_name ( sdp_transport_e transport_type )
+{
+    if (transport_type == SDP_TRANSPORT_UNSUPPORTED) {
+        return (SDP_UNSUPPORTED);
+    } else if (transport_type >= SDP_MAX_TRANSPORT_TYPES) {
+        return ("Invalid transport type");
+    } else {
+        return (sdp_transport[transport_type].name);
+    }
+}
+
+const char *sdp_get_encrypt_name ( sdp_encrypt_type_e encrypt_type )
+{
+    if (encrypt_type == SDP_ENCRYPT_UNSUPPORTED) {
+        return (SDP_UNSUPPORTED);
+    } else if (encrypt_type >= SDP_MAX_ENCRYPT_TYPES) {
+        return ("Invalid encryption type");
+    } else {
+        return (sdp_encrypt[encrypt_type].name);
+    }
+}
+
+const char *sdp_get_payload_name ( sdp_payload_e payload )
+{
+    if (payload == SDP_PAYLOAD_UNSUPPORTED) {
+        return (SDP_UNSUPPORTED);
+    } else if (payload >= SDP_MAX_STRING_PAYLOAD_TYPES) {
+        return ("Invalid payload type");
+    } else {
+        return (sdp_payload[payload].name);
+    }
+}
+
+const char *sdp_get_t38_ratemgmt_name ( sdp_t38_ratemgmt_e rate )
+{
+    if (rate >= SDP_T38_MAX_RATES) {
+        return ("Invalid rate");
+    } else {
+        return (sdp_t38_rate[rate].name);
+    }
+}
+
+const char *sdp_get_t38_udpec_name ( sdp_t38_udpec_e udpec )
+{
+    if (udpec >= SDP_T38_MAX_UDPEC) {
+        return ("Invalid udpec");
+    } else {
+        return (sdp_t38_udpec[udpec].name);
+    }
+}
+
+const char *sdp_get_qos_strength_name ( sdp_qos_strength_e strength )
+{
+    if (strength >= SDP_MAX_QOS_STRENGTH) {
+        return ("Invalid qos strength");
+    } else {
+        return (sdp_qos_strength[strength].name);
+    }
+}
+
+const char *sdp_get_qos_direction_name ( sdp_qos_dir_e direction )
+{
+    if (direction >= SDP_MAX_QOS_DIR) {
+        return ("Invalid qos direction");
+    } else {
+        return (sdp_qos_direction[direction].name);
+    }
+}
+
+const char *sdp_get_qos_status_type_name ( sdp_qos_status_types_e status_type )
+{
+    if (status_type >= SDP_MAX_QOS_STATUS_TYPES) {
+        return ("Invalid qos status type");
+    } else {
+        return (sdp_qos_status_type[status_type].name);
+    }
+}
+
+const char *sdp_get_curr_type_name (sdp_curr_type_e curr_type )
+{
+    if (curr_type >= SDP_MAX_CURR_TYPES) {
+        return ("Invalid curr type");
+    } else {
+        return (sdp_curr_type[curr_type].name);
+    }
+}
+
+const char *sdp_get_des_type_name (sdp_des_type_e des_type )
+{
+    if (des_type >= SDP_MAX_DES_TYPES) {
+        return ("Invalid des type");
+    } else {
+        return (sdp_des_type[des_type].name);
+    }
+}
+
+const char *sdp_get_conf_type_name (sdp_conf_type_e conf_type )
+{
+    if (conf_type >= SDP_MAX_CONF_TYPES) {
+        return ("Invalid conf type");
+    } else {
+        return (sdp_conf_type[conf_type].name);
+    }
+}
+
+const char *sdp_get_silencesupp_pref_name (sdp_silencesupp_pref_e pref)
+{
+    if (pref >= SDP_MAX_SILENCESUPP_PREF) {
+        return ("Invalid silencesupp pref");
+    } else {
+        return (sdp_silencesupp_pref[pref].name);
+    }
+}
+
+const char *sdp_get_silencesupp_siduse_name (sdp_silencesupp_siduse_e siduse)
+{
+    if (siduse >= SDP_MAX_SILENCESUPP_SIDUSE) {
+        return ("Invalid silencesupp siduse");
+    } else {
+        return (sdp_silencesupp_siduse[siduse].name);
+    }
+}
+
+const char *sdp_get_mediadir_role_name (sdp_mediadir_role_e role)
+{
+    if (role >= SDP_MEDIADIR_ROLE_UNKNOWN) {
+        return ("Invalid media direction role");
+    } else {
+        return (sdp_mediadir_role[role].name);
+    }
+}
+
+
+const char *sdp_get_bw_modifier_name (sdp_bw_modifier_e bw_modifier_type)
+{
+    if (bw_modifier_type == SDP_BW_MODIFIER_UNSUPPORTED) {
+        return (SDP_UNSUPPORTED);
+    } else if (bw_modifier_type < SDP_BW_MODIFIER_AS ||
+            bw_modifier_type >= SDP_MAX_BW_MODIFIER_VAL) {
+        return ("Invalid bw modifier type");
+    } else {
+        return (sdp_bw_modifier_val[bw_modifier_type].name);
+    }
+}
+
+const char *sdp_get_group_attr_name (sdp_group_attr_e group_attr_type)
+{
+    if (group_attr_type == SDP_GROUP_ATTR_UNSUPPORTED) {
+        return (SDP_UNSUPPORTED);
+    } else if (group_attr_type >= SDP_MAX_GROUP_ATTR_VAL) {
+        return ("Invalid a=group: attribute type");
+    } else {
+        return (sdp_group_attr_val[group_attr_type].name);
+    }
+}
+
+const char *sdp_get_src_filter_mode_name (sdp_src_filter_mode_e type)
+{
+    if (type >= SDP_MAX_FILTER_MODE) {
+        return ("Invalid source filter mode");
+    } else {
+        return (sdp_src_filter_mode_val[type].name);
+    }
+}
+
+const char *sdp_get_rtcp_unicast_mode_name (sdp_rtcp_unicast_mode_e type)
+{
+    if (type >= SDP_RTCP_MAX_UNICAST_MODE) {
+        return ("Invalid rtcp unicast mode");
+    } else {
+        return (sdp_rtcp_unicast_mode_val[type].name);
+    }
+}
+
+/* Function:    sdp_verify_sdp_ptr
+ * Description: Verify the SDP pointer is valid by checking for
+ *              the SDP magic number.  If not valid, display an error.
+ *              Note that we can't keep a statistic of this because we
+ *              track stats inside the config structure which is stored
+ *              in the SDP structure.
+ * Parameters: sdp_p    The SDP structure handle.
+ * Returns:    TRUE or FALSE.
+ */
+inline tinybool sdp_verify_sdp_ptr (sdp_t *sdp_p)
+{
+    if ((sdp_p != NULL) && (sdp_p->magic_num == SDP_MAGIC_NUM)) {
+        return (TRUE);
+    } else {
+        CSFLogError(logTag, "SDP: Invalid SDP pointer.");
+        return (FALSE);
+    }
+}
+
+/* Function:    sdp_init_description
+ * Description:        Allocates a new SDP structure that can be used for either
+ *              parsing or building an SDP description.  This routine
+ *              saves the config pointer passed in the SDP structure so
+ *              SDP will know how to parse/build based on the options defined.
+ *              An SDP structure must be allocated before parsing or building
+ *              since the handle must be passed to these routines.
+ * Parameters:  config_p     The config handle returned by sdp_init_config
+ * Returns:     A handle for a new SDP structure as a void ptr.
+*/
+sdp_t *sdp_init_description (const char *peerconnection, void *config_p)
+{
+    int i;
+    sdp_t *sdp_p;
+    sdp_conf_options_t *conf_p = (sdp_conf_options_t *)config_p;
+
+    if (sdp_verify_conf_ptr(conf_p) == FALSE) {
+        return (NULL);
+    }
+
+    sdp_p = (sdp_t *)SDP_MALLOC(sizeof(sdp_t));
+    if (sdp_p == NULL) {
+       return (NULL);
+    }
+
+    sstrncpy(sdp_p->peerconnection, peerconnection, sizeof(sdp_p->peerconnection));
+
+    /* Initialize magic number. */
+    sdp_p->magic_num = SDP_MAGIC_NUM;
+
+    sdp_p->conf_p             = conf_p;
+    sdp_p->version            = SDP_CURRENT_VERSION;
+    sdp_p->owner_name[0]      = '\0';
+    sdp_p->owner_sessid[0]    = '\0';
+    sdp_p->owner_version[0]   = '\0';
+    sdp_p->owner_network_type = SDP_NT_INVALID;
+    sdp_p->owner_addr_type    = SDP_AT_INVALID;
+    sdp_p->owner_addr[0]      = '\0';
+    sdp_p->sessname[0]        = '\0';
+    sdp_p->sessinfo_found     = FALSE;
+    sdp_p->uri_found          = FALSE;
+
+    sdp_p->default_conn.nettype      = SDP_NT_INVALID;
+    sdp_p->default_conn.addrtype     = SDP_AT_INVALID;
+    sdp_p->default_conn.conn_addr[0] = '\0';
+    sdp_p->default_conn.is_multicast = FALSE;
+    sdp_p->default_conn.ttl          = 0;
+    sdp_p->default_conn.num_of_addresses = 0;
+
+    sdp_p->bw.bw_data_count   = 0;
+    sdp_p->bw.bw_data_list    = NULL;
+
+    sdp_p->timespec_p         = NULL;
+    sdp_p->sess_attrs_p       = NULL;
+    sdp_p->mca_p              = NULL;
+    sdp_p->mca_count          = 0;
+
+    /* Set default debug flags from application config. */
+    for (i=0; i < SDP_MAX_DEBUG_TYPES; i++) {
+        sdp_p->debug_flag[i] = conf_p->debug_flag[i];
+    }
+
+    return (sdp_p);
+}
+
+
+/* Function:    void sdp_debug(sdp_t *sdp_p, sdp_debug_e debug_type,
+ *                             tinybool my_bool);
+ * Description:        Define the type of debug for this particular SDP structure.
+ *              By default, each SDP description has the settings that are
+ *              set for the application.
+ *              Valid debug types are ERRORS, WARNINGS, and TRACE.  Each
+ *              debug type can be turned on/off individually.  The
+ *              debug level can be redefined at any time.
+ * Parameters: sdp_ptr    The SDP handle returned by sdp_init_description.
+ *              debug_type Specifies the debug type being enabled/disabled.
+ *              my_bool    Defines whether the debug should be enabled or not.
+ * Returns:    Nothing.
+ */
+void sdp_debug (sdp_t *sdp_p, sdp_debug_e debug_type, tinybool debug_flag)
+{
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return;
+    }
+
+    if (debug_type < SDP_MAX_DEBUG_TYPES)  {
+        sdp_p->debug_flag[debug_type] = debug_flag;
+    }
+}
+
+
+/* Function:    void sdp_set_string_debug(sdp_t *sdp_p, char *debug_str)
+ * Description: Define a string to be associated with all debug output
+ *              for this SDP. The string will be copied into the SDP
+ *              structure and so the library will not be dependent on
+ *              the application's memory for this string.
+ * Parameters:  sdp_p      The SDP handle returned by sdp_init_description.
+ *              debug_str  Pointer to a string that should be printed out
+ *                         with every debug msg.
+ * Returns:     Nothing.
+ */
+void sdp_set_string_debug (sdp_t *sdp_p, const char *debug_str)
+{
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return;
+    }
+
+    sstrncpy(sdp_p->debug_str, debug_str, sizeof(sdp_p->debug_str));
+}
+
+
+/* Function:    sdp_validate_sdp
+ * Description: Validate an SDP structure.
+ * Parameters:  sdp_p    The SDP handle of the struct to validate.
+ * Returns:     A result value indicating if the validation was successful.
+ *              If not, what type of error was encountered.
+ */
+sdp_result_e sdp_validate_sdp (sdp_t *sdp_p)
+{
+    int i;
+    u16 num_media_levels;
+
+    /* Need to validate c= info is specified at session level or
+     * at all m= levels.
+     */
+    if (sdp_connection_valid((void *)sdp_p, SDP_SESSION_LEVEL) == FALSE) {
+        num_media_levels = sdp_get_num_media_lines((void *)sdp_p);
+        for (i=1; i <= num_media_levels; i++) {
+            if (sdp_connection_valid((void *)sdp_p, (unsigned short)i) == FALSE) {
+                sdp_parse_error(sdp_p->peerconnection,
+                    "%s c= connection line not specified for "
+                    "every media level, validation failed.",
+                    sdp_p->debug_str);
+                return (SDP_FAILURE);
+            }
+        }
+    }
+
+    /* Validate required lines were specified */
+    if ((sdp_owner_valid((void *)sdp_p) == FALSE) &&
+        (sdp_p->conf_p->owner_reqd == TRUE)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s o= owner line not specified, validation failed.",
+            sdp_p->debug_str);
+        return (SDP_FAILURE);
+    }
+
+    if ((sdp_session_name_valid((void *)sdp_p) == FALSE) &&
+        (sdp_p->conf_p->session_name_reqd == TRUE)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s s= session name line not specified, validation failed.",
+            sdp_p->debug_str);
+        return (SDP_FAILURE);
+    }
+
+    if ((sdp_timespec_valid((void *)sdp_p) == FALSE) &&
+        (sdp_p->conf_p->timespec_reqd == TRUE)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s t= timespec line not specified, validation failed.",
+            sdp_p->debug_str);
+        return (SDP_FAILURE);
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/* Function:    sdp_parse
+ * Description: Parse an SDP description in the specified buffer.
+ * Parameters:  sdp_p    The SDP handle returned by sdp_init_description
+ *              bufp     Pointer to the buffer containing the SDP
+ *                       description to parse.
+ *              len      The length of the buffer.
+ * Returns:     A result value indicating if the parse was successful and
+ *              if not, what type of error was encountered.  The
+ *              information from the parse is stored in the sdp_p structure.
+ */
+sdp_result_e sdp_parse (sdp_t *sdp_p, char **bufp, u16 len)
+{
+    u8           i;
+    u16          cur_level = SDP_SESSION_LEVEL;
+    char        *ptr;
+    char        *next_ptr = NULL;
+    char        *line_end;
+    sdp_token_e  last_token = SDP_TOKEN_V;
+    sdp_result_e result=SDP_SUCCESS;
+    tinybool     parse_done = FALSE;
+    tinybool     end_found = FALSE;
+    tinybool     first_line = TRUE;
+    tinybool     unrec_token = FALSE;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if ((bufp == NULL) || (*bufp == NULL)) {
+        return (SDP_NULL_BUF_PTR);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Trace SDP Parse:", sdp_p->debug_str);
+    }
+
+    next_ptr = *bufp;
+    sdp_p->conf_p->num_parses++;
+
+    /* Initialize the last valid capability instance to zero.  Used
+     * to help in parsing X-cpar attrs. */
+    sdp_p->cap_valid = FALSE;
+    sdp_p->last_cap_inst = 0;
+
+    /* We want to try to find the end of the SDP description, even if
+     * we find a parsing error.
+     */
+    while (!end_found) {
+       /* If the last char of this line goes beyond the end of the buffer,
+        * we don't parse it.
+        */
+        ptr = next_ptr;
+        line_end = sdp_findchar(ptr, "\n");
+        if (line_end >= (*bufp + len)) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s End of line beyond end of buffer.",
+                sdp_p->debug_str);
+            end_found = TRUE;
+            break;
+        }
+
+        /* Print the line if we're tracing. */
+        if ((parse_done == FALSE) &&
+         (sdp_p->debug_flag[SDP_DEBUG_TRACE])) {
+           SDP_PRINT("%s ", sdp_p->debug_str);
+
+           SDP_PRINT("%*s", line_end - ptr, ptr);
+
+        }
+
+        /* Find out which token this line has, if any. */
+        for (i=0; i < SDP_MAX_TOKENS; i++) {
+            if (strncmp(ptr, sdp_token[i].name, SDP_TOKEN_LEN) == 0) {
+                break;
+            }
+        }
+        if (i == SDP_MAX_TOKENS) {
+            /* See if the second char on the next line is an '=' char.
+             * If so, we note this as an unrecognized token line. */
+            if (ptr[1] == '=') {
+                unrec_token = TRUE;
+            }
+            if (first_line == TRUE) {
+                sdp_parse_error(sdp_p->peerconnection,
+                    "%s Attempt to parse text not recognized as "
+                    "SDP text, parse fails.", sdp_p->debug_str);
+                    /* If we haven't already printed out the line we
+                     * were trying to parse, do it now.
+                     */
+                if (!sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+                    SDP_PRINT("%s ", sdp_p->debug_str);
+                    SDP_PRINT("%*s", line_end - ptr, ptr);
+                }
+                sdp_p->conf_p->num_not_sdp_desc++;
+                return (SDP_NOT_SDP_DESCRIPTION);
+            } else {
+                end_found = TRUE;
+                break;
+            }
+        }
+
+        /* This is the beginning of a new SDP description. */
+        if ((first_line != TRUE) && (i == SDP_TOKEN_V)) {
+            end_found = TRUE;
+            break;
+        }
+
+        /* Advance the next ptr to one char beyond the end of the line. */
+        next_ptr = line_end + 1;
+        if (next_ptr >= (*bufp + len)) {
+            end_found = TRUE;
+        }
+
+        /* If we've finished parsing and are just looking for the end of
+         * the SDP description, we don't need to do anything else here.
+         */
+        if (parse_done == TRUE) {
+            continue;
+        }
+
+        /* Only certain tokens are valid at the media level. */
+        if (cur_level != SDP_SESSION_LEVEL) {
+            if ((i != SDP_TOKEN_I) && (i != SDP_TOKEN_C) &&
+                (i != SDP_TOKEN_B) && (i != SDP_TOKEN_K) &&
+                (i != SDP_TOKEN_A) && (i != SDP_TOKEN_M)) {
+                sdp_p->conf_p->num_invalid_token_order++;
+                sdp_parse_error(sdp_p->peerconnection,
+                    "%s Warning: Invalid token %s found at media level",
+                    sdp_p->debug_str, sdp_token[i].name);
+                continue;
+            }
+        }
+
+        /* Verify the token ordering. */
+        if (first_line == TRUE) {
+            if (i != SDP_TOKEN_V) {
+                if (sdp_p->conf_p->version_reqd == TRUE) {
+                    sdp_parse_error(sdp_p->peerconnection,
+                        "%s First line not v=, parse fails",
+                        sdp_p->debug_str);
+                    sdp_p->conf_p->num_invalid_token_order++;
+                    result = SDP_INVALID_TOKEN_ORDERING;
+                    parse_done = TRUE;
+                } else {
+                    last_token = (sdp_token_e)i;
+                }
+            } else {
+                last_token = (sdp_token_e)i;
+            }
+            first_line = FALSE;
+        } else {
+            if (i < last_token) {
+                sdp_p->conf_p->num_invalid_token_order++;
+                sdp_parse_error(sdp_p->peerconnection,
+                    "%s Warning: Invalid token ordering detected, "
+                    "token %s found after token %s", sdp_p->debug_str,
+                    sdp_token[i].name, sdp_token[last_token].name);
+            }
+        }
+
+        /* Finally parse the line. */
+        ptr += SDP_TOKEN_LEN;
+        result = sdp_token[i].parse_func(sdp_p, cur_level, (const char *)ptr);
+        last_token = (sdp_token_e)i;
+        if (last_token == SDP_TOKEN_M) {
+            if (cur_level == SDP_SESSION_LEVEL) {
+                cur_level = 1;
+            } else {
+                cur_level++;
+            }
+            /* The token ordering can start again at i= */
+            last_token = (sdp_token_e)(SDP_TOKEN_I - 1);
+        }
+        if (result != SDP_SUCCESS) {
+            parse_done = TRUE;
+        }
+
+        /* Skip the new line char at the end of this line and see if
+         * this is the end of the buffer.
+         */
+        if ((line_end + 1) == (*bufp + len)) {
+            end_found = TRUE;
+        }
+    }
+
+    /* If we found no valid lines, return an error. */
+    if (first_line == TRUE) {
+        sdp_p->conf_p->num_not_sdp_desc++;
+        return (SDP_NOT_SDP_DESCRIPTION);
+    }
+
+    /* If no errors were found yet, validate the overall sdp. */
+    if (result == SDP_SUCCESS) {
+        result = sdp_validate_sdp(sdp_p);
+    }
+    /* Return the pointer where we left off. */
+    *bufp = next_ptr;
+    /* If the SDP is valid, but the next line following was an
+     * unrecognized <token>= line, indicate this on the return. */
+    if ((result == SDP_SUCCESS) && (unrec_token == TRUE)) {
+        return (SDP_UNRECOGNIZED_TOKEN);
+    } else {
+        return (result);
+    }
+}
+
+
+/* Function:    sdp_build
+ * Description: Build an SDP description in the specified buffer based
+ *              on the information in the given SDP structure.
+ * Parameters:  sdp_p    The SDP handle returned by sdp_init_description
+ *              fs A flex_string where the SDP description should be built.
+ * Returns:     A result value indicating if the build was successful and
+ *              if not, what type of error was encountered - e.g.,
+ *              description was too long for the given buffer.
+ */
+sdp_result_e sdp_build (sdp_t *sdp_p, flex_string *fs)
+{
+    int i, j;
+    sdp_result_e        result = SDP_SUCCESS;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    if (!fs) {
+        return (SDP_NULL_BUF_PTR);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Trace SDP Build:", sdp_p->debug_str);
+    }
+
+    sdp_p->conf_p->num_builds++;
+
+    for (i=0; ((i < SDP_TOKEN_M) &&
+               (result == SDP_SUCCESS)); i++) {
+        result = sdp_token[i].build_func(sdp_p, SDP_SESSION_LEVEL, fs);
+        /* ok not to check buffer space (yet) as the if() checks it */
+    }
+    /* If the session level was ok, build the media lines. */
+    if (result == SDP_SUCCESS) {
+        for (i=1; ((i <= sdp_p->mca_count) &&
+                   (result == SDP_SUCCESS)); i++) {
+            result = sdp_token[SDP_TOKEN_M].build_func(sdp_p, (u16)i, fs);
+
+            /* ok not to check buffer space (yet) as the for() checks it */
+            for (j=SDP_TOKEN_I;
+                 ((j < SDP_TOKEN_M) && (result == SDP_SUCCESS));
+                 j++) {
+                if ((j == SDP_TOKEN_U) || (j == SDP_TOKEN_E) ||
+                    (j == SDP_TOKEN_P) || (j == SDP_TOKEN_T) ||
+                    (j == SDP_TOKEN_R) || (j == SDP_TOKEN_Z)) {
+                    /* These tokens not valid at media level. */
+                    continue;
+                }
+                result = sdp_token[j].build_func(sdp_p, (u16)i, fs);
+                /* ok not to check buffer space (yet) as the for() checks it */
+            }
+        }
+    }
+
+    return (result);
+}
+
+/* Function:    sdp_copy
+ * Description: Create a new SDP structure that is an exact copy of the
+ *              one passed in.
+ * Parameters:  orig_sdp_p  The SDP handle of the SDP to be copied.
+ * Returns:     A handle for a new SDP structure as a void ptr.
+*/
+sdp_t *sdp_copy (sdp_t *orig_sdp_p)
+{
+    int i, j;
+    u16                 cur_level;
+    sdp_result_e        rc;
+    sdp_t              *new_sdp_p;
+    sdp_timespec_t     *cur_time_p = NULL;
+    sdp_timespec_t     *orig_time_p = NULL;
+    sdp_mca_t          *orig_mca_p = NULL;
+    sdp_mca_t          *new_mca_p = NULL;
+    sdp_mca_t          *dst_mca_p = NULL;
+    sdp_media_profiles_t *src_media_profile_p;
+    sdp_media_profiles_t *dst_media_profile_p;
+
+    if (orig_sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Copy SDP:", orig_sdp_p->debug_str);
+    }
+
+    if (sdp_verify_sdp_ptr(orig_sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    new_sdp_p = (sdp_t *)SDP_MALLOC(sizeof(sdp_t));
+    if (new_sdp_p == NULL) {
+        return (NULL);
+    }
+
+    sstrncpy(new_sdp_p->peerconnection, orig_sdp_p->peerconnection,
+        sizeof(new_sdp_p->peerconnection));
+
+    /* Initialize magic number. */
+    new_sdp_p->magic_num = orig_sdp_p->magic_num;
+
+    new_sdp_p->conf_p             = orig_sdp_p->conf_p;
+    new_sdp_p->version            = orig_sdp_p->version;
+    sstrncpy(new_sdp_p->owner_name, orig_sdp_p->owner_name,
+             SDP_MAX_LINE_LEN+1);
+    sstrncpy(new_sdp_p->owner_sessid, orig_sdp_p->owner_sessid,
+             SDP_MAX_LINE_LEN+1);
+    sstrncpy(new_sdp_p->owner_version, orig_sdp_p->owner_version,
+             SDP_MAX_LINE_LEN+1);
+    new_sdp_p->owner_network_type = orig_sdp_p->owner_network_type;
+    new_sdp_p->owner_addr_type    = orig_sdp_p->owner_addr_type;
+    sstrncpy(new_sdp_p->owner_addr, orig_sdp_p->owner_addr,
+             SDP_MAX_LINE_LEN+1);
+    sstrncpy(new_sdp_p->sessname, orig_sdp_p->sessname,
+             SDP_MAX_LINE_LEN+1);
+    new_sdp_p->sessinfo_found     = orig_sdp_p->sessinfo_found;
+    new_sdp_p->uri_found          = orig_sdp_p->uri_found;
+
+    new_sdp_p->default_conn.nettype      = orig_sdp_p->default_conn.nettype;
+    new_sdp_p->default_conn.addrtype     = orig_sdp_p->default_conn.addrtype;
+    sstrncpy(new_sdp_p->default_conn.conn_addr,
+             orig_sdp_p->default_conn.conn_addr,
+             SDP_MAX_LINE_LEN+1);
+
+    new_sdp_p->default_conn.is_multicast =
+        orig_sdp_p->default_conn.is_multicast;
+    new_sdp_p->default_conn.ttl      = orig_sdp_p->default_conn.ttl;
+    new_sdp_p->default_conn.num_of_addresses
+        = orig_sdp_p->default_conn.num_of_addresses;
+    new_sdp_p->encrypt.encrypt_type = orig_sdp_p->encrypt.encrypt_type;
+    sstrncpy(new_sdp_p->encrypt.encrypt_key,
+             orig_sdp_p->encrypt.encrypt_key, SDP_MAX_LINE_LEN+1);
+
+    /* Copy all session level bw lines. */
+    rc = sdp_copy_all_bw_lines(orig_sdp_p, new_sdp_p,
+                               SDP_SESSION_LEVEL, SDP_SESSION_LEVEL);
+    if (rc != SDP_SUCCESS) {
+        sdp_free_description(new_sdp_p);
+        return (NULL);
+    }
+
+    /* Copy timespec structures. */
+    orig_time_p = orig_sdp_p->timespec_p;
+    while (orig_time_p != NULL) {
+        if (cur_time_p == NULL) {
+            new_sdp_p->timespec_p = \
+                (sdp_timespec_t *)SDP_MALLOC(sizeof(sdp_timespec_t));
+            cur_time_p = new_sdp_p->timespec_p;
+        } else {
+            cur_time_p->next_p = \
+                (sdp_timespec_t *)SDP_MALLOC(sizeof(sdp_timespec_t));
+            cur_time_p = cur_time_p->next_p;
+        }
+        if (cur_time_p == NULL) {
+            sdp_free_description(new_sdp_p);
+            return (NULL);
+        }
+        sstrncpy(cur_time_p->start_time, orig_time_p->start_time,
+                 SDP_MAX_LINE_LEN+1);
+        sstrncpy(cur_time_p->stop_time, orig_time_p->stop_time,
+                 SDP_MAX_LINE_LEN+1);
+        cur_time_p->next_p = NULL;
+
+        /* Move to next time structure. */
+        orig_time_p = orig_time_p->next_p;
+    }
+
+    /* Copy all session attributes. */
+    rc = sdp_copy_all_attrs(orig_sdp_p, new_sdp_p,
+                            SDP_SESSION_LEVEL, SDP_SESSION_LEVEL);
+    if (rc != SDP_SUCCESS) {
+        sdp_free_description(new_sdp_p);
+        return (NULL);
+    }
+
+
+    /* Now copy each media level with its parameters and all
+     * corresponding attrs. */
+    orig_mca_p = orig_sdp_p->mca_p;
+    new_mca_p = NULL;
+    cur_level = 0;
+    while (orig_mca_p != NULL) {
+        cur_level++;
+
+        /* Allocate and link in a new media level. */
+        new_mca_p = sdp_alloc_mca();
+        if (new_mca_p == NULL) {
+            sdp_free_description(new_sdp_p);
+            return (NULL);
+        }
+        if (dst_mca_p == NULL) {
+            new_sdp_p->mca_p = new_mca_p;
+        } else {
+            dst_mca_p->next_p = new_mca_p;
+        }
+        dst_mca_p = new_mca_p;
+        new_sdp_p->mca_count++;
+
+        /* Copy all the media level parameters. */
+        dst_mca_p->media         = orig_mca_p->media;
+        dst_mca_p->conn.nettype  = orig_mca_p->conn.nettype;
+        dst_mca_p->conn.addrtype = orig_mca_p->conn.addrtype;
+        sstrncpy(dst_mca_p->conn.conn_addr, orig_mca_p->conn.conn_addr,
+                 SDP_MAX_LINE_LEN+1);
+
+        dst_mca_p->conn.is_multicast = orig_mca_p->conn.is_multicast;
+        dst_mca_p->conn.ttl      = orig_mca_p->conn.ttl;
+        dst_mca_p->conn.num_of_addresses = orig_mca_p->conn.num_of_addresses;
+
+        dst_mca_p->transport     = orig_mca_p->transport;
+        dst_mca_p->port_format   = orig_mca_p->port_format;
+        dst_mca_p->port          = orig_mca_p->port;
+        dst_mca_p->num_ports     = orig_mca_p->num_ports;
+        dst_mca_p->vpi           = orig_mca_p->vpi;
+        dst_mca_p->vci           = orig_mca_p->vci;
+        dst_mca_p->vcci          = orig_mca_p->vcci;
+        dst_mca_p->cid           = orig_mca_p->cid;
+        dst_mca_p->num_payloads  = orig_mca_p->num_payloads;
+
+        for (i=0; i < SDP_MAX_PAYLOAD_TYPES; i++) {
+            dst_mca_p->payload_indicator[i] = orig_mca_p->payload_indicator[i];
+            dst_mca_p->payload_type[i] = orig_mca_p->payload_type[i];
+        }
+
+        dst_mca_p->sessinfo_found = orig_mca_p->sessinfo_found;
+        dst_mca_p->encrypt.encrypt_type = orig_mca_p->encrypt.encrypt_type;
+        sstrncpy(dst_mca_p->encrypt.encrypt_key,
+                 orig_mca_p->encrypt.encrypt_key, SDP_MAX_LINE_LEN+1);
+        dst_mca_p->media_direction = orig_mca_p->media_direction;
+
+        /* Now copy all the media level bw lines over. */
+        rc = sdp_copy_all_bw_lines(orig_sdp_p, new_sdp_p, cur_level, cur_level);
+        if (rc != SDP_SUCCESS) {
+            sdp_free_description(new_sdp_p);
+            return (NULL);
+        }
+
+        dst_mca_p->mid = orig_mca_p->mid;
+
+        if (orig_mca_p->media_profiles_p != NULL) {
+            src_media_profile_p = orig_mca_p->media_profiles_p;
+            dst_mca_p->media_profiles_p = (sdp_media_profiles_t *)
+                SDP_MALLOC(sizeof(sdp_media_profiles_t));
+            dst_media_profile_p = dst_mca_p->media_profiles_p;
+            if (dst_media_profile_p == NULL) {
+                sdp_free_description(new_sdp_p);
+                return (NULL);
+            }
+
+            dst_media_profile_p->num_profiles =
+                src_media_profile_p->num_profiles;
+            for (i=0; i < SDP_MAX_PROFILES; i++) {
+                dst_media_profile_p->profile[i] =
+                    src_media_profile_p->profile[i];
+                dst_media_profile_p->num_payloads[i] =
+                    src_media_profile_p->num_payloads[i];
+                for (j=0; j < SDP_MAX_PAYLOAD_TYPES; j++) {
+                    dst_media_profile_p->payload_indicator[i][j] =
+                        src_media_profile_p->payload_indicator[i][j];
+                    dst_media_profile_p->payload_type[i][j] =
+                        src_media_profile_p->payload_type[i][j];
+
+                }
+            }
+        }
+
+        /* Now copy all the media level attrs over. */
+        (void)sdp_copy_all_attrs(orig_sdp_p, new_sdp_p, cur_level, cur_level);
+
+        /* Now move to the next media level. */
+        orig_mca_p = orig_mca_p->next_p;
+    }
+
+
+    /* Set default debug flags from application config. */
+    for (i=0; i < SDP_MAX_DEBUG_TYPES; i++) {
+        new_sdp_p->debug_flag[i] = orig_sdp_p->debug_flag[i];
+    }
+
+    return (new_sdp_p);
+}
+
+/* Function:    sdp_free_description
+ * Description:        Free an SDP description and all memory associated with it.
+ * Parameters:  sdp_p  The SDP handle returned by sdp_init_description
+ * Returns:     A result value indicating if the free was successful and
+ *              if not, what type of error was encountered - e.g., sdp_p
+ *              was invalid and didn't point to an SDP structure.
+*/
+sdp_result_e sdp_free_description (sdp_t *sdp_p)
+{
+    sdp_timespec_t  *time_p, *next_time_p;
+    sdp_attr_t      *attr_p, *next_attr_p;
+    sdp_mca_t       *mca_p, *next_mca_p;
+    sdp_bw_t        *bw_p;
+    sdp_bw_data_t   *bw_data_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    /* Free any timespec structures - should be only one since
+     * this is all we currently support.
+     */
+    time_p = sdp_p->timespec_p;
+    while (time_p != NULL) {
+       next_time_p = time_p->next_p;
+       SDP_FREE(time_p);
+       time_p = next_time_p;
+    }
+
+    bw_p = &(sdp_p->bw);
+    bw_data_p = bw_p->bw_data_list;
+    while (bw_data_p != NULL) {
+        bw_p->bw_data_list = bw_data_p->next_p;
+        SDP_FREE(bw_data_p);
+        bw_data_p = bw_p->bw_data_list;
+    }
+
+    /* Free any session attr structures */
+    attr_p = sdp_p->sess_attrs_p;
+    while (attr_p != NULL) {
+       next_attr_p = attr_p->next_p;
+       sdp_free_attr(attr_p);
+       attr_p = next_attr_p;
+    }
+
+    /* Free any mca structures */
+    mca_p = sdp_p->mca_p;
+    while (mca_p != NULL) {
+       next_mca_p = mca_p->next_p;
+
+       /* Free any media attr structures */
+       attr_p = mca_p->media_attrs_p;
+       while (attr_p != NULL) {
+           next_attr_p = attr_p->next_p;
+           sdp_free_attr(attr_p);
+           attr_p = next_attr_p;
+       }
+
+        /* Free the media profiles struct if allocated. */
+        if (mca_p->media_profiles_p != NULL) {
+            SDP_FREE(mca_p->media_profiles_p);
+        }
+
+        bw_p = &(mca_p->bw);
+        bw_data_p = bw_p->bw_data_list;
+        while (bw_data_p != NULL) {
+            bw_p->bw_data_list = bw_data_p->next_p;
+            SDP_FREE(bw_data_p);
+            bw_data_p = bw_p->bw_data_list;
+        }
+
+       SDP_FREE(mca_p);
+       mca_p = next_mca_p;
+    }
+
+    SDP_FREE(sdp_p);
+
+    return (SDP_SUCCESS);
+}
+
+/*
+ * sdp_parse_error
+ * Send SDP parsing errors to log and up to peerconnection
+ */
+void sdp_parse_error(const char *peerconnection, const char *format, ...) {
+    va_list ap;
+
+    /* TODO - report error up to PeerConnection here */
+
+    va_start(ap, format);
+    CSFLogErrorV("SDP Parse", format, ap);
+    va_end(ap);
+}
diff --git a/libs/sipcc/core/sdp/sdp_os_defs.h b/libs/sipcc/core/sdp/sdp_os_defs.h
new file mode 100644 (file)
index 0000000..2f04b6f
--- /dev/null
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDP_OS_DEFS_H_
+#define _SDP_OS_DEFS_H_
+
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "phone_debug.h"
+
+
+#define SDP_PRINT     buginf
+#define SDP_MALLOC(x) cpr_calloc(1, (x))
+#define SDP_FREE      cpr_free
+
+typedef uint8_t    tinybool;
+typedef uint8_t    u8;
+typedef uint16_t   u16;
+typedef uint16_t   uint16;
+typedef uint32_t   u32;
+typedef uint32_t   uint32;
+typedef int32_t    int32;
+typedef int16_t    int16;
+typedef unsigned short ushort;
+typedef unsigned long  ulong;
+#define inline
+
+
+#endif /* _SDP_OS_DEFS_H_ */
diff --git a/libs/sipcc/core/sdp/sdp_private.h b/libs/sipcc/core/sdp/sdp_private.h
new file mode 100644 (file)
index 0000000..90634d4
--- /dev/null
@@ -0,0 +1,311 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDP_PRIVATE_H_
+#define _SDP_PRIVATE_H_
+
+
+#include "sdp.h"
+
+extern const sdp_attrarray_t sdp_attr[];
+extern const sdp_namearray_t sdp_media[];
+extern const sdp_namearray_t sdp_nettype[];
+extern const sdp_namearray_t sdp_addrtype[];
+extern const sdp_namearray_t sdp_transport[];
+extern const sdp_namearray_t sdp_encrypt[];
+extern const sdp_namearray_t sdp_payload[];
+extern const sdp_namearray_t sdp_t38_rate[];
+extern const sdp_namearray_t sdp_t38_udpec[];
+extern const sdp_namearray_t sdp_qos_strength[];
+extern const sdp_namearray_t sdp_qos_direction[];
+extern const sdp_namearray_t sdp_qos_status_type[];
+extern const sdp_namearray_t sdp_curr_type[];
+extern const sdp_namearray_t sdp_des_type[];
+extern const sdp_namearray_t sdp_conf_type[];
+extern const sdp_namearray_t sdp_mediadir_role[];
+extern const sdp_namearray_t sdp_fmtp_codec_param[];
+extern const sdp_namearray_t sdp_fmtp_codec_param_val[];
+extern const sdp_namearray_t sdp_silencesupp_pref[];
+extern const sdp_namearray_t sdp_silencesupp_siduse[];
+extern const sdp_namearray_t sdp_srtp_context_crypto_suite[];
+extern const sdp_namearray_t sdp_bw_modifier_val[];
+extern const sdp_namearray_t sdp_group_attr_val[];
+extern const sdp_namearray_t sdp_src_filter_mode_val[];
+extern const sdp_namearray_t sdp_rtcp_unicast_mode_val[];
+
+extern const  sdp_srtp_crypto_suite_list sdp_srtp_crypto_suite_array[];
+/* Function Prototypes */
+
+/* sdp_access.c */
+extern sdp_mca_t *sdp_find_media_level(sdp_t *sdp_p, u16 level);
+extern sdp_bw_data_t* sdp_find_bw_line (void *sdp_ptr, u16 level, u16 inst_num);
+
+/* sdp_attr.c */
+extern sdp_result_e sdp_parse_attribute(sdp_t *sdp_p, u16 level,
+                                        const char *ptr);
+extern sdp_result_e sdp_parse_attr_simple_string(sdp_t *sdp_p,
+                                     sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_simple_string(sdp_t *sdp_p,
+                                     sdp_attr_t *attr_p, flex_string *fs);
+extern sdp_result_e sdp_parse_attr_simple_u32(sdp_t *sdp_p,
+                                     sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_simple_u32(sdp_t *sdp_p,
+                                     sdp_attr_t *attr_p, flex_string *fs);
+extern sdp_result_e sdp_parse_attr_simple_bool(sdp_t *sdp_p,
+                                     sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_simple_bool(sdp_t *sdp_p,
+                                     sdp_attr_t *attr_p, flex_string *fs);
+extern sdp_result_e sdp_parse_attr_maxprate(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_parse_attr_fmtp(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_fmtp(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_direction(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_direction(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_qos(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_qos(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_curr(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_curr (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_des(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_des (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_conf(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_conf (sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_transport_map(sdp_t *sdp_p,
+                                    sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_transport_map(sdp_t *sdp_p,
+                                    sdp_attr_t *attr_p, flex_string *fs);
+extern sdp_result_e sdp_parse_attr_subnet(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_subnet(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_t38_ratemgmt(sdp_t *sdp_p,
+                                     sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_t38_ratemgmt(sdp_t *sdp_p,
+                                     sdp_attr_t *attr_p, flex_string *fs);
+extern sdp_result_e sdp_parse_attr_t38_udpec(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_t38_udpec(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_cap(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_cap(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_cpar(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_cpar(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_pc_codec(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_pc_codec(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_xcap(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                        const char *ptr);
+extern sdp_result_e sdp_build_attr_xcap(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                        flex_string *fs);
+extern sdp_result_e sdp_parse_attr_xcpar(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                         const char *ptr);
+extern sdp_result_e sdp_build_attr_xcpar(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                         flex_string *fs);
+extern sdp_result_e sdp_parse_attr_rtr(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_build_attr_rtr(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                     flex_string *fs);
+extern sdp_result_e sdp_parse_attr_comediadir(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                              const char *ptr);
+extern sdp_result_e sdp_build_attr_comediadir(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                                              flex_string *fs);
+extern sdp_result_e sdp_parse_attr_silencesupp(sdp_t *sdp_p,
+                                               sdp_attr_t *attr_p,
+                                               const char *ptr);
+extern sdp_result_e sdp_build_attr_silencesupp(sdp_t *sdp_p,
+                                               sdp_attr_t *attr_p,
+                                               flex_string *fs);
+extern sdp_result_e sdp_parse_attr_srtpcontext(sdp_t *sdp_p,
+                                               sdp_attr_t *attr_p,
+                                               const char *ptr);
+extern sdp_result_e sdp_build_attr_srtpcontext(sdp_t *sdp_p,
+                                               sdp_attr_t *attr_p,
+                                               flex_string *fs);
+extern sdp_result_e sdp_parse_attr_mptime(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_mptime(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
+
+extern sdp_result_e sdp_parse_attr_x_sidin(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_x_sidin(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
+
+extern sdp_result_e sdp_parse_attr_x_sidout(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_x_sidout(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
+
+extern sdp_result_e sdp_parse_attr_x_confid(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_x_confid(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
+
+extern sdp_result_e sdp_parse_attr_group(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_group(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
+
+extern sdp_result_e sdp_parse_attr_source_filter(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_source_filter(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
+
+extern sdp_result_e sdp_parse_attr_rtcp_unicast(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_build_attr_rtcp_unicast(
+    sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
+
+extern sdp_result_e sdp_build_attr_ice_attr (
+       sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
+extern sdp_result_e sdp_parse_attr_ice_attr (
+       sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+
+extern sdp_result_e sdp_build_attr_rtcp_mux_attr (
+       sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
+extern sdp_result_e sdp_parse_attr_rtcp_mux_attr (
+       sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+extern sdp_result_e sdp_parse_attr_fingerprint_attr (
+    sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
+
+/* sdp_attr_access.c */
+extern void sdp_free_attr(sdp_attr_t *attr_p);
+extern sdp_result_e sdp_find_attr_list(sdp_t *sdp_p, u16 level, u8 cap_num,
+                                       sdp_attr_t **attr_p, char *fname);
+extern sdp_attr_t *sdp_find_attr(sdp_t *sdp_p, u16 level, u8 cap_num,
+                                 sdp_attr_e attr_type, u16 inst_num);
+extern sdp_attr_t *sdp_find_capability(sdp_t *sdp_p, u16 level, u8 cap_num);
+
+/* sdp_config.c */
+extern tinybool sdp_verify_conf_ptr(sdp_conf_options_t *conf_p);
+
+/* sdp_main.c */
+extern const char *sdp_get_attr_name(sdp_attr_e attr_type);
+extern const char *sdp_get_media_name(sdp_media_e media_type);
+extern const char *sdp_get_network_name(sdp_nettype_e network_type);
+extern const char *sdp_get_address_name(sdp_addrtype_e addr_type);
+extern const char *sdp_get_transport_name(sdp_transport_e transport_type);
+extern const char *sdp_get_encrypt_name(sdp_encrypt_type_e encrypt_type);
+extern const char *sdp_get_payload_name(sdp_payload_e payload);
+extern const char *sdp_get_t38_ratemgmt_name(sdp_t38_ratemgmt_e rate);
+extern const char *sdp_get_t38_udpec_name(sdp_t38_udpec_e udpec);
+extern const char *sdp_get_qos_strength_name(sdp_qos_strength_e strength);
+extern const char *sdp_get_qos_direction_name(sdp_qos_dir_e direction);
+extern const char *sdp_get_qos_status_type_name(sdp_qos_status_types_e status_type);
+extern const char *sdp_get_curr_type_name(sdp_curr_type_e curr_type);
+extern const char *sdp_get_des_type_name(sdp_des_type_e des_type);
+extern const char *sdp_get_conf_type_name(sdp_conf_type_e conf_type);
+extern const char *sdp_get_mediadir_role_name (sdp_mediadir_role_e role);
+extern const char *sdp_get_silencesupp_pref_name(sdp_silencesupp_pref_e pref);
+extern const char *sdp_get_silencesupp_siduse_name(sdp_silencesupp_siduse_e
+                                                   siduse);
+
+extern const char *sdp_get_bw_modifier_name(sdp_bw_modifier_e bw_modifier);
+extern const char *sdp_get_group_attr_name(sdp_group_attr_e group_attr);
+extern const char *sdp_get_src_filter_mode_name(sdp_src_filter_mode_e type);
+extern const char *sdp_get_rtcp_unicast_mode_name(sdp_rtcp_unicast_mode_e type);
+
+extern tinybool sdp_verify_sdp_ptr(sdp_t *sdp_p);
+
+
+/* sdp_tokens.c */
+extern sdp_result_e sdp_parse_version(sdp_t *sdp_p, u16 token,
+                                      const char *ptr);
+extern sdp_result_e sdp_build_version(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_owner(sdp_t *sdp_p, u16 token,
+                                    const char *ptr);
+extern sdp_result_e sdp_build_owner(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_sessname(sdp_t *sdp_p, u16 token,
+                                       const char *ptr);
+extern sdp_result_e sdp_build_sessname(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_sessinfo(sdp_t *sdp_p, u16 token,
+                                       const char *ptr);
+extern sdp_result_e sdp_build_sessinfo(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_uri(sdp_t *sdp_p, u16 token, const char *ptr);
+extern sdp_result_e sdp_build_uri(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_email(sdp_t *sdp_p, u16 token, const char *ptr);
+extern sdp_result_e sdp_build_email(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_phonenum(sdp_t *sdp_p, u16 token,
+                                       const char *ptr);
+extern sdp_result_e sdp_build_phonenum(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_connection(sdp_t *sdp_p, u16 token,
+                                         const char *ptr);
+extern sdp_result_e sdp_build_connection(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_bandwidth(sdp_t *sdp_p, u16 token,
+                                        const char *ptr);
+extern sdp_result_e sdp_build_bandwidth(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_timespec(sdp_t *sdp_p, u16 token,
+                                       const char *ptr);
+extern sdp_result_e sdp_build_timespec(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_repeat_time(sdp_t *sdp_p, u16 token,
+                                          const char *ptr);
+extern sdp_result_e sdp_build_repeat_time(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_timezone_adj(sdp_t *sdp_p, u16 token,
+                                           const char *ptr);
+extern sdp_result_e sdp_build_timezone_adj(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_encryption(sdp_t *sdp_p, u16 token,
+                                         const char *ptr);
+extern sdp_result_e sdp_build_encryption(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_media(sdp_t *sdp_p, u16 token, const char *ptr);
+extern sdp_result_e sdp_build_media(sdp_t *sdp_p, u16 token, flex_string *fs);
+extern sdp_result_e sdp_parse_attribute(sdp_t *sdp_p, u16 token,
+                                        const char *ptr);
+extern sdp_result_e sdp_build_attribute(sdp_t *sdp_p, u16 token, flex_string *fs);
+
+extern void sdp_parse_payload_types(sdp_t *sdp_p, sdp_mca_t *mca_p,
+                                     const char *ptr);
+extern sdp_result_e sdp_parse_multiple_profile_payload_types(sdp_t *sdp_p,
+                                               sdp_mca_t *mca_p,
+                                               const char *ptr);
+extern sdp_result_e
+sdp_parse_attr_sdescriptions(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                             const char *ptr);
+
+extern sdp_result_e
+sdp_build_attr_sdescriptions(sdp_t *sdp_p, sdp_attr_t *attr_p,
+                             flex_string *fs);
+
+
+/* sdp_utils.c */
+extern sdp_mca_t *sdp_alloc_mca(void);
+extern tinybool sdp_validate_maxprate(const char *string_parm);
+extern char *sdp_findchar(const char *ptr, char *char_list);
+extern const char *sdp_getnextstrtok(const char *str, char *tokenstr, unsigned tokenstr_len,
+                               const char *delim, sdp_result_e *result);
+extern u32 sdp_getnextnumtok(const char *str, const char **str_end,
+                             const char *delim, sdp_result_e *result);
+extern u32 sdp_getnextnumtok_or_null(const char *str, const char **str_end,
+                                     const char *delim, tinybool *null_ind,
+                                     sdp_result_e *result);
+extern tinybool sdp_getchoosetok(const char *str, const char **str_end,
+                                 const char *delim, sdp_result_e *result);
+
+extern
+tinybool verify_sdescriptions_mki(char *buf, char *mkiVal, u16 *mkiLen);
+
+extern
+tinybool verify_sdescriptions_lifetime(char *buf);
+
+/* sdp_services_xxx.c */
+extern void sdp_dump_buffer(char *_ptr, int _size_bytes);
+
+tinybool sdp_checkrange(sdp_t *sdp, char *num, ulong* lval);
+
+#endif /* _SDP_PRIVATE_H_ */
diff --git a/libs/sipcc/core/sdp/sdp_services_unix.c b/libs/sipcc/core/sdp/sdp_services_unix.c
new file mode 100755 (executable)
index 0000000..79787eb
--- /dev/null
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp_os_defs.h"
+#include "sdp.h"
+#include "sdp_private.h"
+
+
+/******************************************************************/
+/*  Required Platform Routines                                    */
+/*                                                                */
+/*     These routines are called from the common SDP code.        */
+/*     They must be provided for each platform.                   */
+/*                                                                */
+/******************************************************************/
+
+#if 0
+void sdp_log_errmsg (sdp_errmsg_e errmsg, char *str)
+{
+    switch (errmsg) {
+
+    case SDP_ERR_INVALID_CONF_PTR:
+        SDP_ERROR("\nSDP: Invalid Config pointer (%s).", str);
+        break;
+
+    case SDP_ERR_INVALID_SDP_PTR:
+        SDP_ERROR("\nSDP: Invalid SDP pointer (%s).", str);
+        break;
+
+    case SDP_ERR_INTERNAL:
+        SDP_ERROR("\nSDP: Internal error (%s).", str);
+        break;
+
+    default:
+        break;
+    }
+}
+#endif
+
+/*
+ * sdp_dump_buffer
+ *
+ * Utility to send _size_bytes of data from the string
+ * pointed to by _ptr to the buginf function. This may make
+ * multiple buginf calls if the buffer is too large for buginf.
+ */
+void sdp_dump_buffer (char * _ptr, int _size_bytes)
+{
+    buginf_msg(_ptr);
+}
+
+/******************************************************************/
+/*                                                                */
+/*  Platform Specific Routines                                    */
+/*                                                                */
+/*    These routines are only used in this particular platform.   */
+/*    They are called from the required platform specific         */
+/*    routines provided below, not from the common SDP code.      */
+/*                                                                */
+/******************************************************************/
+
+/* There are currently no platform specific routines required. */
diff --git a/libs/sipcc/core/sdp/sdp_services_win32.c b/libs/sipcc/core/sdp/sdp_services_win32.c
new file mode 100755 (executable)
index 0000000..79787eb
--- /dev/null
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp_os_defs.h"
+#include "sdp.h"
+#include "sdp_private.h"
+
+
+/******************************************************************/
+/*  Required Platform Routines                                    */
+/*                                                                */
+/*     These routines are called from the common SDP code.        */
+/*     They must be provided for each platform.                   */
+/*                                                                */
+/******************************************************************/
+
+#if 0
+void sdp_log_errmsg (sdp_errmsg_e errmsg, char *str)
+{
+    switch (errmsg) {
+
+    case SDP_ERR_INVALID_CONF_PTR:
+        SDP_ERROR("\nSDP: Invalid Config pointer (%s).", str);
+        break;
+
+    case SDP_ERR_INVALID_SDP_PTR:
+        SDP_ERROR("\nSDP: Invalid SDP pointer (%s).", str);
+        break;
+
+    case SDP_ERR_INTERNAL:
+        SDP_ERROR("\nSDP: Internal error (%s).", str);
+        break;
+
+    default:
+        break;
+    }
+}
+#endif
+
+/*
+ * sdp_dump_buffer
+ *
+ * Utility to send _size_bytes of data from the string
+ * pointed to by _ptr to the buginf function. This may make
+ * multiple buginf calls if the buffer is too large for buginf.
+ */
+void sdp_dump_buffer (char * _ptr, int _size_bytes)
+{
+    buginf_msg(_ptr);
+}
+
+/******************************************************************/
+/*                                                                */
+/*  Platform Specific Routines                                    */
+/*                                                                */
+/*    These routines are only used in this particular platform.   */
+/*    They are called from the required platform specific         */
+/*    routines provided below, not from the common SDP code.      */
+/*                                                                */
+/******************************************************************/
+
+/* There are currently no platform specific routines required. */
diff --git a/libs/sipcc/core/sdp/sdp_token.c b/libs/sipcc/core/sdp/sdp_token.c
new file mode 100644 (file)
index 0000000..0856dee
--- /dev/null
@@ -0,0 +1,1763 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+
+#include "sdp_os_defs.h"
+#include "sdp.h"
+#include "sdp_private.h"
+#include "configmgr.h"
+#include "prot_configmgr.h"
+#include "ccapi.h"
+#include "CSFLog.h"
+
+static const char *logTag = "sdp_token";
+
+#define MCAST_STRING_LEN 4
+
+
+sdp_result_e sdp_parse_version (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    sdp_result_e result = SDP_FAILURE;
+
+    sdp_p->version = (u16)sdp_getnextnumtok(ptr, &ptr, " \t", &result);
+    if ((result != SDP_SUCCESS) || (sdp_p->version != SDP_CURRENT_VERSION)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Invalid version (%lu) found, parse failed.",
+            sdp_p->debug_str, sdp_p->version);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parse version line successful, version %u",
+                  sdp_p->debug_str, (u16)sdp_p->version);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_version (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    if (sdp_p->version == SDP_INVALID_VALUE) {
+        if (sdp_p->conf_p->version_reqd == TRUE) {
+            CSFLogError(logTag, "%s Invalid version for v= line, "
+                        "build failed.", sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        } else {
+            /* v= line is not required. */
+            return (SDP_SUCCESS);
+        }
+    }
+
+    flex_string_sprintf(fs, "v=%u\r\n", (u16)sdp_p->version);
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Built v= version line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_owner (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    int          i;
+    char        *tmpptr;
+    sdp_result_e result;
+    char         tmp[SDP_MAX_STRING_LEN];
+
+    if (sdp_p->owner_name[0] != '\0') {
+        sdp_p->conf_p->num_invalid_token_order++;
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: More than one o= line specified.",
+            sdp_p->debug_str);
+    }
+
+    /* Find the owner name. */
+    ptr = sdp_getnextstrtok(ptr, sdp_p->owner_name, sizeof(sdp_p->owner_name), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No owner name specified for o=.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the owner session id.  This is a numeric field but is
+     * stored as a string since it may be 64 bit.
+     */
+    ptr = sdp_getnextstrtok(ptr, sdp_p->owner_sessid, sizeof(sdp_p->owner_sessid), " \t", &result);
+    if (result == SDP_SUCCESS) {
+        /* Make sure the sessid is numeric, even though we store it as
+         * a string.
+         */
+        (void)sdp_getnextnumtok(sdp_p->owner_sessid,
+                                (const char **)&tmpptr, " \t",&result);
+    }
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Invalid owner session id specified for o=.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the owner version. */
+    ptr = sdp_getnextstrtok(ptr, sdp_p->owner_version, sizeof(sdp_p->owner_version), " \t", &result);
+    if (result == SDP_SUCCESS) {
+        /* Make sure the version is numeric, even though we store it as
+         * a string.
+         */
+        (void)sdp_getnextnumtok(sdp_p->owner_version,
+                                (const char **)&tmpptr," \t",&result);
+    }
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Invalid owner version specified for o=.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the owner network type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No owner network type specified for o=.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    sdp_p->owner_network_type = SDP_NT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_NETWORK_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_nettype[i].name,
+                        sdp_nettype[i].strlen) == 0) {
+            if (sdp_p->conf_p->nettype_supported[i] == TRUE) {
+                sdp_p->owner_network_type = (sdp_nettype_e)i;
+            }
+        }
+    }
+    if (sdp_p->owner_network_type == SDP_NT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Owner network type unsupported (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the owner address type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No owner address type specified for o=.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    sdp_p->owner_addr_type = SDP_AT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_ADDR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_addrtype[i].name,
+                        sdp_addrtype[i].strlen) == 0) {
+            if (sdp_p->conf_p->addrtype_supported[i] == TRUE) {
+                sdp_p->owner_addr_type = (sdp_addrtype_e)i;
+            }
+        }
+    }
+    if ((sdp_p->owner_addr_type == SDP_AT_UNSUPPORTED) &&
+        (sdp_p->owner_network_type != SDP_NT_ATM)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Owner address type unsupported (%s)",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the owner address. */
+    ptr = sdp_getnextstrtok(ptr, sdp_p->owner_addr, sizeof(sdp_p->owner_addr), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No owner address specified.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parse owner: name %s, session id %s, version %s",
+                  sdp_p->debug_str, sdp_p->owner_name, sdp_p->owner_sessid,
+                  sdp_p->owner_version);
+        SDP_PRINT("%s              network %s, address type %s, "
+                  "address %s", sdp_p->debug_str,
+                  sdp_get_network_name(sdp_p->owner_network_type),
+                  sdp_get_address_name(sdp_p->owner_addr_type),
+                  sdp_p->owner_addr);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_owner (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    if ((sdp_p->owner_name[0] == '\0') ||
+        (sdp_p->owner_network_type >= SDP_MAX_NETWORK_TYPES) ||
+        (sdp_p->owner_addr_type >= SDP_MAX_ADDR_TYPES) ||
+        (sdp_p->owner_addr[0] == '\0')) {
+
+        if((sdp_p->owner_network_type == SDP_NT_ATM) &&
+           (sdp_p->owner_addr_type == SDP_AT_INVALID)) {
+          flex_string_sprintf(fs, "o=%s %s %s %s - -\r\n",
+                    sdp_p->owner_name, sdp_p->owner_sessid,
+                    sdp_p->owner_version,
+                    sdp_get_network_name(sdp_p->owner_network_type));
+        }
+
+        if (sdp_p->conf_p->owner_reqd == TRUE) {
+            CSFLogError(logTag, "%s Invalid params for o= owner line, "
+                        "build failed.", sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        } else {
+            /* o= line is not required. */
+            return (SDP_SUCCESS);
+        }
+    }
+
+    flex_string_sprintf(fs, "o=%s %s %s %s %s %s\r\n",
+                    sdp_p->owner_name, sdp_p->owner_sessid,
+                    sdp_p->owner_version,
+                    sdp_get_network_name(sdp_p->owner_network_type),
+                    sdp_get_address_name(sdp_p->owner_addr_type),
+                    sdp_p->owner_addr);
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Built o= owner line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_sessname (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    int   str_len;
+    char *endptr;
+
+    if (sdp_p->sessname[0] != '\0') {
+        sdp_p->conf_p->num_invalid_token_order++;
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: More than one s= line specified.",
+            sdp_p->debug_str);
+    }
+
+    endptr = sdp_findchar(ptr, "\r\n");
+    if (ptr == endptr) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No session name specified.",
+            sdp_p->debug_str);
+    }
+    str_len = MIN(endptr - ptr, SDP_MAX_STRING_LEN);
+    sstrncpy(sdp_p->sessname, ptr, str_len+1);
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parse session name, %s",
+                  sdp_p->debug_str, sdp_p->sessname);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_sessname (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    if (sdp_p->sessname[0] == '\0') {
+        if (sdp_p->conf_p->session_name_reqd == TRUE) {
+            CSFLogError(logTag, "%s No param defined for s= session name line, "
+                        "build failed.", sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        } else {
+            /* s= line is not required. */
+            return (SDP_SUCCESS);
+        }
+    }
+
+    flex_string_sprintf(fs, "s=%s\r\n", sdp_p->sessname);
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Built s= session name line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+/* We don't want to store the session info, but we do want to validate
+ * that at most one i= line exists at each level and if the line exists
+ * there should be a parameter.
+ */
+sdp_result_e sdp_parse_sessinfo (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    char *endptr;
+    sdp_mca_t *mca_p;
+
+    if (level == SDP_SESSION_LEVEL) {
+        if (sdp_p->sessinfo_found == TRUE) {
+            sdp_p->conf_p->num_invalid_token_order++;
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: More than one i= line specified.",
+                sdp_p->debug_str);
+        }
+        sdp_p->sessinfo_found = TRUE;
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_FAILURE);
+        }
+        if (mca_p->sessinfo_found == TRUE) {
+            sdp_p->conf_p->num_invalid_token_order++;
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: More than one i= line specified"
+                " for media line %d.", sdp_p->debug_str, level);
+        }
+        mca_p->sessinfo_found = TRUE;
+    }
+
+    endptr = sdp_findchar(ptr, "\n");
+    if (ptr == endptr) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No session info specified.",
+            sdp_p->debug_str);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed session info line.", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_sessinfo (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    /* Build session info line not supported. */
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_uri (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    char *endptr;
+
+    if (sdp_p->uri_found == TRUE) {
+        sdp_p->conf_p->num_invalid_token_order++;
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: More than one u= line specified.",
+            sdp_p->debug_str);
+    }
+    sdp_p->uri_found = TRUE;
+
+    endptr = sdp_findchar(ptr, "\n");
+    if (ptr == endptr) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No URI info specified.", sdp_p->debug_str);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed URI line.", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_uri (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    /* Build URI line not supported. */
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_email (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    char *endptr;
+
+    endptr = sdp_findchar(ptr, "\n");
+    if (ptr == endptr) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No email info specified.", sdp_p->debug_str);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parse email line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_email (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    /* Build email line not supported. */
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_phonenum (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    char *endptr;
+
+    endptr = sdp_findchar(ptr, "\n");
+    if (ptr == endptr) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No phone number info specified.",
+            sdp_p->debug_str);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parse phone number line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_phonenum (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    /* Build phone number line not supported. */
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_connection (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    int           i;
+    const char   *slash_ptr;
+    sdp_result_e  result;
+    sdp_conn_t   *conn_p;
+    sdp_mca_t    *mca_p;
+    char          tmp[SDP_MAX_STRING_LEN];
+    char mcast_str[MCAST_STRING_LEN];
+    int  mcast_bits;
+    unsigned long strtoul_result;
+    char *strtoul_end;
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_FAILURE);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    /* See if the c= line is already defined at this level. We don't
+     * currently support multihoming and so we only support one c= at
+     * each level.
+     */
+    if (conn_p->nettype != SDP_NT_INVALID) {
+        sdp_p->conf_p->num_invalid_token_order++;
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s c= line specified twice at same level, "
+            "parse failed.", sdp_p->debug_str);
+        return (SDP_INVALID_TOKEN_ORDERING);
+    }
+
+    /* Find the connection network type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No connection network type specified for c=.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    conn_p->nettype = SDP_NT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_NETWORK_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_nettype[i].name,
+                        sdp_nettype[i].strlen) == 0) {
+            if (sdp_p->conf_p->nettype_supported[i] == TRUE) {
+                conn_p->nettype = (sdp_nettype_e)i;
+            }
+        }
+    }
+    if (conn_p->nettype == SDP_NT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Connection network type unsupported "
+            "(%s) for c=.", sdp_p->debug_str, tmp);
+    }
+
+    /* Find the connection address type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        if (conn_p->nettype == SDP_NT_ATM) {
+            /* If the nettype is ATM, addr type and addr are not reqd */
+            if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+                SDP_PRINT("%s Parse connection: network %s", sdp_p->debug_str,
+                          sdp_get_network_name(conn_p->nettype));
+            }
+            return (SDP_SUCCESS);
+        } else {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s No connection address type specified for "
+                "c=.", sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+    }
+    conn_p->addrtype = SDP_AT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_ADDR_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_addrtype[i].name,
+                        sdp_addrtype[i].strlen) == 0) {
+            if (sdp_p->conf_p->addrtype_supported[i] == TRUE) {
+                conn_p->addrtype = (sdp_addrtype_e)i;
+            }
+        }
+    }
+    if (conn_p->addrtype == SDP_AT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Connection address type unsupported "
+            "(%s) for c=.", sdp_p->debug_str, tmp);
+    }
+
+    /* Find the connection address. */
+    ptr = sdp_getnextstrtok(ptr, conn_p->conn_addr, sizeof(conn_p->conn_addr), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No connection address specified for c=.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    /* We currently only support addrs containing '/'s for EPN addrs.
+     * For other addrs this would indicate multicast addrs. */
+    /* Multicast host group addresses are defined to be the IP addresses
+     * whose high-order four bits are 1110, giving an address range from
+     * 224.0.0.0 through 239.255.255.255
+     */
+    /* multicast addr check */
+    sstrncpy (mcast_str, conn_p->conn_addr, MCAST_STRING_LEN);
+
+    errno = 0;
+    strtoul_result = strtoul(mcast_str, &strtoul_end, 10);
+
+    if (errno || mcast_str == strtoul_end || strtoul_result > 255) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Error parsing address %s for mcast.",
+            sdp_p->debug_str, mcast_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return SDP_INVALID_PARAMETER;
+    }
+
+
+    mcast_bits = (int) strtoul_result;
+    if ((mcast_bits >= SDP_MIN_MCAST_ADDR_HI_BIT_VAL ) &&
+        (mcast_bits <= SDP_MAX_MCAST_ADDR_HI_BIT_VAL)) {
+        SDP_PRINT("%s Parsed to be a multicast address with mcast bits %d",
+                  sdp_p->debug_str, mcast_bits);
+        conn_p->is_multicast = TRUE;
+    }
+
+    if (conn_p->addrtype != SDP_AT_EPN) {
+        slash_ptr = sdp_findchar(conn_p->conn_addr, "/");
+        if (slash_ptr[0] != '\0') {
+            if (conn_p->is_multicast) {
+                SDP_PRINT("%s A multicast address with slash %s",
+                          sdp_p->debug_str, conn_p->conn_addr);
+                slash_ptr++;
+                slash_ptr = sdp_getnextstrtok(slash_ptr, tmp, sizeof(tmp), "/", &result);
+                if (result != SDP_SUCCESS) {
+                    sdp_parse_error(sdp_p->peerconnection,
+                        "%s No ttl value specified for this multicast addr with a slash",
+                        sdp_p->debug_str);
+                    sdp_p->conf_p->num_invalid_param++;
+                    return (SDP_INVALID_PARAMETER);
+                }
+
+                errno = 0;
+                strtoul_result = strtoul(tmp, &strtoul_end, 10);
+
+                if (errno || tmp == strtoul_end || conn_p->ttl > SDP_MAX_TTL_VALUE) {
+                    sdp_parse_error(sdp_p->peerconnection,
+                        "%s Invalid TTL: Value must be in the range 0-255 ",
+                        sdp_p->debug_str);
+                    sdp_p->conf_p->num_invalid_param++;
+                    return (SDP_INVALID_PARAMETER);
+                }
+
+                conn_p->ttl = (int) strtoul_result;
+
+                /* search for num of addresses */
+                /*sa_ignore NO_NULL_CHK
+                  {ptr is valid since the pointer was checked earlier and the
+                   function would have exited if NULL.}*/
+                slash_ptr = sdp_findchar(slash_ptr, "/");
+                if (slash_ptr != NULL &&
+                      slash_ptr[0] != '\0') {
+                    SDP_PRINT("%s Found a num addr field for multicast addr %s ",
+                              sdp_p->debug_str,slash_ptr);
+                    slash_ptr++;
+
+                    errno = 0;
+                    strtoul_result = strtoul(slash_ptr, &strtoul_end, 10);
+
+                    if (errno || slash_ptr == strtoul_end || strtoul_result == 0) {
+                        sdp_parse_error(sdp_p->peerconnection,
+                            "%s Invalid Num of addresses: Value must be > 0 ",
+                            sdp_p->debug_str);
+                        sdp_p->conf_p->num_invalid_param++;
+                        return SDP_INVALID_PARAMETER;
+                    }
+
+                    conn_p->num_of_addresses = (int) strtoul_result;
+                }
+               } else {
+                sdp_p->conf_p->num_invalid_param++;
+                SDP_PRINT("%s Only multicast addresses allowed with slashes",
+                          sdp_p->debug_str);
+                return (SDP_INVALID_PARAMETER);
+            }
+        }
+    }
+
+    /* See if the address is the choose param and if it's allowed. */
+    if ((sdp_p->conf_p->allow_choose[SDP_CHOOSE_CONN_ADDR] == FALSE) &&
+        (strcmp(conn_p->conn_addr, "$") == 0)) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Choose parameter for connection "
+            "address specified but not allowed.", sdp_p->debug_str);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parse connection: network %s, address type %s, "
+                  "address %s ttl= %d num of addresses = %d",
+                  sdp_p->debug_str,
+                  sdp_get_network_name(conn_p->nettype),
+                  sdp_get_address_name(conn_p->addrtype),
+                  conn_p->conn_addr, conn_p->ttl, conn_p->num_of_addresses);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_connection (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    sdp_mca_t  *mca_p;
+    sdp_conn_t *conn_p;
+
+    if (level == SDP_SESSION_LEVEL) {
+        conn_p = &(sdp_p->default_conn);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_FAILURE);
+        }
+        conn_p = &(mca_p->conn);
+    }
+
+    if((conn_p->nettype == SDP_NT_ATM ) &&
+       (conn_p->addrtype == SDP_AT_INVALID)) {
+        /*allow c= line to be built without address type and address fields
+         * This is a special case for ATM PVC*/
+        flex_string_sprintf(fs, "c=%s\r\n",
+                    sdp_get_network_name(conn_p->nettype));
+        return SDP_SUCCESS;
+    }
+    if ((conn_p->nettype >= SDP_MAX_NETWORK_TYPES) ||
+        (conn_p->addrtype >= SDP_MAX_ADDR_TYPES) ||
+        (conn_p->conn_addr[0] == '\0')) {
+        /* Connection info isn't set - don't need to build the token. */
+        return (SDP_SUCCESS);
+    }
+
+    if (conn_p->is_multicast) {
+        if (conn_p->num_of_addresses > 1) {
+            flex_string_sprintf(fs, "c=%s %s %s/%d/%d\r\n",
+                             sdp_get_network_name(conn_p->nettype),
+                             sdp_get_address_name(conn_p->addrtype),
+                             conn_p->conn_addr, conn_p->ttl,
+                             conn_p->num_of_addresses);
+        } else {
+            flex_string_sprintf(fs, "c=%s %s %s/%d\r\n",
+                             sdp_get_network_name(conn_p->nettype),
+                             sdp_get_address_name(conn_p->addrtype),
+                             conn_p->conn_addr, conn_p->ttl);
+        }
+    } else {
+
+        flex_string_sprintf(fs, "c=%s %s %s\r\n",
+                         sdp_get_network_name(conn_p->nettype),
+                         sdp_get_address_name(conn_p->addrtype),
+                         conn_p->conn_addr);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Built c= connection line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+/*
+ * sdp_parse_bandwidth
+ *
+ * This function parses a bandwidth field. The parsing is done in accordance
+ * to the following ABNF:
+ *
+ * bandwidth-fields =    *("b=" bwtype ":" bandwidth CRLF)
+ * bwtype =              1*(alpha-numeric)
+ * bandwidth =           1*(DIGIT)
+ *
+ * It currently supports three types of valid bwtypes - AS, CT and TIAS
+ */
+sdp_result_e sdp_parse_bandwidth (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    int                  i;
+    sdp_mca_t            *mca_p;
+    sdp_bw_t             *bw_p;
+    sdp_bw_data_t        *bw_data_p;
+    sdp_bw_data_t        *new_bw_data_p;
+    sdp_result_e         result;
+    char                 tmp[SDP_MAX_STRING_LEN];
+    sdp_bw_modifier_e    bw_modifier = SDP_BW_MODIFIER_UNSUPPORTED;
+    int                  bw_val = 0;
+
+    if (level == SDP_SESSION_LEVEL) {
+        bw_p = &(sdp_p->bw);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_FAILURE);
+        }
+        bw_p = &(mca_p->bw);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parse bandwidth line", sdp_p->debug_str);
+    }
+
+    /* Find the bw type (AS, CT or TIAS) */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), ":", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No bandwidth type specified for b= ",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    for (i=0; i < SDP_MAX_BW_MODIFIER_VAL; i++) {
+        if (cpr_strncasecmp(tmp, sdp_bw_modifier_val[i].name,
+                        sdp_bw_modifier_val[i].strlen) == 0) {
+            bw_modifier  = (sdp_bw_modifier_e)i;
+            break;
+        }
+    }
+
+    if (bw_modifier == SDP_BW_MODIFIER_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Error: BW Modifier type unsupported (%s).",
+            sdp_p->debug_str, tmp);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find the BW type value */
+    /*sa_ignore NO_NULL_CHK
+      {ptr is valid since the pointer was checked earlier and the
+       function would have exited if NULL.}*/
+    if (*ptr == ':') {
+        ptr++;
+        bw_val = sdp_getnextnumtok(ptr, &ptr, " \t", &result);
+        if ((result != SDP_SUCCESS)) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Error: No BW Value specified ",
+                sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+    }
+
+    /*
+     * Allocate a new sdp_bw_data_t instance and set it's values from the
+     * input parameters.
+     */
+    new_bw_data_p = (sdp_bw_data_t*)SDP_MALLOC(sizeof(sdp_bw_data_t));
+    if (new_bw_data_p == NULL) {
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_NO_RESOURCE);
+    }
+    new_bw_data_p->next_p = NULL;
+    new_bw_data_p->bw_modifier = bw_modifier;
+    new_bw_data_p->bw_val = bw_val;
+
+    /*
+     * Enqueue the sdp_bw_data_t instance at the end of the list of
+     * sdp_bw_data_t instances.
+     */
+    if (bw_p->bw_data_list == NULL) {
+        bw_p->bw_data_list = new_bw_data_p;
+    } else {
+        for (bw_data_p = bw_p->bw_data_list;
+             bw_data_p->next_p != NULL;
+             bw_data_p = bw_data_p->next_p) {
+            ; // Empty For
+        }
+        bw_data_p->next_p = new_bw_data_p;
+    }
+    bw_p->bw_data_count++;
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed bw type %s, value %d", sdp_p->debug_str,
+                     sdp_get_bw_modifier_name(new_bw_data_p->bw_modifier),
+                    new_bw_data_p->bw_val);
+    }
+
+    return (SDP_SUCCESS);
+}
+
+/*
+ * sdp_build_bandwidth
+ *
+ * Builds *all* the bandwith lines for the specified level.
+ */
+sdp_result_e sdp_build_bandwidth (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    sdp_bw_t            *bw_p;
+    sdp_bw_data_t       *bw_data_p;
+    sdp_mca_t           *mca_p;
+
+    if (level == SDP_SESSION_LEVEL) {
+        bw_p = &(sdp_p->bw);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_FAILURE);
+        }
+        bw_p = &(mca_p->bw);
+    }
+
+    bw_data_p = bw_p->bw_data_list;
+    while (bw_data_p) {
+        flex_string_sprintf(fs, "b=%s:%d\r\n",
+                         sdp_get_bw_modifier_name(bw_data_p->bw_modifier),
+                         bw_data_p->bw_val);
+
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+           SDP_PRINT("%s Built b=%s:%d bandwidth line", sdp_p->debug_str,
+                     sdp_get_bw_modifier_name(bw_data_p->bw_modifier),
+                     bw_data_p->bw_val);
+        }
+
+        bw_data_p = bw_data_p->next_p;
+    }
+
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_timespec (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    char            *tmpptr;
+    sdp_result_e     result;
+    sdp_timespec_t  *timespec_p;
+    sdp_timespec_t  *next_timespec_p;
+
+    timespec_p = (sdp_timespec_t *)SDP_MALLOC(sizeof(sdp_timespec_t));
+    if (timespec_p == NULL) {
+        sdp_p->conf_p->num_no_resource++;
+        return (SDP_NO_RESOURCE);
+    }
+
+    /* Validate start and stop times. */
+    ptr = sdp_getnextstrtok(ptr, timespec_p->start_time, sizeof(timespec_p->start_time), " \t", &result);
+    if (result == SDP_SUCCESS) {
+        /* Make sure the start_time is numeric, even though we store it as
+         * a string.
+         */
+        (void)sdp_getnextnumtok(timespec_p->start_time,
+                                (const char **)&tmpptr, " \t", &result);
+    }
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Invalid timespec start time specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        SDP_FREE(timespec_p);
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    ptr = sdp_getnextstrtok(ptr, timespec_p->stop_time, sizeof(timespec_p->stop_time), " \t", &result);
+    if (result == SDP_SUCCESS) {
+        /* Make sure the start_time is numeric, even though we store it as
+         * a string.
+         */
+        (void)sdp_getnextnumtok(timespec_p->stop_time,
+                                (const char **)&tmpptr, " \t", &result);
+    }
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Invalid timespec stop time specified.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        SDP_FREE(timespec_p);
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Link the new timespec in to the end of the list. */
+    if (sdp_p->timespec_p == NULL) {
+        sdp_p->timespec_p = timespec_p;
+    } else {
+        next_timespec_p = sdp_p->timespec_p;
+        while (next_timespec_p->next_p != NULL) {
+            next_timespec_p = next_timespec_p->next_p;
+        }
+        next_timespec_p->next_p = timespec_p;
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed timespec line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_timespec (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    if ((sdp_p->timespec_p == NULL) ||
+        (sdp_p->timespec_p->start_time == '\0') ||
+        (sdp_p->timespec_p->stop_time == '\0')) {
+        if (sdp_p->conf_p->timespec_reqd == TRUE) {
+            CSFLogError(logTag, "%s Invalid params for t= time spec line, "
+                        "build failed.", sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        } else {
+            /* t= line not required. */
+            return (SDP_SUCCESS);
+        }
+    }
+
+    /* Note: We only support one t= line currently. */
+    flex_string_sprintf(fs, "t=%s %s\r\n", sdp_p->timespec_p->start_time,
+                    sdp_p->timespec_p->stop_time);
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Built t= timespec line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_repeat_time (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    char *endptr;
+
+    endptr = sdp_findchar(ptr, "\n");
+    if (ptr == endptr) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No repeat time parameters "
+            "specified.", sdp_p->debug_str);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed repeat time line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_repeat_time (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    /* Build repeat time line not supported. */
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_timezone_adj (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    char *endptr;
+
+    endptr = sdp_findchar(ptr, "\n");
+    if (ptr == endptr) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No timezone parameters specified.",
+            sdp_p->debug_str);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parse timezone adustment line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_timezone_adj (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    /* Build timezone adjustment line not supported. */
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_encryption (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    int                  i;
+    sdp_result_e         result;
+    sdp_encryptspec_t   *encrypt_p;
+    sdp_mca_t           *mca_p;
+    char                 tmp[SDP_MAX_STRING_LEN];
+
+    if (level == SDP_SESSION_LEVEL) {
+        encrypt_p = &(sdp_p->encrypt);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_FAILURE);
+        }
+        encrypt_p = &(mca_p->encrypt);
+    }
+    encrypt_p->encrypt_key[0] = '\0';
+
+    /* Find the encryption type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), ":", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No encryption type specified for k=.",
+            sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    encrypt_p->encrypt_type = SDP_ENCRYPT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_ENCRYPT_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_encrypt[i].name,
+                        sdp_encrypt[i].strlen) == 0) {
+            encrypt_p->encrypt_type = (sdp_encrypt_type_e)i;
+            break;
+        }
+    }
+    if (encrypt_p->encrypt_type == SDP_ENCRYPT_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Encryption type unsupported (%s).",
+            sdp_p->debug_str, tmp);
+    }
+
+    /* Find the encryption key. */
+    encrypt_p->encrypt_key[0] = '\0';
+    /*sa_ignore NO_NULL_CHK
+      {ptr is valid since the pointer was checked earlier and the
+       function would have exited if NULL.}*/
+    if (*ptr == ':')
+        ptr++;
+    if (encrypt_p->encrypt_type != SDP_ENCRYPT_PROMPT) {
+        ptr = sdp_getnextstrtok(ptr, encrypt_p->encrypt_key, sizeof(encrypt_p->encrypt_key), " \t", &result);
+        if ((result != SDP_SUCCESS) &&
+            ((encrypt_p->encrypt_type == SDP_ENCRYPT_CLEAR) ||
+             (encrypt_p->encrypt_type == SDP_ENCRYPT_BASE64) ||
+             (encrypt_p->encrypt_type == SDP_ENCRYPT_URI))) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: No encryption key specified "
+                "as required.", sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parse encryption type %s, key %s", sdp_p->debug_str,
+                   sdp_get_encrypt_name(encrypt_p->encrypt_type),
+                   encrypt_p->encrypt_key);
+    }
+    return (SDP_SUCCESS);
+}
+
+/* If the encryption info is valid, we build it.  Else skip it. */
+sdp_result_e sdp_build_encryption (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    sdp_encryptspec_t   *encrypt_p;
+    sdp_mca_t           *mca_p;
+
+    if (level == SDP_SESSION_LEVEL) {
+        encrypt_p = &(sdp_p->encrypt);
+    } else {
+        mca_p = sdp_find_media_level(sdp_p, level);
+        if (mca_p == NULL) {
+            return (SDP_FAILURE);
+        }
+        encrypt_p = &(mca_p->encrypt);
+    }
+
+    if ((encrypt_p->encrypt_type >= SDP_MAX_ENCRYPT_TYPES) ||
+        ((encrypt_p->encrypt_type != SDP_ENCRYPT_PROMPT) &&
+         (encrypt_p->encrypt_key[0] == '\0'))) {
+        /* Encryption info isn't set - don't need to build the token. */
+        return (SDP_SUCCESS);
+    }
+
+    flex_string_sprintf(fs, "k=%s",
+                     sdp_get_encrypt_name(encrypt_p->encrypt_type));
+
+    if (encrypt_p->encrypt_type == SDP_ENCRYPT_PROMPT) {
+        /* There is no key to print. */
+        flex_string_sprintf(fs, "\r\n");
+    } else {
+        flex_string_sprintf(fs, ":%s\r\n", encrypt_p->encrypt_key);
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Built k= encryption line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_parse_media (sdp_t *sdp_p, u16 level, const char *ptr)
+{
+    u16                   i;
+    u16                   num_port_params=0;
+    int32                 num[SDP_MAX_PORT_PARAMS];
+    tinybool              valid_param = FALSE;
+    sdp_result_e          result;
+    sdp_mca_t            *mca_p;
+    sdp_mca_t            *next_mca_p;
+    char                  tmp[SDP_MAX_STRING_LEN];
+    char                  port[SDP_MAX_STRING_LEN];
+    const char           *port_ptr;
+    int32                 sctp_port;
+
+    /* Allocate resource for new media stream. */
+    mca_p = sdp_alloc_mca();
+    if (mca_p == NULL) {
+        sdp_p->conf_p->num_no_resource++;
+        return (SDP_NO_RESOURCE);
+    }
+
+    /* Find the media type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No media type specified, parse failed.",
+            sdp_p->debug_str);
+        SDP_FREE(mca_p);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    mca_p->media = SDP_MEDIA_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_MEDIA_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_media[i].name,
+                        sdp_media[i].strlen) == 0) {
+            mca_p->media = (sdp_media_e)i;
+        }
+    }
+    if (mca_p->media == SDP_MEDIA_UNSUPPORTED) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Media type unsupported (%s).",
+            sdp_p->debug_str, tmp);
+    }
+
+    /* Find the port token parameters, but don't process it until
+     * we determine the transport protocol as that determines what
+     * port number formats are valid.
+     */
+    ptr = sdp_getnextstrtok(ptr, port, sizeof(port), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No port specified in m= media line, "
+            "parse failed.", sdp_p->debug_str);
+        SDP_FREE(mca_p);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    port_ptr = port;
+    for (i=0; i < SDP_MAX_PORT_PARAMS; i++) {
+        if (sdp_getchoosetok(port_ptr, &port_ptr, "/ \t", &result) == TRUE) {
+            num[i] = SDP_CHOOSE_PARAM;
+        } else {
+            num[i] = sdp_getnextnumtok(port_ptr, (const char **)&port_ptr,
+                                       "/ \t", &result);
+            if (result != SDP_SUCCESS) {
+                break;
+            }
+        }
+        num_port_params++;
+    }
+
+    /* Find the transport protocol type. */
+    ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s No transport protocol type specified, "
+            "parse failed.", sdp_p->debug_str);
+        SDP_FREE(mca_p);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+    mca_p->transport = SDP_TRANSPORT_UNSUPPORTED;
+    for (i=0; i < SDP_MAX_TRANSPORT_TYPES; i++) {
+        if (cpr_strncasecmp(tmp, sdp_transport[i].name,
+                        sdp_transport[i].strlen) == 0) {
+            mca_p->transport = (sdp_transport_e)i;
+            break;
+        }
+    }
+    if (mca_p->transport == SDP_TRANSPORT_UNSUPPORTED) {
+        /* If we don't recognize or don't support the transport type,
+         * just store the first num as the port.
+         */
+        mca_p->port = num[0];
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Transport protocol type unsupported "
+            "(%s).", sdp_p->debug_str, tmp);
+    }
+
+    /* Check for each of the possible port formats according to the
+     * type of transport protocol specified.
+     */
+    valid_param = FALSE;
+    switch (num_port_params) {
+    case 1:
+        if ((mca_p->transport == SDP_TRANSPORT_RTPAVP) ||
+           (mca_p->transport == SDP_TRANSPORT_RTPSAVP) ||
+           (mca_p->transport == SDP_TRANSPORT_RTPSAVPF) ||
+            (mca_p->transport == SDP_TRANSPORT_UDP) ||
+            (mca_p->transport == SDP_TRANSPORT_TCP) ||
+            (mca_p->transport == SDP_TRANSPORT_UDPTL) ||
+            (mca_p->transport == SDP_TRANSPORT_UDPSPRT) ||
+            (mca_p->transport == SDP_TRANSPORT_LOCAL) ||
+            (mca_p->transport == SDP_TRANSPORT_SCTPDTLS)) {
+            /* Port format is simply <port>.  Make sure that either
+             * the choose param is allowed or that the choose value
+             * wasn't specified.
+             */
+            if ((sdp_p->conf_p->allow_choose[SDP_CHOOSE_PORTNUM]) ||
+                (num[0] != SDP_CHOOSE_PARAM)) {
+                mca_p->port        = num[0];
+                mca_p->port_format = SDP_PORT_NUM_ONLY;
+                valid_param        = TRUE;
+            }
+        } else if (mca_p->transport == SDP_TRANSPORT_AAL1AVP) {
+            /* Port format is simply <vcci>, choose param is not allowed.
+             */
+            if (num[0] != SDP_CHOOSE_PARAM) {
+                mca_p->vcci        = num[0];
+                mca_p->port_format = SDP_PORT_VCCI;
+                valid_param        = TRUE;
+            }
+        } else if ((mca_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
+            (mca_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
+            (mca_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) {
+            /* Port format is simply <port>, and choose param is allowed,
+             * according to AAL2 definitions.
+             */
+            mca_p->port        = num[0];
+            mca_p->port_format = SDP_PORT_NUM_ONLY;
+            valid_param        = TRUE;
+        }
+        break;
+    case 2:
+        if ((mca_p->transport == SDP_TRANSPORT_RTPAVP) ||
+           (mca_p->transport == SDP_TRANSPORT_RTPSAVP) ||
+           (mca_p->transport == SDP_TRANSPORT_RTPSAVPF) ||
+            (mca_p->transport == SDP_TRANSPORT_UDP) ||
+            (mca_p->transport == SDP_TRANSPORT_LOCAL)) {
+            /* Port format is <port>/<num of ports>. Make sure choose
+             * params were not specified.
+             */
+            if ((num[0] != SDP_CHOOSE_PARAM) &&
+                (num[1] != SDP_CHOOSE_PARAM)) {
+                mca_p->port        = num[0];
+                mca_p->num_ports   = num[1];
+                mca_p->port_format = SDP_PORT_NUM_COUNT;
+                valid_param        = TRUE;
+            }
+        } else if (mca_p->transport == SDP_TRANSPORT_UDPTL) {
+            /* Port format is <port>/<num of ports>. Make sure choose
+             * params were not specified.  For UDPTL, only "1" may
+             * be specified for number of ports.
+             */
+            if ((num[0] != SDP_CHOOSE_PARAM) &&
+                (num[1] == 1)) {
+                mca_p->port        = num[0];
+                mca_p->num_ports   = 1;
+                mca_p->port_format = SDP_PORT_NUM_COUNT;
+                valid_param        = TRUE;
+            }
+        } else if (mca_p->transport == SDP_TRANSPORT_CES10) {
+            /* Port format is <vpi>/<vci>. Make sure choose
+             * params were not specified.
+             */
+            if ((num[0] != SDP_CHOOSE_PARAM) &&
+                (num[1] != SDP_CHOOSE_PARAM)) {
+                mca_p->vpi         = num[0];
+                mca_p->vci         = num[1];
+                mca_p->port_format = SDP_PORT_VPI_VCI;
+                valid_param        = TRUE;
+            }
+        } else if ((mca_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
+                   (mca_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
+                   (mca_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) {
+            /* Port format is either <vcci>/<cid> or $/$.  If one
+             * param is '$' the other must be also.  The choose params
+             * are allowed by default and don't need to be allowed
+             * through the appl config.
+             */
+            if (((num[0] != SDP_CHOOSE_PARAM) &&
+                 (num[1] != SDP_CHOOSE_PARAM)) ||
+                ((num[0] == SDP_CHOOSE_PARAM) &&
+                 (num[1] == SDP_CHOOSE_PARAM))) {
+                mca_p->vcci        = num[0];
+                mca_p->cid         = num[1];
+                mca_p->port_format = SDP_PORT_VCCI_CID;
+                valid_param        = TRUE;
+            }
+        }
+        break;
+    case 3:
+        if (mca_p->transport == SDP_TRANSPORT_AAL1AVP) {
+            /* Port format is <port>/<vpi>/<vci>. Make sure choose
+             * params were not specified.
+             */
+            if ((num[0] != SDP_CHOOSE_PARAM) &&
+                (num[1] != SDP_CHOOSE_PARAM) &&
+                (num[2] != SDP_CHOOSE_PARAM)) {
+                mca_p->port        = num[0];
+                mca_p->vpi         = num[1];
+                mca_p->vci         = num[2];
+                mca_p->port_format = SDP_PORT_NUM_VPI_VCI;
+                valid_param        = TRUE;
+            }
+        }
+        break;
+    case 4:
+        if ((mca_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
+            (mca_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
+            (mca_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) {
+            /* Port format is <port>/<vpi>/<vci>/<cid>. Make sure choose
+             * params were not specified.
+             */
+            if ((num[0] != SDP_CHOOSE_PARAM) &&
+                (num[1] != SDP_CHOOSE_PARAM) &&
+                (num[2] != SDP_CHOOSE_PARAM) &&
+                (num[3] != SDP_CHOOSE_PARAM)) {
+                mca_p->port        = num[0];
+                mca_p->vpi         = num[1];
+                mca_p->vci         = num[2];
+                mca_p->cid         = num[3];
+                mca_p->port_format = SDP_PORT_NUM_VPI_VCI_CID;
+                valid_param        = TRUE;
+            }
+        }
+        break;
+    }
+    if (valid_param == FALSE) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Invalid port format (%s) specified for transport "
+            "protocol (%s), parse failed.", sdp_p->debug_str,
+            port, sdp_get_transport_name(mca_p->transport));
+        sdp_p->conf_p->num_invalid_param++;
+        SDP_FREE(mca_p);
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Find payload formats. AAL2 media lines allow multiple
+     * transport/profile types per line, so these are handled differently. */
+    if ((mca_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
+        (mca_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
+        (mca_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) {
+
+        if (sdp_parse_multiple_profile_payload_types(sdp_p, mca_p, ptr) !=
+            SDP_SUCCESS) {
+            sdp_p->conf_p->num_invalid_param++;
+           SDP_FREE(mca_p);
+            return (SDP_INVALID_PARAMETER);
+        }
+    } else {
+        /* Transport is a non-AAL2 type.  Parse payloads normally. */
+        sdp_parse_payload_types(sdp_p, mca_p, ptr);
+    }
+
+    /* Parse SCTP/DTLS port */
+    if (mca_p->transport == SDP_TRANSPORT_SCTPDTLS) {
+        ptr = sdp_getnextstrtok(ptr, port, sizeof(port), " \t", &result);
+        if (result != SDP_SUCCESS) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s No sctp port specified in m= media line, "
+                "parse failed.", sdp_p->debug_str);
+            SDP_FREE(mca_p);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+        port_ptr = port;
+
+        if (sdp_getchoosetok(port_ptr, &port_ptr, "/ \t", &result)) {
+               sctp_port = SDP_CHOOSE_PARAM;
+        } else {
+               sctp_port = sdp_getnextnumtok(port_ptr, (const char **)&port_ptr,
+                                           "/ \t", &result);
+            if (result != SDP_SUCCESS) {
+               return (SDP_INVALID_PARAMETER);
+            }
+            mca_p->sctpport = sctp_port;
+        }
+    }
+
+    /* Media line params are valid.  Add it into the SDP. */
+    sdp_p->mca_count++;
+    if (sdp_p->mca_p == NULL) {
+        sdp_p->mca_p = mca_p;
+    } else {
+        for (next_mca_p = sdp_p->mca_p; next_mca_p->next_p != NULL;
+             next_mca_p = next_mca_p->next_p) {
+            ; // Empty For
+        }
+        next_mca_p->next_p = mca_p;
+    }
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+
+        SDP_PRINT("%s Parsed media type %s, ", sdp_p->debug_str,
+                  sdp_get_media_name(mca_p->media));
+        switch (mca_p->port_format) {
+        case SDP_PORT_NUM_ONLY:
+            SDP_PRINT("Port num %ld, ", mca_p->port);
+            break;
+
+        case SDP_PORT_NUM_COUNT:
+            SDP_PRINT("Port num %ld, count %ld, ",
+                      mca_p->port, mca_p->num_ports);
+            break;
+        case SDP_PORT_VPI_VCI:
+            SDP_PRINT("VPI/VCI %ld/%lu, ", mca_p->vpi, mca_p->vci);
+            break;
+        case SDP_PORT_VCCI:
+            SDP_PRINT("VCCI %ld, ", mca_p->vcci);
+            break;
+        case SDP_PORT_NUM_VPI_VCI:
+            SDP_PRINT("Port %ld, VPI/VCI %ld/%lu, ", mca_p->port,
+                      mca_p->vpi, mca_p->vci);
+            break;
+        case SDP_PORT_VCCI_CID:
+            SDP_PRINT("VCCI %ld, CID %ld, ", mca_p->vcci, mca_p->cid);
+            break;
+        case SDP_PORT_NUM_VPI_VCI_CID:
+            SDP_PRINT("Port %ld, VPI/VCI %ld/%lu, CID %ld, ", mca_p->port,
+                      mca_p->vpi, mca_p->vci, mca_p->cid);
+            break;
+        default:
+            SDP_PRINT("Port format not valid, ");
+            break;
+        }
+
+        if ((mca_p->transport >= SDP_TRANSPORT_AAL2_ITU) &&
+            (mca_p->transport <= SDP_TRANSPORT_AAL2_CUSTOM)) {
+            for (i=0; i < mca_p->media_profiles_p->num_profiles; i++) {
+                SDP_PRINT("Profile %s, Num payloads %u ",
+                   sdp_get_transport_name(mca_p->media_profiles_p->profile[i]),
+                   mca_p->media_profiles_p->num_payloads[i]);
+            }
+        } else {
+            SDP_PRINT("Transport %s, Num payloads %u",
+                      sdp_get_transport_name(mca_p->transport),
+                      mca_p->num_payloads);
+        }
+    }
+    return (SDP_SUCCESS);
+}
+
+sdp_result_e sdp_build_media (sdp_t *sdp_p, u16 level, flex_string *fs)
+{
+    int                   i, j;
+    sdp_mca_t            *mca_p;
+    tinybool              invalid_params=FALSE;
+    sdp_media_profiles_t *profile_p;
+
+    /* Find the right media line */
+    mca_p = sdp_find_media_level(sdp_p, level);
+    if (mca_p == NULL) {
+        return (SDP_FAILURE);
+    }
+
+    /* Validate params for this media line */
+    if ((mca_p->media >= SDP_MAX_MEDIA_TYPES) ||
+        (mca_p->port_format >= SDP_MAX_PORT_FORMAT_TYPES) ||
+        (mca_p->transport >= SDP_MAX_TRANSPORT_TYPES)) {
+        invalid_params = TRUE;
+    }
+
+    if (invalid_params == TRUE) {
+        CSFLogError(logTag, "%s Invalid params for m= media line, "
+                    "build failed.", sdp_p->debug_str);
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    /* Build the media type */
+    flex_string_sprintf(fs, "m=%s ", sdp_get_media_name(mca_p->media));
+
+    /* Build the port based on the specified port format */
+    if (mca_p->port_format == SDP_PORT_NUM_ONLY) {
+        if (mca_p->port == SDP_CHOOSE_PARAM) {
+            flex_string_sprintf(fs, "$ ");
+        } else {
+            flex_string_sprintf(fs, "%u ", (u16)mca_p->port);
+        }
+    } else if (mca_p->port_format == SDP_PORT_NUM_COUNT) {
+        flex_string_sprintf(fs, "%u/%u ", (u16)mca_p->port,
+                        (u16)mca_p->num_ports);
+    } else if (mca_p->port_format == SDP_PORT_VPI_VCI) {
+        flex_string_sprintf(fs, "%u/%u ",
+                         (u16)mca_p->vpi, (u16)mca_p->vci);
+    } else if (mca_p->port_format == SDP_PORT_VCCI) {
+        flex_string_sprintf(fs, "%u ", (u16)mca_p->vcci);
+    } else if (mca_p->port_format == SDP_PORT_NUM_VPI_VCI) {
+        flex_string_sprintf(fs, "%u/%u/%u ", (u16)mca_p->port,
+                         (u16)mca_p->vpi, (u16)mca_p->vci);
+    } else if (mca_p->port_format == SDP_PORT_VCCI_CID) {
+        if ((mca_p->vcci == SDP_CHOOSE_PARAM) &&
+            (mca_p->cid == SDP_CHOOSE_PARAM)) {
+            flex_string_sprintf(fs, "$/$ ");
+        } else if ((mca_p->vcci == SDP_CHOOSE_PARAM) ||
+                   (mca_p->cid == SDP_CHOOSE_PARAM)) {
+            /* If one is set but not the other, this is an error. */
+            CSFLogError(logTag, "%s Invalid params for m= port parameter, "
+                        "build failed.", sdp_p->debug_str);
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        } else {
+            flex_string_sprintf(fs, "%u/%u ",
+                             (u16)mca_p->vcci, (u16)mca_p->cid);
+        }
+    } else if (mca_p->port_format == SDP_PORT_NUM_VPI_VCI_CID) {
+        flex_string_sprintf(fs, "%u/%u/%u/%u ", (u16)mca_p->port,
+                        (u16)mca_p->vpi, (u16)mca_p->vci, (u16)mca_p->cid);
+    }
+
+    /* If the media line has AAL2 profiles, build them differently. */
+    if ((mca_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
+        (mca_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
+        (mca_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) {
+        profile_p = mca_p->media_profiles_p;
+        for (i=0; i < profile_p->num_profiles; i++) {
+            flex_string_sprintf(fs, "%s",
+                             sdp_get_transport_name(profile_p->profile[i]));
+
+            for (j=0; j < profile_p->num_payloads[i]; j++) {
+                flex_string_sprintf(fs, " %u",
+                                 profile_p->payload_type[i][j]);
+            }
+            flex_string_sprintf(fs, " ");
+        }
+        flex_string_sprintf(fs, "\n");
+        if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+            SDP_PRINT("%s Built m= media line", sdp_p->debug_str);
+        }
+        return (SDP_SUCCESS);
+    }
+
+    /* Build the transport name */
+    flex_string_sprintf(fs, "%s",
+                     sdp_get_transport_name(mca_p->transport));
+
+    if(mca_p->transport != SDP_TRANSPORT_SCTPDTLS) {
+
+        /* Build the format lists */
+        for (i=0; i < mca_p->num_payloads; i++) {
+            if (mca_p->payload_indicator[i] == SDP_PAYLOAD_ENUM) {
+                flex_string_sprintf(fs, " %s",
+                                 sdp_get_payload_name((sdp_payload_e)mca_p->payload_type[i]));
+            } else {
+                flex_string_sprintf(fs, " %u", mca_p->payload_type[i]);
+            }
+        }
+    } else {
+        /* Add port to SDP if transport is SCTP/DTLS */
+       flex_string_sprintf(fs, " %u ", (u32)mca_p->sctpport);
+    }
+
+    flex_string_sprintf(fs, "\r\n");
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Built m= media line", sdp_p->debug_str);
+    }
+    return (SDP_SUCCESS);
+}
+
+
+/* Function:    sdp_parse_payload_types
+ * Description: Parse a list of payload types.  The list may be part of
+ *              a media line or part of a capability line.
+ * Parameters:  sdp_ptr      The SDP handle returned by sdp_init_description.
+ *              mca_p        The mca structure the payload types should be
+ *                           added to.
+ *              ptr          The pointer to the list of payloads.
+ * Returns:     Nothing.
+ */
+void sdp_parse_payload_types (sdp_t *sdp_p, sdp_mca_t *mca_p, const char *ptr)
+{
+    u16           i;
+    u16           num_payloads;
+    sdp_result_e  result;
+    tinybool      valid_payload;
+    char          tmp[SDP_MAX_STRING_LEN];
+    char         *tmp2;
+
+    for (num_payloads = 0; (num_payloads < SDP_MAX_PAYLOAD_TYPES); ) {
+        ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+        if (result != SDP_SUCCESS) {
+            /* If there are no more payload types, we're finished */
+            break;
+        }
+        mca_p->payload_type[num_payloads] = (u16)sdp_getnextnumtok(tmp,
+                                                        (const char **)&tmp2,
+                                                        " \t", &result);
+        if (result == SDP_SUCCESS) {
+            if ((mca_p->media == SDP_MEDIA_IMAGE) &&
+                (mca_p->transport == SDP_TRANSPORT_UDPTL)) {
+                sdp_parse_error(sdp_p->peerconnection,
+                    "%s Warning: Numeric payload type not "
+                    "valid for media %s with transport %s.",
+                    sdp_p->debug_str,
+                    sdp_get_media_name(mca_p->media),
+                    sdp_get_transport_name(mca_p->transport));
+            } else {
+                mca_p->payload_indicator[num_payloads] = SDP_PAYLOAD_NUMERIC;
+                mca_p->num_payloads++;
+                num_payloads++;
+            }
+            continue;
+        }
+
+        valid_payload = FALSE;
+        for (i=0; i < SDP_MAX_STRING_PAYLOAD_TYPES; i++) {
+            if (cpr_strncasecmp(tmp, sdp_payload[i].name,
+                            sdp_payload[i].strlen) == 0) {
+                valid_payload = TRUE;
+                break;
+            }
+        }
+        if (valid_payload == TRUE) {
+            /* We recognized the payload type.  Make sure it
+             * is valid for this media line. */
+            valid_payload = FALSE;
+            if ((mca_p->media == SDP_MEDIA_IMAGE) &&
+                (mca_p->transport == SDP_TRANSPORT_UDPTL) &&
+                (i == SDP_PAYLOAD_T38)) {
+                valid_payload = TRUE;
+            } else if ((mca_p->media == SDP_MEDIA_APPLICATION) &&
+                       (mca_p->transport == SDP_TRANSPORT_UDP) &&
+                       (i == SDP_PAYLOAD_XTMR)) {
+                valid_payload = TRUE;
+            } else if ((mca_p->media == SDP_MEDIA_APPLICATION) &&
+                       (mca_p->transport == SDP_TRANSPORT_TCP) &&
+                       (i == SDP_PAYLOAD_T120)) {
+                valid_payload = TRUE;
+            }
+
+            if (valid_payload == TRUE) {
+                mca_p->payload_indicator[num_payloads] = SDP_PAYLOAD_ENUM;
+                mca_p->payload_type[num_payloads] = i;
+                mca_p->num_payloads++;
+                num_payloads++;
+            } else {
+                sdp_parse_error(sdp_p->peerconnection,
+                    "%s Warning: Payload type %s not valid for "
+                    "media %s with transport %s.",
+                    sdp_p->debug_str,
+                    sdp_get_payload_name((sdp_payload_e)i),
+                    sdp_get_media_name(mca_p->media),
+                    sdp_get_transport_name(mca_p->transport));
+            }
+        } else {
+            /* Payload type wasn't recognized. */
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: Payload type "
+                "unsupported (%s).", sdp_p->debug_str, tmp);
+        }
+    }
+    if (mca_p->num_payloads == 0) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No payload types specified.",
+            sdp_p->debug_str);
+    }
+}
+
+
+/* Function:    sdp_parse_multiple_profile_payload_types
+ * Description: Parse a list of payload types.  The list may be part of
+ *              a media line or part of a capability line.
+ * Parameters:  sdp_ptr      The SDP handle returned by sdp_init_description.
+ *              mca_p        The mca structure the payload types should be
+ *                           added to.
+ *              ptr          The pointer to the list of payloads.
+ * Returns:     Nothing.
+ */
+sdp_result_e sdp_parse_multiple_profile_payload_types (sdp_t *sdp_p,
+                                                 sdp_mca_t *mca_p,
+                                                 const char *ptr)
+{
+    u16                   i;
+    u16                   prof;
+    u16                   payload;
+    sdp_result_e          result;
+    sdp_media_profiles_t *profile_p;
+    char                  tmp[SDP_MAX_STRING_LEN];
+    char                 *tmp2;
+
+    /* If the transport type is any of the AAL2 formats, then we
+     * need to look for multiple AAL2 profiles and their associated
+     * payload lists. */
+    mca_p->media_profiles_p = (sdp_media_profiles_t *) \
+        SDP_MALLOC(sizeof(sdp_media_profiles_t));
+    if (mca_p->media_profiles_p == NULL) {
+        sdp_p->conf_p->num_no_resource++;
+        SDP_FREE(mca_p);
+        return (SDP_NO_RESOURCE);
+    }
+    profile_p = mca_p->media_profiles_p;
+    /* Set the first profile to the one already detected. */
+    profile_p->num_profiles = 1;
+    prof = 0;
+    payload = 0;
+    profile_p->profile[prof] = mca_p->transport;
+    profile_p->num_payloads[prof] = 0;
+
+    /* Now find the payload type lists and any other profile types */
+    while (TRUE) {
+        ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result);
+        if (result != SDP_SUCCESS) {
+            /* If there are no more payload types, we're finished */
+            break;
+        }
+
+        /* See if the next token is a new profile type. */
+        if (prof < SDP_MAX_PROFILES) {
+            profile_p->profile[prof+1] = SDP_TRANSPORT_UNSUPPORTED;
+            for (i=SDP_TRANSPORT_AAL2_ITU;
+                 i <= SDP_TRANSPORT_AAL2_CUSTOM; i++) {
+                if (cpr_strncasecmp(tmp, sdp_transport[i].name,
+                                sdp_transport[i].strlen) == 0) {
+                    profile_p->profile[prof+1] = (sdp_transport_e)i;
+                    break;
+                }
+            }
+            /* If we recognized the profile type, start looking for the
+             * next payload list. */
+            if (profile_p->profile[prof+1] != SDP_TRANSPORT_UNSUPPORTED) {
+                /* Now reset the payload counter for the next profile type. */
+                payload = 0;
+                prof++;
+                profile_p->num_profiles++;
+                if (prof < SDP_MAX_PROFILES) {
+                    profile_p->num_payloads[prof] = 0;
+                }
+                continue;
+            }
+        }
+
+        /* This token must be a payload type. Make sure there aren't
+         * too many payload types. */
+        if (payload >= SDP_MAX_PAYLOAD_TYPES) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: Too many payload types "
+                "found, truncating.", sdp_p->debug_str);
+            continue;
+        }
+
+        /* See if the payload type is numeric. */
+        if (prof < SDP_MAX_PROFILES && payload < SDP_MAX_PAYLOAD_TYPES) {
+            profile_p->payload_type[prof][payload] = (u16)sdp_getnextnumtok(tmp,
+                                                             (const char **)&tmp2,
+                                                             " \t", &result);
+            if (result == SDP_SUCCESS) {
+                profile_p->payload_indicator[prof][payload] = SDP_PAYLOAD_NUMERIC;
+                profile_p->num_payloads[prof]++;
+                payload++;
+                continue;
+            }
+        }
+
+        /* No string payload types are currently valid for the AAL2
+         * transport types.  This support can be added when needed. */
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Unsupported payload type "
+            "found (%s).", sdp_p->debug_str, tmp);
+    }
+    for (i=0; i < profile_p->num_profiles; i++) {
+        /* Make sure we have payloads for each profile type. */
+        if (profile_p->num_payloads[i] == 0) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: No payload types specified "
+                "for AAL2 profile %s.", sdp_p->debug_str,
+                sdp_get_transport_name(profile_p->profile[i]));
+        }
+    }
+    return (SDP_SUCCESS);
+}
diff --git a/libs/sipcc/core/sdp/sdp_utils.c b/libs/sipcc/core/sdp/sdp_utils.c
new file mode 100644 (file)
index 0000000..f3685e8
--- /dev/null
@@ -0,0 +1,773 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+#include <limits.h>
+#include "sdp_os_defs.h"
+#include "sdp.h"
+#include "sdp_private.h"
+#include "CSFLog.h"
+
+#define MKI_BUF_LEN 4
+
+static const char* logTag = "sdp_utils";
+
+sdp_mca_t *sdp_alloc_mca () {
+    sdp_mca_t           *mca_p;
+
+    /* Allocate resource for new media stream. */
+    mca_p = (sdp_mca_t *)SDP_MALLOC(sizeof(sdp_mca_t));
+    if (mca_p == NULL) {
+        return (NULL);
+    }
+    /* Initialize mca structure */
+    mca_p->media              = SDP_MEDIA_INVALID;
+    mca_p->conn.nettype       = SDP_NT_INVALID;
+    mca_p->conn.addrtype      = SDP_AT_INVALID;
+    mca_p->conn.conn_addr[0]  = '\0';
+    mca_p->conn.is_multicast  = FALSE;
+    mca_p->conn.ttl           = 0;
+    mca_p->conn.num_of_addresses = 0;
+    mca_p->transport          = SDP_TRANSPORT_INVALID;
+    mca_p->port               = SDP_INVALID_VALUE;
+    mca_p->num_ports          = SDP_INVALID_VALUE;
+    mca_p->vpi                = SDP_INVALID_VALUE;
+    mca_p->vci                = 0;
+    mca_p->vcci               = SDP_INVALID_VALUE;
+    mca_p->cid                = SDP_INVALID_VALUE;
+    mca_p->num_payloads       = 0;
+    mca_p->sessinfo_found     = FALSE;
+    mca_p->encrypt.encrypt_type  = SDP_ENCRYPT_INVALID;
+    mca_p->media_attrs_p      = NULL;
+    mca_p->next_p             = NULL;
+    mca_p->mid                = 0;
+    mca_p->bw.bw_data_count   = 0;
+    mca_p->bw.bw_data_list    = NULL;
+
+    return (mca_p);
+}
+
+/*
+ * next_token
+ *
+ * copy token param with chars from str until null, cr, lf, or one of the delimiters is found.
+ * delimiters at the beginning will be skipped.
+ * The pointer *string_of_tokens is moved forward to the next token on sucess.
+ *
+ */
+static sdp_result_e next_token(const char **string_of_tokens, char *token, unsigned token_max_len, const char *delim)
+{
+  int flag2moveon = 0;
+  const char *str = *string_of_tokens;
+  char *token_start = token;
+  const char *next_delim;
+
+  if (!string_of_tokens || !*string_of_tokens || !token || !delim) {
+    return SDP_FAILURE;
+  }
+
+  /* Locate front of token, skipping any delimiters */
+  for ( ; ((*str != '\0') && (*str != '\n') && (*str != '\r')); str++) {
+    flag2moveon = 1;  /* Default to move on unless we find a delimiter */
+    for (next_delim=delim; *next_delim; next_delim++) {
+      if (*str == *next_delim) {
+        flag2moveon = 0;
+        break;
+      }
+    }
+    if( flag2moveon ) {
+      break;  /* We're at the beginning of the token */
+    }
+  }
+
+  /* Make sure there's really a token present. */
+  if ((*str == '\0') || (*str == '\n') || (*str == '\r')) {
+    return SDP_FAILURE;
+  }
+
+  /* Now locate end of token */
+  flag2moveon = 0;
+
+  while (((token-token_start) < token_max_len - 1) &&
+         (*str != '\0') && (*str != '\n') && (*str != '\r')) {
+      for (next_delim=delim; *next_delim; next_delim++) {
+          if (*str == *next_delim) {
+              flag2moveon = 1;
+              break;
+          }
+      }
+      if( flag2moveon ) {
+          break;
+      } else {
+          *token++ = *str++;
+      }
+  }
+
+  /* mark end of token */
+  *token = '\0';
+
+  /* set the string of tokens to the next token */
+  *string_of_tokens = str;
+
+  return SDP_SUCCESS;
+}
+
+/*
+ * verify_sdescriptions_mki
+ *
+ * Verifies the syntax of the MKI parameter.
+ *
+ * mki            = mki-value ":" mki-length
+ * mki-value      = 1*DIGIT
+ * mki-length     = 1*3DIGIT   ; range 1..128
+ *
+ * Inputs:
+ *   buf      - ptr to start of MKI string assumes NULL
+ *              terminated string
+ *   mkiValue - buffer to store the MKI value, assumes calling
+ *              function has provided memory for this.
+ *   mkiLen   - integer to store the MKI length
+ *
+ * Outputs:
+ *   Returns TRUE if syntax is correct and stores the
+ *   MKI value in mkiVal and stores the length in mkiLen.
+ *   Returns FALSE otherwise.
+ */
+
+tinybool
+verify_sdescriptions_mki (char *buf, char *mkiVal, u16 *mkiLen)
+{
+
+    char       *ptr,
+               mkiValBuf[SDP_SRTP_MAX_MKI_SIZE_BYTES],
+              mkiLenBuf[MKI_BUF_LEN];
+    int        idx = 0;
+    unsigned long strtoul_result;
+    char *strtoul_end;
+
+    ptr = buf;
+    /* MKI must begin with a digit */
+    if (!ptr || (!isdigit((int) *ptr))) {
+        return FALSE;
+    }
+
+    /* scan until we reach a non-digit or colon */
+    while (*ptr) {
+        if (*ptr == ':') {
+           /* terminate the MKI value */
+           mkiValBuf[idx] = 0;
+           ptr++;
+           break;
+       } else if ((isdigit((int) *ptr) && (idx < SDP_SRTP_MAX_MKI_SIZE_BYTES-1))) {
+            mkiValBuf[idx++] = *ptr;
+       } else {
+            return FALSE;
+       }
+
+       ptr++;
+    }
+
+    /* there has to be a mki length */
+    if (*ptr == 0) {
+        return FALSE;
+    }
+
+    idx = 0;
+
+    /* verify the mki length (max 3 digits) */
+    while (*ptr) {
+        if (isdigit((int) *ptr) && (idx < 3)) {
+           mkiLenBuf[idx++] = *ptr;
+       } else {
+           return FALSE;
+       }
+
+       ptr++;
+    }
+
+    mkiLenBuf[idx] = 0;
+
+    errno = 0;
+    strtoul_result = strtoul(mkiLenBuf, &strtoul_end, 10);
+
+    /* mki len must be between 1..128 */
+    if (errno || mkiLenBuf == strtoul_end || strtoul_result < 1 || strtoul_result > 128) {
+      *mkiLen = 0;
+      return FALSE;
+    }
+
+    *mkiLen = (u16) strtoul_result;
+    sstrncpy(mkiVal, mkiValBuf, MKI_BUF_LEN);
+
+    return TRUE;
+}
+
+/*
+ * verify_srtp_lifetime
+ *
+ * Verifies the Lifetime parameter syntax.
+ *
+ *  lifetime = ["2^"] 1*(DIGIT)
+ *
+ * Inputs:
+ *   buf - pointer to start of lifetime string. Assumes string is
+ *         NULL terminated.
+ * Outputs:
+ *   Returns TRUE if syntax is correct. Returns FALSE otherwise.
+ */
+
+tinybool
+verify_sdescriptions_lifetime (char *buf)
+{
+
+    char     *ptr;
+    tinybool tokenFound = FALSE;
+
+    ptr = buf;
+    if (!ptr || *ptr == 0) {
+        return FALSE;
+    }
+
+    while (*ptr) {
+        if (*ptr == '^') {
+           if (tokenFound) {
+               /* make sure we don't have multiple ^ */
+                return FALSE;
+            } else {
+                tokenFound = TRUE;
+                /* Lifetime is in power of 2 format, make sure first and second
+                * chars are 2^
+                */
+
+                if (buf[0] != '2' || buf[1] != '^') {
+                   return FALSE;
+                }
+            }
+        } else if (!isdigit((int) *ptr)) {
+                  return FALSE;
+        }
+
+        ptr++;
+
+    }
+
+    /* Make sure if the format is 2^ that there is a number after the ^. */
+    if (tokenFound) {
+        if (strlen(buf) <= 2) {
+           return FALSE;
+       }
+    }
+
+    return TRUE;
+}
+
+
+/*
+ * sdp_validate_maxprate
+ *
+ * This function validates that the string passed in is of the form:
+ * packet-rate = 1*DIGIT ["." 1*DIGIT]
+ */
+tinybool
+sdp_validate_maxprate(const char *string_parm)
+{
+    tinybool retval = FALSE;
+
+    if (string_parm && (*string_parm)) {
+        while (isdigit((int)*string_parm)) {
+            string_parm++;
+        }
+
+        if (*string_parm == '.') {
+            string_parm++;
+            while (isdigit((int)*string_parm)) {
+                string_parm++;
+            }
+        }
+
+        if (*string_parm == '\0') {
+            retval = TRUE;
+        } else {
+            retval = FALSE;
+        }
+    }
+
+    return retval;
+}
+
+char *sdp_findchar (const char *ptr, char *char_list)
+{
+    int i;
+
+    for (;*ptr != '\0'; ptr++) {
+       for (i=0; char_list[i] != '\0'; i++) {
+           if (*ptr == char_list[i]) {
+               return ((char *)ptr);
+           }
+       }
+    }
+    return ((char *)ptr);
+}
+
+/* Locate the next token in a line.  The delim characters are passed in
+ * as a param.  The token also will not go past a new line char or the
+ * end of the string.  Skip any delimiters before the token.
+ */
+const char *sdp_getnextstrtok (const char *str, char *tokenstr, unsigned tokenstr_len,
+                         const char *delim, sdp_result_e *result)
+{
+  const char *token_list = str;
+
+  if (!str || !tokenstr || !delim || !result) {
+    if (result) {
+      *result = SDP_FAILURE;
+    }
+    return str;
+  }
+
+  *result = next_token(&token_list, tokenstr, tokenstr_len, delim);
+
+  return token_list;
+}
+
+
+
+/* Locate the next null ("-") or numeric token in a string.  The delim
+ * characters are passed in as a param.  The token also will not go past
+ * a new line char or the end of the string.  Skip any delimiters before
+ * the token.
+ */
+u32 sdp_getnextnumtok_or_null (const char *str, const char **str_end,
+                               const char *delim, tinybool *null_ind,
+                               sdp_result_e *result)
+{
+  const char *token_list = str;
+  char temp_token[SDP_MAX_STRING_LEN];
+  char *strtoul_end;
+  unsigned long numval;
+
+  *null_ind = FALSE;
+
+  if (!str || !str_end || !delim || !null_ind || !result) {
+    if (result) {
+      *result = SDP_FAILURE;
+    }
+    return 0;
+  }
+
+  *result = next_token(&token_list, temp_token, sizeof(temp_token), delim);
+
+  if (*result != SDP_SUCCESS) {
+    return 0;
+  }
+
+  /* First see if its the null char ("-") */
+  if (temp_token[0] == '-') {
+      *null_ind = TRUE;
+      *result = SDP_SUCCESS;
+      *str_end = str;
+      return 0;
+  }
+
+  errno = 0;
+  numval = strtoul(temp_token, &strtoul_end, 10);
+
+  if (errno || strtoul_end == temp_token || numval > UINT_MAX) {
+    *result = SDP_FAILURE;
+    return 0;
+  }
+
+  *result = SDP_SUCCESS;
+  *str_end = token_list;
+  return (u32) numval;
+}
+
+
+/* Locate the next numeric token in a string.  The delim characters are
+ * passed in as a param.  The token also will not go past a new line char
+ * or the end of the string.  Skip any delimiters before the token.
+ */
+u32 sdp_getnextnumtok (const char *str, const char **str_end,
+                       const char *delim, sdp_result_e *result)
+{
+  const char *token_list = str;
+  char temp_token[SDP_MAX_STRING_LEN];
+  char *strtoul_end;
+  unsigned long numval;
+
+  if (!str || !str_end || !delim || !result) {
+    if (result) {
+      *result = SDP_FAILURE;
+    }
+    return 0;
+  }
+
+  *result = next_token(&token_list, temp_token, sizeof(temp_token), delim);
+
+  if (*result != SDP_SUCCESS) {
+    return 0;
+  }
+
+  errno = 0;
+  numval = strtoul(temp_token, &strtoul_end, 10);
+
+  if (errno || strtoul_end == temp_token || numval > UINT_MAX) {
+    *result = SDP_FAILURE;
+    return 0;
+  }
+
+  *result = SDP_SUCCESS;
+  *str_end = token_list;
+  return (u32) numval;
+}
+
+
+/* See if the next token in a string is the choose character.  The delim
+ * characters are passed in as a param.  The check also will not go past
+ * a new line char or the end of the string.  Skip any delimiters before
+ * the token.
+ */
+tinybool sdp_getchoosetok (const char *str, const char **str_end,
+                           const char *delim, sdp_result_e *result)
+{
+    const char *b;
+    int   flag2moveon;
+
+    if ((str == NULL)  || (str_end == NULL)) {
+        *result = SDP_FAILURE;
+        return(FALSE);
+    }
+
+    /* Locate front of token, skipping any delimiters */
+    for ( ; ((*str != '\0') && (*str != '\n') && (*str != '\r')); str++) {
+        flag2moveon = 1;  /* Default to move on unless we find a delimiter */
+        for (b=delim; *b; b++) {
+            if (*str == *b) {
+                flag2moveon = 0;
+                break;
+            }
+        }
+        if( flag2moveon ) {
+            break;  /* We're at the beginning of the token */
+        }
+    }
+
+    /* Make sure there's really a token present. */
+    if ((*str == '\0') || (*str == '\n') || (*str == '\r')) {
+        *result = SDP_FAILURE;
+        *str_end = (char *)str;
+        return(FALSE);
+    }
+
+    /* See if the token is '$' followed by a delimiter char or end of str. */
+    if (*str == '$') {
+        str++;
+        if ((*str == '\0') || (*str == '\n') || (*str == '\r')) {
+            *result = SDP_SUCCESS;
+            /* skip the choose char in the string. */
+            *str_end = (char *)(str+1);
+            return(TRUE);
+        }
+        for (b=delim; *b; b++) {
+            if (*str == *b) {
+                *result = SDP_SUCCESS;
+                /* skip the choose char in the string. */
+                *str_end = (char *)(str+1);
+                return(TRUE);
+            }
+        }
+    }
+
+    /* If the token was not '$' followed by a delim, token is not choose */
+    *result = SDP_SUCCESS;
+    *str_end = (char *)str;
+    return(FALSE);
+
+}
+
+/*
+ * SDP Crypto Utility Functions.
+ *
+ * First a few common definitions.
+ */
+
+/*
+ * Constants
+ *
+ * crypto_string = The string used to identify the start of sensative
+ *     crypto data.
+ *
+ * inline_string = The string used to identify the start of key/salt
+ *     crypto data.
+ *
+ * star_string = The string used to overwrite sensative data.
+ *
+ * '*_strlen' = The length of '*_string' in bytes (not including '\0')
+ */
+static const char crypto_string[] = "X-crypto:";
+static const int crypto_strlen = sizeof(crypto_string) - 1;
+static const char inline_string[] = "inline:";
+static const int inline_strlen = sizeof(inline_string) - 1;
+/* 40 characters is the current maximum for a Base64 encoded key/salt */
+static const char star_string[] = "****************************************";
+static const int star_strlen = sizeof(star_string) - 1;
+
+/*
+ * MIN_CRYPTO_STRING_SIZE_BYTES = This value defines the minimum
+ *     size of a string that could contain a key/salt. This value
+ *     is used to skip out of parsing when there is no reasonable
+ *     assumption that sensative data will be found. The general
+ *     format of a SRTP Key Salt in SDP looks like:
+ *
+ * X-crypto:<crypto_suite_name> inline:<master_key_salt>||
+ *
+ *     if <crypto_suite_name> and <master_key_salt> is at least
+ *     one character and one space is used before the "inline:",
+ *     then this translates to a size of (aligned by collumn from
+ *     the format shown above):
+ *
+ * 9+       1+                 1+7+    1+                2 = 21
+ *
+ */
+#define MIN_CRYPTO_STRING_SIZE_BYTES 21
+
+/*
+ * Utility macros
+ *
+ * CHAR_IS_WHITESPACE = macro to determine if the passed _test_char
+ *     is whitespace.
+ *
+ * SKIP_WHITESPACE = Macro to advance _cptr to the next non-whitespace
+ *     character. _cptr will not be advanced past _max_cptr.
+ *
+ * FIND_WHITESPACE = Macro to advance _cptr until whitespace is found.
+ *     _cptr will not be advanced past _max_cptr.
+ */
+#define CHAR_IS_WHITESPACE(_test_char) \
+    ((((_test_char)==' ')||((_test_char)=='\t'))?1:0)
+
+#define SKIP_WHITESPACE(_cptr, _max_cptr)          \
+    while ((_cptr)<=(_max_cptr)) {                 \
+       if (!CHAR_IS_WHITESPACE(*(_cptr))) break;   \
+       (_cptr)++;                                  \
+    }
+
+#define FIND_WHITESPACE(_cptr, _max_cptr)          \
+    while ((_cptr)<=(_max_cptr)) {                 \
+       if (CHAR_IS_WHITESPACE(*(_cptr))) break;    \
+       (_cptr)++;                                  \
+    }
+
+/* Function:    sdp_crypto_debug
+ * Description: Check the passed buffer for sensitive data that should
+ *             not be output (such as SRTP Master Key/Salt) and output
+ *             the buffer as debug. Sensitive data will be replaced
+ *             with the '*' character(s). This function may be used
+ *             to display very large buffers so this function ensures
+ *             that buginf is not overloaded.
+ * Parameters:  buffer         pointer to the message buffer to filter.
+ *              length_bytes   size of message buffer in bytes.
+ * Returns:     Nothing.
+ */
+void sdp_crypto_debug (char *buffer, ulong length_bytes)
+{
+    char *current, *start;
+    char *last = buffer + length_bytes;
+    int result;
+
+    /*
+     * For SRTP Master Key/Salt has the form:
+     * X-crypto:<crypto_suite_name> inline:<master_key_salt>||
+     * Where <master_key_salt> is the data to elide (filter).
+     */
+    for (start=current=buffer;
+        current<=last-MIN_CRYPTO_STRING_SIZE_BYTES;
+        current++) {
+       if ((*current == 'x') || (*current == 'X')) {
+           result = cpr_strncasecmp(current, crypto_string, crypto_strlen);
+           if (!result) {
+               current += crypto_strlen;
+               if (current > last) break;
+
+               /* Skip over crypto suite name */
+               FIND_WHITESPACE(current, last);
+
+               /* Skip over whitespace */
+               SKIP_WHITESPACE(current, last);
+
+               /* identify inline keyword */
+               result = cpr_strncasecmp(current, inline_string, inline_strlen);
+               if (!result) {
+                   int star_count = 0;
+
+                   current += inline_strlen;
+                   if (current > last) break;
+
+                   sdp_dump_buffer(start, current - start);
+
+                   /* Hide sensitive key/salt data */
+                   while (current<=last) {
+                       if (*current == '|' || *current == '\n') {
+                           /* Done, print the stars */
+                           while (star_count > star_strlen) {
+                               /*
+                                * This code is only for the case where
+                                * too much base64 data was supplied
+                                */
+                               sdp_dump_buffer((char*)star_string, star_strlen);
+                               star_count -= star_strlen;
+                           }
+                           sdp_dump_buffer((char*)star_string, star_count);
+                           break;
+                       } else {
+                           star_count++;
+                           current++;
+                       }
+                   }
+                   /* Update start pointer */
+                   start=current;
+               }
+           }
+       }
+    }
+
+    if (last > start) {
+       /* Display remainder of buffer */
+       sdp_dump_buffer(start, last - start);
+    }
+}
+
+/*
+ * sdp_debug_msg_filter
+ *
+ * DESCRIPTION
+ *     Check the passed message buffer for sensitive data that should
+ *     not be output (such as SRTP Master Key/Salt). Sensitive data
+ *     will be replaced with the '*' character(s).
+ *
+ * PARAMETERS
+ *     buffer: pointer to the message buffer to filter.
+ *
+ *     length_bytes: size of message buffer in bytes.
+ *
+ * RETURN VALUE
+ *     The buffer modified.
+ */
+char * sdp_debug_msg_filter (char *buffer, ulong length_bytes)
+{
+    char *current;
+    char *last = buffer + length_bytes;
+    int result;
+
+    SDP_PRINT("\n%s:%d: Eliding sensitive data from debug output",
+           __FILE__, __LINE__);
+    /*
+     * For SRTP Master Key/Salt has the form:
+     * X-crypto:<crypto_suite_name> inline:<master_key_salt>||
+     * Where <master_key_salt> is the data to elide (filter).
+     */
+    for (current=buffer;
+        current<=last-MIN_CRYPTO_STRING_SIZE_BYTES;
+        current++) {
+       if ((*current == 'x') || (*current == 'X')) {
+           result = cpr_strncasecmp(current, crypto_string, crypto_strlen);
+           if (!result) {
+               current += crypto_strlen;
+               if (current > last) break;
+
+               /* Skip over crypto suite name */
+               FIND_WHITESPACE(current, last);
+
+               /* Skip over whitespace */
+               SKIP_WHITESPACE(current, last);
+
+               /* identify inline keyword */
+               result = cpr_strncasecmp(current, inline_string, inline_strlen);
+               if (!result) {
+                   current += inline_strlen;
+                   if (current > last) break;
+
+                   /* Hide sensitive key/salt data */
+                   while (current<=last) {
+                       if (*current == '|' || *current == '\n') {
+                           /* Done */
+                           break;
+                       } else {
+                           *current = '*';
+                           current++;
+                       }
+                   }
+               }
+           }
+       }
+    }
+
+    return buffer;
+}
+
+
+/* Function:    sdp_checkrange
+ * Description: This checks the range of a ulong value to make sure its
+ *              within the range of 0 and 4Gig. stroul cannot be used since
+ *              for values greater greater than 4G, stroul will either wrap
+ *              around or return ULONG_MAX.
+ * Parameters:  sdp_p       Pointer to the sdp structure
+ *              num         The number to check the range for
+ *              u_val       This variable get populated with the ulong value
+ *                          if the number is within the range.
+ * Returns:     tinybool - returns TRUE if the number passed is within the
+ *                         range, FALSE otherwise
+ */
+tinybool sdp_checkrange (sdp_t *sdp_p, char *num, ulong *u_val)
+{
+    ulong l_val;
+    char *endP = NULL;
+    *u_val = 0;
+
+    if (!num || !*num) {
+        return FALSE;
+    }
+
+    if (*num == '-') {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s ERROR: Parameter value is a negative number: %s",
+                      sdp_p->debug_str, num);
+        }
+        return FALSE;
+    }
+
+    l_val = strtoul(num, &endP, 10);
+    if (*endP == '\0') {
+
+        if (l_val > 4294967295UL) {
+           if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+               CSFLogError(logTag, "%s ERROR: Parameter value: %s is greater than 4294967295",
+                         sdp_p->debug_str, num);
+           }
+           return FALSE;
+       }
+
+       if (l_val == 4294967295UL) {
+           /*
+            * On certain platforms where ULONG_MAX is equivalent to
+            * 4294967295, strtoul will return ULONG_MAX even if the the
+            * value of the string is greater than 4294967295. To detect
+            * that scenario we make an explicit check here.
+            */
+           if (strcmp("4294967295", num)) {
+               if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+                   CSFLogError(logTag, "%s ERROR: Parameter value: %s is greater than 4294967295",
+                             sdp_p->debug_str, num);
+               }
+               return FALSE;
+           }
+       }
+    }
+    *u_val = l_val;
+    return TRUE;
+}
+
+#undef CHAR_IS_WHITESPACE
+#undef SKIP_WHITESPACE
+#undef FIND_WHITESPACE
diff --git a/libs/sipcc/core/sipstack/ccsip_callinfo.c b/libs/sipcc/core/sipstack/ccsip_callinfo.c
new file mode 100644 (file)
index 0000000..c76afd8
--- /dev/null
@@ -0,0 +1,677 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <limits.h>
+#include <errno.h>
+
+#include "ccsip_callinfo.h"
+#include "ccsip_protocol.h"
+#include "ccsip_core.h"
+#include "cpr_string.h"
+#include "cpr_strings.h"
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_memory.h"
+#include "cpr_stdlib.h"
+#include "phone_debug.h"
+
+
+#define FEAT_STRING_SIZE 80
+
+/*
+ * which_feature
+ *
+ * Description:
+ *
+ * A quick determination of the feature based on the string.
+ */
+static cc_call_info_e
+which_feature (char *feat_string_p)
+{
+    if (cpr_strcasecmp(feat_string_p, SIP_CI_HOLD_STR) == 0)
+        return CC_FEAT_HOLD;
+
+    if (cpr_strcasecmp(feat_string_p, SIP_CI_RESUME_STR) == 0)
+        return CC_FEAT_RESUME;
+
+    if (cpr_strcasecmp(feat_string_p, SIP_CI_BARGE_STR) == 0)
+        return CC_FEAT_BARGE;
+
+    if (cpr_strcasecmp(feat_string_p, SIP_CI_CBARGE_STR) == 0)
+        return CC_FEAT_CBARGE;
+
+    if (cpr_strcasecmp(feat_string_p, SIP_CI_CALL_INFO_STR) == 0)
+        return CC_FEAT_CALLINFO;
+
+    return CC_FEAT_NONE;
+}
+
+/*
+ * parse_call_info_parm
+ *
+ * Description:
+ *
+ * Parse potential callinfo feature parms.
+ */
+static void
+parse_call_info_parm (char *parm_p, cc_call_info_data_t * feature_data_p)
+{
+    static const char fname[] = "parse_call_info_parm";
+    char *temp_p;
+    uint16_t instance_id;
+    unsigned long strtoul_result;
+    char *strtoul_end;
+
+    if (!parm_p)
+        return;
+
+    while (parm_p) {
+        parm_p++;
+        SKIP_LWS(parm_p);
+
+        if (!cpr_strncasecmp(parm_p, SIP_CI_SECURITY,
+                             sizeof(SIP_CI_SECURITY) - 1)) {
+            parm_p = parm_p + sizeof(SIP_CI_SECURITY) - 1;
+            SKIP_LWS(parm_p);
+
+            if (*parm_p) {
+                feature_data_p->call_info_feat_data.feature_flag |= CC_SECURITY;
+                if (!cpr_strncasecmp(parm_p, SIP_CI_SECURITY_UNKNOWN,
+                                     sizeof(SIP_CI_SECURITY_UNKNOWN) - 1)) {
+                    feature_data_p->call_info_feat_data.security = CC_SECURITY_UNKNOWN;
+                } else if (!cpr_strncasecmp(parm_p, SIP_CI_SECURITY_AUTH,
+                                         sizeof(SIP_CI_SECURITY_AUTH) - 1)) {
+                    feature_data_p->call_info_feat_data.security = CC_SECURITY_AUTHENTICATED;
+                } else if (!cpr_strncasecmp(parm_p, SIP_CI_SECURITY_ENCRYPTED,
+                                         sizeof(SIP_CI_SECURITY_ENCRYPTED) - 1)) {
+                    feature_data_p->call_info_feat_data.security = CC_SECURITY_ENCRYPTED;
+                } else if (!cpr_strncasecmp(parm_p, SIP_CI_SECURITY_NOT_AUTH,
+                                             sizeof(SIP_CI_SECURITY_NOT_AUTH) - 1)) {
+                    feature_data_p->call_info_feat_data.security = CC_SECURITY_NOT_AUTHENTICATED;
+                } else {
+                            CCSIP_DEBUG_ERROR(SIP_F_PREFIX "Unknown security"
+                                              " value %s\n", fname, parm_p);
+                    feature_data_p->call_info_feat_data.security = CC_SECURITY_UNKNOWN;
+                }
+            } else {
+                break;
+            }
+        } else if (!cpr_strncasecmp(parm_p, SIP_CI_POLICY,
+                       sizeof(SIP_CI_POLICY) - 1)) {
+           parm_p = parm_p + sizeof(SIP_CI_POLICY) - 1;
+           SKIP_LWS(parm_p);
+
+           if (*parm_p) {
+               feature_data_p->call_info_feat_data.feature_flag |= CC_POLICY;
+               if (!cpr_strncasecmp(parm_p, SIP_CI_POLICY_CHAPERONE ,
+                               sizeof(SIP_CI_POLICY_CHAPERONE) - 1)) {
+                   feature_data_p->call_info_feat_data.policy = CC_POLICY_CHAPERONE;
+               } else if (!cpr_strncasecmp(parm_p, SIP_CI_POLICY_UNKNOWN,
+                               sizeof(SIP_CI_POLICY_UNKNOWN) - 1)) {
+                   feature_data_p->call_info_feat_data.policy = CC_POLICY_UNKNOWN;
+               } else {
+                   CCSIP_DEBUG_ERROR("%s ERROR: Unknown policy"
+                       " value %s\n", fname, parm_p) ;
+                   feature_data_p->call_info_feat_data.policy = CC_POLICY_UNKNOWN;
+               }
+           } else {
+               break;
+           }
+               } else if (!cpr_strncasecmp(parm_p, SIP_CI_ORIENTATION,
+                                    sizeof(SIP_CI_ORIENTATION) - 1)) {
+            parm_p = parm_p + sizeof(SIP_CI_ORIENTATION) - 1;
+            SKIP_LWS(parm_p);
+
+            if (*parm_p) {
+                feature_data_p->call_info_feat_data.feature_flag |= CC_ORIENTATION;
+                if (!cpr_strncasecmp(parm_p, SIP_CI_ORIENTATION_FROM,
+                                     sizeof(SIP_CI_ORIENTATION_FROM) - 1)) {
+                    feature_data_p->call_info_feat_data.orientation = CC_ORIENTATION_FROM;
+                } else if (!cpr_strncasecmp(parm_p, SIP_CI_ORIENTATION_TO,
+                                         sizeof(SIP_CI_ORIENTATION_TO) - 1)) {
+                    feature_data_p->call_info_feat_data.orientation = CC_ORIENTATION_TO;
+                } else {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX  "Unknown orientation info"
+                                      " value %s\n", fname, parm_p);
+                    feature_data_p->call_info_feat_data.orientation = CC_ORIENTATION_NONE;
+                }
+            } else {
+                break;
+            }
+        } else if (!cpr_strncasecmp(parm_p, SIP_CI_UI_STATE,
+                                    sizeof(SIP_CI_UI_STATE) - 1)) {
+            parm_p = parm_p + sizeof(SIP_CI_UI_STATE) - 1;
+            SKIP_LWS(parm_p);
+
+            if (*parm_p) {
+                feature_data_p->call_info_feat_data.feature_flag |= CC_UI_STATE;
+                if (!cpr_strncasecmp(parm_p, SIP_CI_UI_STATE_RINGOUT,
+                                     sizeof(SIP_CI_UI_STATE_RINGOUT) - 1)) {
+                    feature_data_p->call_info_feat_data.ui_state = CC_UI_STATE_RINGOUT;
+                } else if (!cpr_strncasecmp(parm_p, SIP_CI_UI_STATE_CONNECTED,
+                                         sizeof(SIP_CI_UI_STATE_CONNECTED) - 1)) {
+                    feature_data_p->call_info_feat_data.ui_state = CC_UI_STATE_CONNECTED;
+                } else if (!cpr_strncasecmp(parm_p, SIP_CI_UI_STATE_BUSY,
+                                         sizeof(SIP_CI_UI_STATE_BUSY) - 1)) {
+                    feature_data_p->call_info_feat_data.ui_state = CC_UI_STATE_BUSY;
+                } else {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX  "Unknown call state"
+                                      " value %s\n", fname, parm_p);
+                    /* Unknown value, ignore the call state */
+                    feature_data_p->call_info_feat_data.feature_flag &=
+                         ~(CC_UI_STATE);
+                    feature_data_p->call_info_feat_data.ui_state =
+                         CC_UI_STATE_NONE;
+                }
+            } else {
+                break;
+            }
+        } else if (!cpr_strncasecmp(parm_p, SIP_CI_CALL_INSTANCE,
+                                    sizeof(SIP_CI_CALL_INSTANCE) - 1)) {
+            parm_p = parm_p + sizeof(SIP_CI_CALL_INSTANCE) - 1;
+            SKIP_LWS(parm_p);
+
+            if (*parm_p) {
+                int idx=0;
+                char tempbuf[4];
+
+                feature_data_p->call_info_feat_data.feature_flag |= CC_CALL_INSTANCE;
+                /* Initialized the call instance id, just in case */
+                feature_data_p->call_info_feat_data.caller_id.call_instance_id
+                    = 0;
+                /* Parse instance id from line */
+                temp_p = parm_p;
+                while (isdigit((int) *parm_p)&&idx<3) {
+                                       tempbuf[idx++] = *parm_p++;
+                }
+                tempbuf[idx] = 0;
+                if (idx == 0) {
+                    /* Did not find any digit after "call_instance=" */
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX  "no digits found for"
+                                      " call_instance parameter.\n", fname);
+                    feature_data_p->call_info_feat_data.feature_flag &=
+                             ~(CC_CALL_INSTANCE);
+                    break;
+                } else {
+                    errno = 0;
+                    strtoul_result = strtoul(tempbuf, &strtoul_end, 10);
+
+                    if (errno || tempbuf == strtoul_end || strtoul_result > USHRT_MAX) {
+                      CCSIP_DEBUG_ERROR(SIP_F_PREFIX  "parse error for call_instance_id: %s",
+                                        __FUNCTION__, tempbuf);
+                      strtoul_result = 0;
+                    }
+
+                    feature_data_p->call_info_feat_data.caller_id.call_instance_id =
+                        (uint16_t) strtoul_result;
+                }
+            } else {
+                break;
+            }
+        } else if (!cpr_strncasecmp(parm_p, SIP_CI_PRIORITY,
+                                    sizeof(SIP_CI_PRIORITY) - 1)) {
+            parm_p = parm_p + sizeof(SIP_CI_PRIORITY) - 1;
+            SKIP_LWS(parm_p);
+
+            if (*parm_p) {
+                temp_p = parm_p;
+                if ((!cpr_strncasecmp(parm_p, SIP_CI_PRIORITY_URGENT,
+                                      sizeof(SIP_CI_PRIORITY_URGENT) - 1)) ||
+                    (!cpr_strncasecmp(parm_p, SIP_CI_PRIORITY_EMERGENCY,
+                                      sizeof(SIP_CI_PRIORITY_EMERGENCY) - 1))) {
+                    feature_data_p->call_info_feat_data.priority = CC_CALL_PRIORITY_URGENT;
+                } // otherwise, it will be defaulted to normal priority
+                else {
+                    errno = 0;
+                    strtoul_result = strtoul(temp_p, &strtoul_end, 10);
+
+                    if (errno || temp_p == strtoul_end || strtoul_result > MAX_INSTANCES) {
+                        /*
+                         * Call instance ID should not exceed max instances
+                         * or calls.
+                         */
+                        CCSIP_DEBUG_ERROR(SIP_F_PREFIX  "invalid call_instance"
+                                          " value %u\n", fname, (unsigned) strtoul_result);
+                        feature_data_p->call_info_feat_data.feature_flag &=
+                                 ~(CC_CALL_INSTANCE);
+                    } else {
+                        instance_id = (uint16_t) strtoul_result;
+                        feature_data_p->call_info_feat_data.caller_id.call_instance_id = instance_id;
+                    }
+                }
+            }
+        } else if (!cpr_strncasecmp(parm_p, SIP_CI_GCID,
+                                    sizeof(SIP_CI_GCID) - 1)) {
+            parm_p = parm_p + sizeof(SIP_CI_GCID) - 1;
+            SKIP_LWS(parm_p);
+            memset(feature_data_p->call_info_feat_data.global_call_id, 0, CC_GCID_LEN);
+            if (*parm_p) {
+              temp_p = strchr(parm_p, SEMI_COLON);
+              if (temp_p) {
+                unsigned int length = ((temp_p - parm_p)<CC_GCID_LEN) ?
+                                          (temp_p - parm_p):(CC_GCID_LEN);
+                sstrncpy(feature_data_p->call_info_feat_data.global_call_id, parm_p, length);
+              } else {
+                // No Semicolon found this could be the last parameter
+                sstrncpy(feature_data_p->call_info_feat_data.global_call_id, parm_p, CC_GCID_LEN);
+              }
+              feature_data_p->call_info_feat_data.global_call_id[CC_GCID_LEN-1] = 0;
+            }
+        } else if (!cpr_strncasecmp(parm_p, SIP_CI_DUSTINGCALL,
+                                    sizeof(SIP_CI_DUSTINGCALL) - 1)) {
+            parm_p = parm_p + sizeof(SIP_CI_DUSTINGCALL) - 1;
+            SKIP_LWS(parm_p);
+            feature_data_p->call_info_feat_data.dusting = TRUE;
+        }
+
+        parm_p = strchr(parm_p, SEMI_COLON);
+    }
+}
+
+/*
+ * parse_gen_parm
+ *
+ * Description:
+ *
+ * Parse feature parms where the only expected parm is the purpose.
+ */
+static void
+parse_gen_parm (char *parm_p, cc_call_info_data_t * feature_data_p)
+{
+    if (!parm_p)
+        return;
+
+    while (parm_p) {
+        parm_p++;
+        SKIP_LWS(parm_p);
+
+        if (!cpr_strncasecmp(parm_p, SIP_CI_GENERIC,
+                             sizeof(SIP_CI_GENERIC) - 1)) {
+            parm_p = parm_p + sizeof(SIP_CI_GENERIC) - 1;
+            SKIP_LWS(parm_p);
+
+            if (*parm_p) {
+                if (!cpr_strncasecmp(parm_p, SIP_CI_GENERIC_ICON,
+                                     sizeof(SIP_CI_GENERIC_ICON) - 1)) {
+                    feature_data_p->purpose = CC_PURPOSE_ICON;
+                } else {
+                    if (!cpr_strncasecmp(parm_p, SIP_CI_GENERIC_INFO,
+                                         sizeof(SIP_CI_GENERIC_INFO) - 1)) {
+                        feature_data_p->purpose = CC_PURPOSE_INFO;
+                    } else {
+                        if (!cpr_strncasecmp(parm_p, SIP_CI_GENERIC_CARD,
+                                             sizeof(SIP_CI_GENERIC_CARD) - 1)) {
+                            feature_data_p->purpose = CC_PURPOSE_CARD;
+                        }
+                    }
+                }
+            }
+        } else {
+            break;
+        }
+        parm_p = strchr(parm_p, SEMI_COLON);
+    }
+}
+
+/*
+ * set_parm_defaults
+ *
+ * Description:
+ *
+ * A quick determination of the feature based on the string.
+ */
+static void
+set_parm_defaults (cc_call_info_t *call_info_p)
+{
+    switch (call_info_p->type) {
+    case CC_FEAT_HOLD:
+    case CC_FEAT_RESUME:
+    case CC_FEAT_NONE:
+        call_info_p->data.hold_resume_reason = CC_REASON_NONE;
+        break;
+
+    case CC_FEAT_BARGE:
+    case CC_FEAT_CBARGE:
+        call_info_p->data.purpose = CC_PURPOSE_NONE;
+        break;
+
+    case CC_FEAT_CALLINFO:
+        call_info_p->data.call_info_feat_data.policy = CC_POLICY_NONE;
+        call_info_p->data.call_info_feat_data.security = CC_SECURITY_NONE;
+        call_info_p->data.call_info_feat_data.orientation = CC_ORIENTATION_NONE;
+        call_info_p->data.call_info_feat_data.ui_state = CC_UI_STATE_NONE;
+        call_info_p->data.call_info_feat_data.priority = CC_CALL_PRIORITY_NORMAL;
+        call_info_p->data.call_info_feat_data.global_call_id[0] = 0;
+        call_info_p->data.call_info_feat_data.dusting = FALSE;
+        break;
+
+    default:
+        break;
+    }
+
+}
+
+/*
+ * ccsip_decode_call_info_hdr
+ *
+ * Description:
+ *
+ * Main method which decodes a single call info header and stores the
+ * related parms.
+ *
+ * Example Input:
+ * ---------------
+ * <urn:x-cisco-remotecc:hold>; reason= conference
+ * <urn:x-cisco-remotecc:callinfo>; seCuRity=unsecure; orienTation= to
+ */
+static void
+ccsip_decode_call_info_hdr (const char *call_info_hdr_p,
+                            cc_call_info_t *call_info_p)
+{
+    char           *ptr = NULL;
+    char           *laq_ptr = NULL;
+    char           *raq_ptr = NULL;
+    boolean         ret_val = FALSE;
+    char            feat_string[FEAT_STRING_SIZE];
+
+    memset(feat_string, '\0', sizeof(feat_string));
+
+    /*
+     * call_info_hdr_p and call_info_p are verified by caller so they
+     * are not checked here.
+     */
+
+    ptr = laq_ptr = strchr(call_info_hdr_p, LAQUOT);
+    raq_ptr = strchr(call_info_hdr_p, RAQUOT);
+
+    // Parse out the remotecc string and the feature string.
+    if (laq_ptr && raq_ptr) {
+        ptr++;
+
+        // Verify the remotecc string.
+        if (!cpr_strncasecmp(ptr, URN_REMOTECC, sizeof(URN_REMOTECC) - 1)) {
+            ptr += sizeof(URN_REMOTECC) - 1;
+            sstrncpy(feat_string, ptr, raq_ptr - ptr + 1);
+
+            // Which feature do we have in this header?
+            call_info_p->type = which_feature(feat_string);
+
+            if (call_info_p->type != CC_FEAT_NONE) {
+                ret_val = TRUE;
+                set_parm_defaults(call_info_p);
+            }
+        }
+    }
+
+    if (!ret_val) {
+        return;
+    }
+
+    if (!(ptr = strchr(raq_ptr, SEMI_COLON))) {
+        return;
+    }
+
+    switch (call_info_p->type) {
+    case CC_FEAT_CALLINFO:
+        parse_call_info_parm(ptr, &call_info_p->data);
+        break;
+    default:
+        parse_gen_parm(ptr, &call_info_p->data);
+    }
+}
+
+/*
+ * ccsip_encode_call_info_hdr
+ *
+ * Description:
+ *
+ * Encode the call info header using the passed in feature id and
+ * feature specific data.
+ *
+ * The miscParms parameter will usually be null.  It exists in case
+ * you want to toss in an additional string parm without using the
+ * encoding mechanism.  An example would be "extraParm= text".
+ *
+ * Remember to delete the store in the return parm.  It is the
+ * caller's responsibility.
+ */
+char *
+ccsip_encode_call_info_hdr (cc_call_info_t *call_info_p,
+                            const char *misc_parms_p)
+{
+    static const char *fname = "ccsip_encode_call_info_hdr";
+    char *header;
+
+    header = (char *) cpr_malloc(MAX_SIP_HEADER_LENGTH);
+    if (!header) {
+        return NULL;
+    }
+
+    if (!call_info_p) {
+        cpr_free(header);
+        return NULL;
+    }
+
+    snprintf(header, MAX_SIP_HEADER_LENGTH, "<%s", URN_REMOTECC);
+
+    switch (call_info_p->type) {
+    case CC_FEAT_HOLD:
+    case CC_FEAT_RESUME:
+        if (call_info_p->type == CC_FEAT_HOLD) {
+            sstrncat(header, SIP_CI_HOLD_STR,
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+        } else {
+            sstrncat(header, SIP_CI_RESUME_STR,
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+        }
+        sstrncat(header, ">", MAX_SIP_HEADER_LENGTH - strlen(header));
+
+        switch (call_info_p->data.hold_resume_reason) {
+        case CC_REASON_NONE:
+        case CC_REASON_INTERNAL:
+        case CC_REASON_SWAP:
+            break;
+        case CC_REASON_XFER:
+            sstrncat(header, "; reason= ",
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+            sstrncat(header, SIP_CI_HOLD_REASON_XFER,
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+            break;
+        case CC_REASON_CONF:
+            sstrncat(header, "; reason= ",
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+            sstrncat(header, SIP_CI_HOLD_REASON_CONF,
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+            break;
+        default:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX  "unsupported hold_resume_reason\n",
+                              fname);
+            cpr_free(header);
+            return NULL;
+        }
+
+        /* Add swap information */
+        if (call_info_p->data.call_info_feat_data.swap == TRUE) {
+            sstrncat(header, "; operation= swap",
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+        }
+
+        if (call_info_p->data.call_info_feat_data.protect == TRUE) {
+            sstrncat(header, "; protect= true; noholdreversion",
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+        }
+
+        break;
+
+    case CC_FEAT_INIT_CALL:
+        /* Add global call id here */
+        if (call_info_p->data.initcall.gcid[0] != '\0') {
+            sstrncat(header, "callinfo>; gci= ",
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+            sstrncat(header, call_info_p->data.initcall.gcid,
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+        } else {
+            cpr_free(header);
+            return NULL;
+        }
+        /* Add the monitor mode here if it exists */
+        if (call_info_p->data.initcall.monitor_mode != CC_MONITOR_NONE) {
+            sstrncat(header, "; mode=",
+                    MAX_SIP_HEADER_LENGTH - strlen(header));
+
+            switch (call_info_p->data.initcall.monitor_mode) {
+
+            case CC_MONITOR_SILENT :
+                sstrncat(header, SIP_CI_SILENT_STR,
+                        MAX_SIP_HEADER_LENGTH - strlen(header));
+                break;
+
+            case CC_MONITOR_COACHING :
+                sstrncat(header, SIP_CI_COACHING_STR,
+                        MAX_SIP_HEADER_LENGTH - strlen(header));
+                break;
+
+            default:
+                break;
+            }
+        }
+        break;
+
+    case CC_FEAT_TOGGLE_TO_WHISPER_COACHING:
+        sstrncat(header, "callinfo>",
+               MAX_SIP_HEADER_LENGTH - strlen(header));
+        sstrncat(header, "; mode=",
+            MAX_SIP_HEADER_LENGTH - strlen(header));
+        sstrncat(header, SIP_CI_COACHING_STR,
+            MAX_SIP_HEADER_LENGTH - strlen(header));
+
+        break;
+
+    case CC_FEAT_TOGGLE_TO_SILENT_MONITORING:
+        sstrncat(header, "callinfo>",
+            MAX_SIP_HEADER_LENGTH - strlen(header));
+        sstrncat(header, "; mode=",
+            MAX_SIP_HEADER_LENGTH - strlen(header));
+        sstrncat(header, SIP_CI_SILENT_STR,
+            MAX_SIP_HEADER_LENGTH - strlen(header));
+
+        break;
+
+    default:
+        cpr_free(header);
+        return NULL;
+    }
+
+
+    if (misc_parms_p) {
+        sstrncat(header, misc_parms_p,
+                MAX_SIP_HEADER_LENGTH - strlen(header));
+    }
+    sstrncat(header, "\0", MAX_SIP_HEADER_LENGTH - strlen(header));
+    return (header);
+}
+
+/*
+ * ccsip_free_call_info_header
+ *
+ * Description:
+ *
+ * Frees the memory allocated to a call info structure.
+ */
+void
+ccsip_free_call_info_header (cc_call_info_t *call_info_p)
+{
+       if(call_info_p->type == CC_FEAT_CALLINFO) {
+
+       }
+    cpr_free(call_info_p);
+}
+
+/*
+ * ccsip_process_call_info_header
+ *
+ * Description:
+ *
+ * Checks if there is a call info header in the provided SIP message. If there is,
+ * the call info in the CCB is cleared and the new call info is parsed into the
+ * CCB call info structure.
+ */
+void
+ccsip_process_call_info_header (sipMessage_t *request_p, ccsipCCB_t *ccb)
+{
+    char       *call_info_hdrs[MAX_CALL_INFO_HEADERS];
+    uint16_t    num_call_info_headers;
+    int         i = 0;
+
+    if (!ccb) {
+        return;
+    }
+
+    if (ccb->in_call_info) {
+        ccsip_free_call_info_header(ccb->in_call_info);
+        ccb->in_call_info = NULL;
+    }
+
+    if (!request_p) {
+        return;
+    }
+
+    memset(call_info_hdrs, 0, MAX_CALL_INFO_HEADERS * sizeof(char *));
+
+    num_call_info_headers = sippmh_get_num_particular_headers(request_p,
+                                                              SIP_HEADER_CALL_INFO,
+                                                              SIP_HEADER_CALL_INFO,
+                                                              call_info_hdrs,
+                                                              MAX_CALL_INFO_HEADERS);
+
+    if (num_call_info_headers > 0) {
+        ccb->in_call_info = (cc_call_info_t *)
+            cpr_calloc(1, sizeof(cc_call_info_t));
+        if (ccb->in_call_info) {
+
+            ccb->in_call_info->data.call_info_feat_data.feature_flag = 0;
+
+            // Parse each Call-Info header
+            for (i = 0; i < MAX_CALL_INFO_HEADERS; i++) {
+                if (call_info_hdrs[i]) {
+                    ccsip_decode_call_info_hdr(call_info_hdrs[i], ccb->in_call_info);
+                }
+            }
+
+        } else {
+            ccb->in_call_info = NULL;
+        }
+    }
+
+}
+
+/*
+ * ccsip_store_call_info
+ *
+ * Description: Used for storing call_info received from GSM
+ *
+ * Store specified call info structure in ccb.
+ */
+void
+ccsip_store_call_info (cc_call_info_t *call_info_p, ccsipCCB_t *ccb)
+{
+    if (!ccb) {
+        return;
+    }
+
+    if (ccb->out_call_info) {
+        ccsip_free_call_info_header(ccb->out_call_info);
+        ccb->out_call_info = NULL;
+    }
+
+    if (call_info_p->type != CC_FEAT_NONE) {
+        ccb->out_call_info = (cc_call_info_t *)
+            cpr_malloc(sizeof(cc_call_info_t));
+        if (ccb->out_call_info) {
+            memcpy(ccb->out_call_info, call_info_p, sizeof(cc_call_info_t));
+        } else {
+            ccb->out_call_info = NULL;
+        }
+    }
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_cc.c b/libs/sipcc/core/sipstack/ccsip_cc.c
new file mode 100755 (executable)
index 0000000..0a97efc
--- /dev/null
@@ -0,0 +1,305 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "ccapi.h"
+#include "string_lib.h"
+#include "ccsip_pmh.h"
+#include "ccsip_messaging.h"
+
+/*
+ *  Function: sip_cc_mv_msg_body_to_cc_msg
+ *
+ *  Parameters: cc_msg  - pointer to cc_msgbody_info structure to
+ *                        move the content from the SIP body msg. to.
+ *              sip_msg - pointer to sipMessage_t structure of the source
+ *                        body.
+ *
+ *  Description: This routine moves the body parts from sipMessage_t to
+ *               CCAPI body msg. Once the content is moved,
+ *               the all pointers from the sipMessage_t structure,
+ *               will be NULL so that they are not own by SIP stack.
+ *               The destination of the msg. needs to free the
+ *               memory for of the parts.
+ *
+ *  Returns: N/A
+ *
+ */
+void sip_cc_mv_msg_body_to_cc_msg (cc_msgbody_info_t *cc_msg,
+                                   sipMessage_t *sip_msg)
+{
+    int i;
+    uint32_t num_parts = 0;
+    cc_msgbody_t *part;
+
+    if (cc_msg == NULL) {
+        /* destination to move msg. to */
+        return;
+    }
+    if (sip_msg == NULL) {
+        /* No SIP message to move from, set number of part to zero */
+        cc_msg->num_parts = 0;
+        return;
+    }
+
+    part = &cc_msg->parts[0];
+    for (i = 0; (i < sip_msg->num_body_parts) &&
+                (i < CC_MAX_BODY_PARTS); i++) {
+        if ((sip_msg->mesg_body[i].msgBody != NULL) &&
+            (sip_msg->mesg_body[i].msgLength)) {
+            /* Body */
+            part->body        = sip_msg->mesg_body[i].msgBody;
+            part->body_length = sip_msg->mesg_body[i].msgLength;
+            sip_msg->mesg_body[i].msgBody = NULL;
+
+            /* Content type */
+            part->content_type =
+                    sip2cctype(sip_msg->mesg_body[i].msgContentTypeValue);
+            /* Disposition */
+            part->content_disposition.disposition =
+                    sip2ccdisp(sip_msg->mesg_body[i].msgContentDisp);
+            part->content_disposition.required_handling =
+                    sip_msg->mesg_body[i].msgRequiredHandling;
+            /* Content ID */
+            part->content_id = sip_msg->mesg_body[i].msgContentId;
+            sip_msg->mesg_body[i].msgContentId = NULL;
+
+            /* Next part */
+            part++;
+            num_parts++;
+        }
+    }
+    /* Set the number of parts */
+    cc_msg->num_parts = num_parts;
+}
+
+/*
+ *  Function: sip_cc_create_cc_msg_body_from_sip_msg
+ *
+ *  Parameters: cc_msg  - pointer to cc_msgbody_info structure to
+ *                        store the content of the SIP body msg.
+ *              sip_msg - pointer to sipMessage_t structure of the source
+ *                        body.
+ *
+ *  Description: This routine creates the cc_msgbody_info_t content
+ *               from the SIP message. The original msg. body in the
+ *               SIP message remains in the SIP message.
+ *
+ *  Returns: TRUE  - success
+ *           FALSE - fail.
+ *
+ */
+boolean sip_cc_create_cc_msg_body_from_sip_msg (cc_msgbody_info_t *cc_msg,
+                                                sipMessage_t *sip_msg)
+{
+    int i, len;
+    uint32_t num_parts = 0;
+    cc_msgbody_t *part;
+    boolean  status = TRUE;
+
+    if (cc_msg == NULL) {
+        /* destination to move msg. to */
+        return (FALSE);
+    }
+
+    if (sip_msg == NULL) {
+        /* No SIP message to move from, set number of part to zero */
+        cc_msg->num_parts = 0;
+        return (FALSE);
+    }
+
+    memset(cc_msg, 0, sizeof(cc_msgbody_info_t));
+    part = &cc_msg->parts[0];
+    for (i = 0; (i < sip_msg->num_body_parts) &&
+                (i < CC_MAX_BODY_PARTS) ; i++) {
+        if ((sip_msg->mesg_body[i].msgBody != NULL) &&
+            (sip_msg->mesg_body[i].msgLength)) {
+            /* Body */
+            part->body = (char *) cpr_malloc(sip_msg->mesg_body[i].msgLength);
+            if (part->body != NULL) {
+                part->body_length = sip_msg->mesg_body[i].msgLength;
+                memcpy(part->body,
+                       sip_msg->mesg_body[i].msgBody,
+                       sip_msg->mesg_body[i].msgLength);
+            } else {
+                /* Unable to allocate memory for msg. body */
+                status = FALSE;
+                break;
+            }
+
+            /* Content type */
+            part->content_type =
+                    sip2cctype(sip_msg->mesg_body[i].msgContentTypeValue);
+            /* Disposition */
+            part->content_disposition.disposition =
+                    sip2ccdisp(sip_msg->mesg_body[i].msgContentDisp);
+            part->content_disposition.required_handling =
+                    sip_msg->mesg_body[i].msgRequiredHandling;
+
+            /* Content ID */
+            if (sip_msg->mesg_body[i].msgContentId != NULL) {
+                /* Get length of the msgContentID with NULL */
+                len = strlen(sip_msg->mesg_body[i].msgContentId) + 1;
+                part->content_id = (char *) cpr_malloc(len);
+                if (part->content_id != NULL) {
+                    memcpy(part->content_id,
+                           sip_msg->mesg_body[i].msgContentId,
+                           len);
+                } else {
+                    /* Unable to allocate allocate memory for content ID */
+                    status = FALSE;
+                    break;
+                }
+            } else {
+                /* No content ID */
+                part->content_id = NULL;
+            }
+            /* Next part */
+            part++;
+            num_parts++;
+        }
+    }
+    /* Set the number of parts */
+    cc_msg->num_parts = num_parts;
+
+    if (!status) {
+        /*
+         * Faied for some reason, free the resources that mighe be
+         * created before failure
+         */
+        cc_free_msg_body_parts(cc_msg);
+    }
+    return (status);
+}
+
+void sip_cc_setup (callid_t call_id, line_t line,
+                   string_t calling_name, string_t calling_number, string_t alt_calling_number,
+                   boolean display_calling_number,
+                   string_t called_name,  string_t called_number,
+                   boolean display_called_number,
+                   string_t orig_called_name, string_t orig_called_number,
+                   string_t last_redirect_name, string_t last_redirect_number,
+                   cc_call_type_e   call_type,
+                   cc_alerting_type alert_info,
+                   vcm_ring_mode_t alerting_ring,
+                   vcm_tones_t alerting_tone, cc_call_info_t *call_info_p,
+                   boolean replaces, string_t recv_info_list, sipMessage_t *sip_msg)
+{
+    cc_caller_id_t caller_id;
+    cc_msgbody_info_t cc_body_info;
+
+    caller_id.calling_name = calling_name;
+    caller_id.calling_number = calling_number;
+    caller_id.alt_calling_number = alt_calling_number;
+    caller_id.display_calling_number = display_calling_number;
+    caller_id.called_name = called_name;
+    caller_id.called_number = called_number;
+    caller_id.display_called_number = display_called_number;
+    caller_id.last_redirect_name = last_redirect_name;
+    caller_id.last_redirect_number = last_redirect_number;
+    caller_id.orig_called_name = orig_called_name;
+    caller_id.orig_called_number = orig_called_number;
+    caller_id.orig_rpid_number = strlib_empty();
+    caller_id.call_type = call_type;
+
+    /* Move the SIP body parts to the CCAPI msg. body information block */
+    sip_cc_mv_msg_body_to_cc_msg(&cc_body_info, sip_msg);
+
+// Check with CraigB
+    cc_setup(CC_SRC_SIP, call_id, line, &caller_id, alert_info,
+             alerting_ring, alerting_tone, NULL, call_info_p, replaces,
+             recv_info_list, &cc_body_info);
+}
+
+
+#ifdef REMOVED_UNUSED_FUNCTION
+void sip_cc_setup_ack (callid_t call_id, line_t line,
+                       cc_msgbody_info_t *msg_body)
+{
+    cc_setup_ack(CC_SRC_SIP, call_id, line, NULL, msg_body);
+}
+#endif
+
+void sip_cc_proceeding (callid_t call_id, line_t line)
+{
+    cc_proceeding(CC_SRC_SIP, call_id, line, NULL);
+}
+
+void sip_cc_alerting (callid_t call_id, line_t line,
+                      sipMessage_t *sip_msg, int inband)
+{
+    cc_msgbody_info_t cc_body_info;
+
+    /* Move the SIP body parts to the CCAPI msg. body information block */
+    sip_cc_mv_msg_body_to_cc_msg(&cc_body_info, sip_msg);
+
+    cc_alerting(CC_SRC_SIP, call_id, line, NULL, &cc_body_info,
+                (boolean)inband);
+}
+
+
+void sip_cc_connected (callid_t call_id, line_t line, string_t recv_info_list, sipMessage_t *sip_msg)
+{
+    cc_msgbody_info_t cc_body_info;
+
+    /* Move the SIP body parts to the CCAPI msg. body information block */
+    sip_cc_mv_msg_body_to_cc_msg(&cc_body_info, sip_msg);
+
+    cc_connected(CC_SRC_SIP, call_id, line, NULL, recv_info_list, &cc_body_info);
+}
+
+
+void sip_cc_connected_ack (callid_t call_id, line_t line,
+                           sipMessage_t *sip_msg)
+{
+    cc_msgbody_info_t cc_body_info;
+
+    /* Move the SIP body parts to the CCAPI msg. body information block */
+    sip_cc_mv_msg_body_to_cc_msg(&cc_body_info, sip_msg);
+
+    cc_connected_ack(CC_SRC_SIP, call_id, line, NULL, &cc_body_info);
+}
+
+
+void sip_cc_release (callid_t call_id, line_t line, cc_causes_t cause,
+                     const char *dialstring)
+{
+    cc_release(CC_SRC_SIP, call_id, line, cause, dialstring, NULL);
+}
+
+
+void sip_cc_release_complete (callid_t call_id, line_t line, cc_causes_t cause)
+{
+    cc_release_complete(CC_SRC_SIP, call_id, line, cause, NULL);
+}
+
+
+void sip_cc_feature (callid_t call_id, line_t line, cc_features_t feature, void *data)
+{
+    cc_feature(CC_SRC_SIP, call_id, line, feature, (cc_feature_data_t *)data);
+}
+
+
+void sip_cc_feature_ack (callid_t call_id, line_t line, cc_features_t feature,
+                         void *data, cc_causes_t cause)
+{
+    cc_feature_ack(CC_SRC_SIP, call_id, line, feature, (cc_feature_data_t *)data, cause);
+}
+
+
+void sip_cc_mwi (callid_t call_id, line_t line, boolean on, int type,
+                 int newCount, int oldCount, int hpNewCount, int hpOldCount)
+{
+    cc_mwi(CC_SRC_SIP, call_id, line, on, type, newCount, oldCount, hpNewCount, hpOldCount);
+}
+
+void sip_cc_options (callid_t call_id, line_t line, sipMessage_t *pSipMessage)
+{
+    cc_options_sdp_req(CC_SRC_SIP, call_id, line, pSipMessage);
+}
+
+void sip_cc_audit (callid_t call_id, line_t line, boolean apply_ringout)
+{
+    cc_audit_sdp_req(CC_SRC_SIP, call_id, line, apply_ringout);
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_common_util.c b/libs/sipcc/core/sipstack/ccsip_common_util.c
new file mode 100644 (file)
index 0000000..170bd98
--- /dev/null
@@ -0,0 +1,260 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_in.h"
+#include "ccsip_common_cb.h"
+#include "sip_common_transport.h"
+#include "prot_configmgr.h"
+#include "ccsip_register.h"
+#include "util_string.h"
+
+/**
+ * This function will set dest ip and port in common control block of SCB and PCB.
+ *
+ * @param[in] cb_p - pointer to the header control block.
+ *
+ * @return none
+ *
+ * @pre     (cb_p != NULL)
+ */
+void ccsip_common_util_set_dest_ipaddr_port (ccsip_common_cb_t *cb_p)
+{
+    char            addr[MAX_IPADDR_STR_LEN];
+
+    if (cb_p->dest_sip_addr.type == CPR_IP_ADDR_INVALID) {
+        sipTransportGetPrimServerAddress(cb_p->dn_line, addr);
+        dns_error_code = sipTransportGetServerAddrPort(addr,
+                                                       &cb_p->dest_sip_addr,
+                                                       (uint16_t *)&cb_p->dest_sip_port,
+                                                       &cb_p->SRVhandle,
+                                                       FALSE);
+        if (dns_error_code == 0) {
+            util_ntohl(&(cb_p->dest_sip_addr), &(cb_p->dest_sip_addr));
+        } else {
+            sipTransportGetServerIPAddr(&(cb_p->dest_sip_addr), cb_p->dn_line);
+        }
+
+        cb_p->dest_sip_port = ((dns_error_code == 0) && (cb_p->dest_sip_port)) ?
+                              ntohs((uint16_t)cb_p->dest_sip_port) :
+                              (sipTransportGetPrimServerPort(cb_p->dn_line));
+    }
+}
+
+/**
+ * This function will set source ip  in common control block of SCB and PCB.
+ *
+ * @param[in] cb_p - pointer to the header control block.
+ *
+ * @return none
+ *
+ * @pre     (cb_p != NULL)
+ */
+void ccsip_common_util_set_src_ipaddr (ccsip_common_cb_t *cb_p)
+{
+    int             nat_enable = 0;
+
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        sip_config_get_net_device_ipaddr(&(cb_p->src_addr));
+    } else {
+        sip_config_get_nat_ipaddr(&(cb_p->src_addr));
+    }
+}
+
+/*
+ * This function will set retry settings in common control block of SCB and PCB.
+ *
+ * Description: Based on transport used, determines the value to be
+ *     used to set either TimerE or TimerF
+ *     1. For reliable tranport: we SHOULD start timer F (= 64*T1),
+ *        no retransmits are required.
+ *     2. To prevent retransmits for TCP/TLS, we set scbp->retx_counter
+ *        to Max value.
+ *     3. For unreliable tranport: we SHOULD start timer E (= T1),
+ *        retransmits are required.
+ *     This routine must only be invoked before sending a request for
+ *     the first time with valid args.
+ *
+ * @param[in] cb_p - pointer to header control block.
+ * @param[out] timeout_p - returns the value to be used to set a timer.
+ *
+ * @return none
+ *
+ * @pre     (cb_p != NULL) and (timeout_p != NULL)
+ */
+void ccsip_common_util_set_retry_settings (ccsip_common_cb_t *cb_p, int *timeout_p)
+{
+    uint32_t max_retx = 0;
+    const char *transport = NULL;
+
+    *timeout_p = 0;
+    cb_p->retx_flag = TRUE;
+    config_get_value(CFGID_TIMER_T1, timeout_p, sizeof(*timeout_p));
+
+    transport = sipTransportGetTransportType(cb_p->dn_line, TRUE, NULL);
+    if (transport) {
+        if (strcmp(transport, "UDP") == 0) {
+            cb_p->retx_counter = 0;
+        } else {
+            config_get_value(CFGID_SIP_RETX, &max_retx, sizeof(max_retx));
+            if (max_retx > MAX_NON_INVITE_RETRY_ATTEMPTS) {
+                max_retx = MAX_NON_INVITE_RETRY_ATTEMPTS;
+            }
+            cb_p->retx_counter = max_retx;
+            (*timeout_p) = (64 * (*timeout_p));
+        }
+    }
+}
+
+
+/**
+ * This function will generate authorization header value.
+ *
+ * @param[in] pSipMessage - pointer to sipMessage_t
+ * @param[in] cb_p - pointer to header control block.
+ * @param[in] rsp_method - response method
+ * @param[in] response_code - response code
+ * @param[in] uri - uri
+ *
+ * @return TRUE if it is successful.
+ *
+ * @pre     (cb_p != NULL) and (pSipMessage != NULL) and (rsp_method != NULL) and (uri != NULL)
+ */
+boolean ccsip_common_util_generate_auth (sipMessage_t *pSipMessage, ccsip_common_cb_t *cb_p,
+                                         const char *rsp_method, int response_code, char *uri)
+{
+    static const char fname[] = "ccsip_common_util_generate_auth";
+    const char     *authenticate = NULL;
+    credentials_t   credentials;
+    sip_authen_t   *sip_authen = NULL;
+    char           *author_str = NULL;
+
+    if (!(cb_p->authen.cred_type & CRED_LINE)) {
+        cb_p->authen.cred_type |= CRED_LINE;
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "configured credentials for line %d not accepeted. Verify the config\n",
+                          fname, cb_p->dn_line);
+        return FALSE;
+    }
+
+    /*
+     * get authname & password from configuration.
+     */
+    cred_get_line_credentials(cb_p->dn_line, &credentials,
+                              sizeof(credentials.id),
+                              sizeof(credentials.pw));
+    /*
+     * Extract Authenticate/Proxy-Authenticate header from the message.
+     */
+    authenticate = sippmh_get_header_val(pSipMessage, AUTH_HDR(response_code), NULL);
+    if (authenticate == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"%s header missing in the %d response\n",
+                          fname, AUTH_HDR_STR(response_code), response_code);
+        return FALSE;
+    }
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Authenticate header %s = %s\n", DEB_F_PREFIX_ARGS(SIP_AUTH, fname), AUTH_HDR_STR(response_code), authenticate);
+    /*
+     * Parse Authenticate header.
+     */
+    sip_authen = sippmh_parse_authenticate(authenticate);
+    if (sip_authen == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"%s:%s header formatted incorrectly in the %d response\n",
+                          fname, AUTH_HDR_STR(response_code), authenticate, response_code);
+        return FALSE;
+    }
+    cb_p->authen.new_flag = FALSE;
+    cb_p->authen.cnonce[0] = '\0';
+    /*
+     * Generate Authorization string.
+     */
+    if (sipSPIGenerateAuthorizationResponse(sip_authen,
+                                            uri,
+                                            rsp_method,
+                                            credentials.id,
+                                            credentials.pw,
+                                            &author_str,
+                                            &(cb_p->authen.nc_count),
+                                            NULL) == TRUE) {
+
+        if (cb_p->authen.authorization != NULL) {
+            cpr_free(cb_p->authen.authorization);
+            cb_p->authen.authorization = NULL;
+        }
+
+        if (cb_p->authen.sip_authen != NULL) {
+            sippmh_free_authen(cb_p->authen.sip_authen);
+            cb_p->authen.sip_authen = NULL;
+        }
+
+        cb_p->authen.authorization = (char *)
+        cpr_malloc(strlen(author_str) * sizeof(char) + 1);
+
+        /*
+         * Cache the Authorization header so that it can be
+         * used for later requests
+         */
+        if (cb_p->authen.authorization != NULL) {
+            memcpy(cb_p->authen.authorization, author_str,
+                   strlen(author_str) * sizeof(char) + 1);
+            cb_p->authen.status_code = response_code;
+            cb_p->authen.sip_authen = sip_authen;
+        }
+
+        cpr_free(author_str);
+    } else {
+         CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Authorization header build unsuccessful\n", fname);
+         sippmh_free_authen(sip_authen);
+         return FALSE;
+    }
+    return TRUE;
+}
+
+/**
+ * This function will extract user part from sip From header.
+ *
+ * @param[in] pSipMessage - pointer to sipMessage_t
+ * @param[out] entity - pointer to user buffer.
+ *
+ * @return void
+ *
+ * @pre     (pSipMessage != NULL) and (entity != NULL)
+ */
+void ccsip_util_get_from_entity (sipMessage_t *pSipMessage, char *entity)
+{
+    const char     *sip_from = NULL;
+    sipLocation_t  *from_loc = NULL;
+
+    sip_from = sippmh_get_cached_header_val(pSipMessage, FROM);
+    if (sip_from != NULL) {
+        from_loc = sippmh_parse_from_or_to((char *) sip_from, TRUE);
+        if ((from_loc) && (from_loc->genUrl->schema == URL_TYPE_SIP) && (from_loc->genUrl->u.sipUrl->user)) {
+            sstrncpy(entity, from_loc->genUrl->u.sipUrl->user, CC_MAX_DIALSTRING_LEN);
+        }
+    }
+    if (from_loc) {
+        sippmh_free_location(from_loc);
+    }
+}
+
+/**
+ * This function will extract user part from sip URL.
+ *
+ * @param[in] url - pointer to URL
+ * @param[out] user - pointer to user buffer.
+ *
+ * @return void
+ *
+ * @pre     (url != NULL) and (user != NULL)
+ */
+void ccsip_util_extract_user (char *url, char *user)
+{
+    genUrl_t *genUrl = NULL;
+
+    genUrl = sippmh_parse_url(url, TRUE);
+    if (genUrl != NULL) {
+        sstrncpy(user, genUrl->u.sipUrl->user, CC_MAX_DIALSTRING_LEN);
+        sippmh_genurl_free(genUrl);
+    }
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_core.c b/libs/sipcc/core/sipstack/ccsip_core.c
new file mode 100644 (file)
index 0000000..e0807b3
--- /dev/null
@@ -0,0 +1,12298 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_in.h"
+#include "cpr_rand.h"
+
+#include "ccsip_core.h"
+#include "text_strings.h"
+#include "util_string.h"
+#include "ccsip_messaging.h"
+#include "ccsip_platform_udp.h"
+#include "ccsip_platform.h"
+#include "ccsip_macros.h"
+#include "ccsip_pmh.h"
+#include "ccsip_spi_utils.h"
+#include "phone_debug.h"
+#include "ccsip_register.h"
+#include "ccsip_credentials.h"
+#include "ccsip_callinfo.h"
+#include "ccsip_cc.h"
+#include "ccsip_task.h"
+#include "config.h"
+#include "string_lib.h"
+#include "dialplan.h"
+#include "fsm.h"
+#include "sip_interface_regmgr.h"
+#include "ccsip_subsmanager.h"
+#include "ccsip_publish.h"
+#include "sdp.h"
+#include "sip_common_transport.h"
+#include "sip_common_regmgr.h"
+#include "rtp_defs.h"
+#include "uiapi.h"
+#include "text_strings.h"
+#include "platform_api.h"
+#include "misc_util.h"
+
+
+/*
+ * OS specific hooks
+ */
+
+extern void sip_platform_handle_service_control_notify(sipServiceControl_t *scp);
+extern uint32_t IPNameCk(char *name, char *addr_error);
+
+
+#define ADD_TO_ARP_CACHE(dest_sip_addr)
+#define UNBIND_UDP_ICMP_HANDLER(udp_id)
+
+
+extern boolean sip_mode_quiet;
+
+extern void ccsip_debug_init(void);
+extern void shutdownCCAck(int action);
+void ccsip_remove_wlan_classifiers(void);
+
+#define USECALLMANAGER_LEN 14
+/*
+ * Needed to parse the alert-info header
+ */
+//CPR TODO: need reference for
+extern const char *tone_names[];
+const char *ring_names[] = {
+    "Bellcore-dr1",
+    "Bellcore-dr2",
+    "Bellcore-dr3",
+    "Bellcore-dr4",
+    "Bellcore-dr5"
+};
+
+
+/* Forward function declarations */
+static int sip_sm_request_check_and_store(ccsipCCB_t *ccb, sipMessage_t *request,
+                                               sipMethod_t request_method,
+                                               boolean midcall,
+                                               uint16_t *request_check_reason_code,
+                                               char *request_check_reason_phrase,
+                                               boolean store_invite);
+void sip_sm_update_to_from_on_callsetup_finalresponse(ccsipCCB_t *ccb,
+                                                                 sipMessage_t *response);
+void sip_sm_update_contact_recordroute(ccsipCCB_t *ccb, sipMessage_t *response,
+                                                  int response_code, boolean midcall);
+static boolean ccsip_set_replace_info(ccsipCCB_t *ccb, cc_setup_t * setup);
+static boolean ccsip_handle_cc_select_event(sipSMEvent_t *sip_sm_event);
+static boolean ccsip_handle_cc_b2bjoin_event(sipSMEvent_t *sip_sm_event);
+static void ccsip_set_join_info(ccsipCCB_t *ccb, cc_setup_t * setup);
+static boolean ccsip_get_join_info(ccsipCCB_t *ccb, sipMessage_t *request);
+static char *ccsip_find_preallocated_sip_call_id(line_t dn_line);
+static void ccsip_free_preallocated_sip_call_id(line_t dn_line);
+static boolean ccsip_handle_cc_hook_event(sipSMEvent_t *sip_sm_event);
+
+extern cc_int32_t dnsGetHostByName (const char *hname, cpr_ip_addr_t *ipaddr_ptr, cc_int32_t timeout, cc_int32_t retries);
+
+//CPR TODO: need reference for
+extern char *Basic_is_phone_forwarded(line_t line);
+
+/* External Declarations */
+extern sipPlatformUITimer_t sipPlatformUISMTimers[];
+extern sipGlobal_t sip;
+extern sipCallHistory_t gCallHistory[];
+
+/* Globals */
+int      dns_error_code;  // Global DNS error code value
+uint16_t server_caps = 0; // Server capabilities
+boolean  sip_reg_all_failed;
+
+ccsipGlobInfo_t  gGlobInfo;
+sipCallHistory_t gCallHistory[MAX_TEL_LINES];
+
+typedef struct {
+    int16_t sipValidEvent;
+    int16_t actionIndex;
+} subStateEvent_t;
+
+#define MAX_STATE_EVENTS 13
+typedef struct {
+    int16_t sipState;
+    subStateEvent_t validEvent[MAX_STATE_EVENTS];
+} sipSMfunctable_t;
+
+static char *preAllocatedSipCallID[MAX_REG_LINES] = { NULL };
+static char *preAllocatedTag[MAX_REG_LINES] = { NULL };
+boolean g_disable_mass_reg_debug_print = FALSE;
+
+static const sipSMfunctable_t g_sip_table[SIP_STATE_END - SIP_STATE_BASE + 1] =
+{
+    /*
+     * SIP_STATE_IDLE
+     */
+    { SIP_STATE_IDLE,
+        {
+      /* E_SIP_INVITE              ccsip_handle_idle_ev_sip_invite, */
+      {E_SIP_INVITE, H_IDLE_EV_SIP_INVITE},
+      /* E_CC_SETUP                ccsip_handle_idle_ev_cc_setup, */
+      {E_CC_SETUP, H_IDLE_EV_CC_SETUP},
+      /* E_SIP_NOTIFY              ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+      /* E_CC_FEATURE              ccsip_handle_default_ev_cc_feature */
+      {E_CC_FEATURE, H_DEFAULT_EV_CC_FEATURE},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+    /*
+     * SIP_STATE_SENT_INVITE
+     */
+    {SIP_STATE_SENT_INVITE,
+     {
+      /* E_SIP_1xx                   ccsip_handle_sentinvite_ev_sip_1xx, */
+      {E_SIP_1xx, H_SENTINVITE_EV_SIP_1XX},
+      /* E_SIP_2xx                   ccsip_handle_sentinvite_ev_sip_2xx, */
+      {E_SIP_2xx, H_SENTINVITE_EV_SIP_2XX},
+      /* E_SIP_3xx                   ccsip_handle_sentinvite_ev_sip_3xx */
+      {E_SIP_3xx, H_SENTINVITE_EV_SIP_3XX},
+      /* E_SIP_FAILURE_RESPONSE      ccsip_handle_sentinvite_ev_sip_fxx, */
+      {E_SIP_FAILURE_RESPONSE, H_SENTINVITE_EV_SIP_FXX},
+      /* E_CC_RELEASE                ccsip_handle_disconnect_local_early */
+      {E_CC_RELEASE, H_DISCONNECT_LOCAL_EARLY},
+      /* E_SIP_INV_EXPIRES_TIMER     ccsip_handle_disconnect_local_early */
+      {E_SIP_INV_EXPIRES_TIMER, H_DISCONNECT_LOCAL_EARLY},
+      /* E_SIP_UPDATE                ccsip_handle_early_ev_sip_update */
+      {E_SIP_UPDATE, H_EARLY_EV_SIP_UPDATE},
+      /* E_SIP_UPDATE_RESPONSE       ccsip_handle_early_ev_sip_update_response */
+      {E_SIP_UPDATE_RESPONSE, H_EARLY_EV_SIP_UPDATE_RESPONSE},
+      /* E_CC_UPDATE                 ccsip_handle_early_ev_cc_feature */
+      {E_CC_FEATURE, H_EARLY_EV_CC_FEATURE},
+      /* E_CC_FEATURE_ACK            ccsip_handle_early_ev_cc_feature_ack */
+      {E_CC_FEATURE_ACK, H_EARLY_EV_CC_FEATURE_ACK},
+      /* E_SIP_NOTIFY                ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+
+    /*
+     * SIP_STATE_SENT_INVITE_CONNECTED
+     */
+    {SIP_STATE_SENT_INVITE_CONNECTED,
+     {
+      /* E_SIP_BYE                   ccsip_handle_disconnect_remote */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_CC_CONNECTED_ACK          ccsip_handle_sentinviteconnected_ev_cc_connected_ack, */
+      {E_CC_CONNECTED_ACK, H_SENTINVITECONNECTED_EV_CC_CONNECTED_ACK},
+      /* E_CC_RELEASE                ccsip_handle_disconnect_local,                       */
+      {E_CC_RELEASE, H_DISCONNECT_LOCAL},
+      /* E_SIP_UPDATE                ccsip_handle_early_ev_sip_update */
+      {E_SIP_UPDATE, H_EARLY_EV_SIP_UPDATE},
+      /* E_SIP_UPDATE_RESPONSE       ccsip_handle_early_ev_sip_update_response */
+      {E_SIP_UPDATE_RESPONSE, H_EARLY_EV_SIP_UPDATE_RESPONSE},
+      /* E_CC_UPDATE                 ccsip_handle_early_ev_cc_feature */
+      {E_CC_FEATURE, H_EARLY_EV_CC_FEATURE},
+      /* E_CC_FEATURE_ACK            ccsip_handle_early_ev_cc_feature_ack */
+      {E_CC_FEATURE_ACK, H_EARLY_EV_CC_FEATURE_ACK},
+      /* E_SIP_NOTIFY                ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+    /*
+     * SIP_STATE_RECV_INVITE
+     */
+    {SIP_STATE_RECV_INVITE,
+     {
+      /* E_SIP_BYE                      ccsip_handle_disconnect_remote */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_SIP_CANCEL                   ccsip_handle_disconnect_remote,          */
+      {E_SIP_CANCEL, H_DISCONNECT_REMOTE},
+      /* E_CC_SETUP_ACK                 ccsip_handle_recvinvite_ev_cc_setup_ack, */
+      {E_CC_SETUP_ACK, H_RECVINVITE_EV_CC_SETUP_ACK},
+      /* E_CC_PROCEEDING                ccsip_handle_recvinvite_ev_cc_proceeding, */
+      {E_CC_PROCEEDING, H_RECVINVITE_EV_CC_PROCEEDING},
+      /* E_CC_ALERTING                  ccsip_handle_recvinvite_ev_cc_alerting,  */
+      {E_CC_ALERTING, H_RECVINVITE_EV_CC_ALERTING},
+      /* E_CC_CONNECTED                 ccsip_handle_recvinvite_ev_cc_connected, */
+      {E_CC_CONNECTED, H_RECVINVITE_EV_CC_CONNECTED},
+      /* E_CC_RELEASE                   ccsip_handle_disconnect_local_unanswered, */
+      {E_CC_RELEASE, H_DISCONNECT_LOCAL_UNANSWERED},
+      /* E_SIP_INV_LOCALEXPIRES_TIMER   ccsip_handle_localexpires_timer */
+      {E_SIP_INV_LOCALEXPIRES_TIMER, H_HANDLE_LOCALEXPIRES_TIMER},
+      /* E_SIP_UPDATE                   ccsip_handle_early_ev_sip_update */
+      {E_SIP_UPDATE, H_EARLY_EV_SIP_UPDATE},
+      /* E_SIP_UPDATE_RESPONSE          ccsip_handle_early_ev_sip_update_response */
+      {E_SIP_UPDATE_RESPONSE, H_EARLY_EV_SIP_UPDATE_RESPONSE},
+      /* E_CC_UPDATE                    ccsip_handle_early_ev_cc_feature */
+      {E_CC_FEATURE, H_EARLY_EV_CC_FEATURE},
+      /* E_CC_FEATURE_ACK               ccsip_handle_early_ev_cc_feature_ack */
+      {E_CC_FEATURE_ACK, H_EARLY_EV_CC_FEATURE_ACK},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY}
+
+      /* Initializing any events which are not used to Invalid events */
+      }
+     },
+
+    /*
+     * SIP_STATE_RECV_INVITE_PROCEEDING
+     */
+    {SIP_STATE_RECV_INVITE_PROCEEDING,
+     {
+      /* E_SIP_BYE                      ccsip_handle_disconnect_remote,          */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_SIP_CANCEL                   ccsip_handle_disconnect_remote           */
+      {E_SIP_CANCEL, H_DISCONNECT_REMOTE},
+      /* E_CC_ALERTING                  ccsip_handle_recvinvite_ev_cc_alerting,  */
+      {E_CC_ALERTING, H_RECVINVITE_EV_CC_ALERTING},
+      /* E_CC_CONNECTED                 ccsip_handle_recvinvite_ev_cc_connected, */
+      {E_CC_CONNECTED, H_RECVINVITE_EV_CC_CONNECTED},
+      /* E_CC_RELEASE                   ccsip_handle_disconnect_local_unanswered, */
+      {E_CC_RELEASE, H_DISCONNECT_LOCAL_UNANSWERED},
+      /* E_SIP_INV_LOCALEXPIRES_TIMER   ccsip_handle_localexpires_timer */
+      {E_SIP_INV_LOCALEXPIRES_TIMER, H_HANDLE_LOCALEXPIRES_TIMER},
+      /* E_SIP_UPDATE                   ccsip_handle_early_ev_sip_update */
+      {E_SIP_UPDATE, H_EARLY_EV_SIP_UPDATE},
+      /* E_SIP_UPDATE_RESPONSE          ccsip_handle_early_ev_sip_update_response */
+      {E_SIP_UPDATE_RESPONSE, H_EARLY_EV_SIP_UPDATE_RESPONSE},
+      /* E_CC_UPDATE                    ccsip_handle_early_ev_cc_feature */
+      {E_CC_FEATURE, H_EARLY_EV_CC_FEATURE},
+      /* E_CC_FEATURE_ACK               ccsip_handle_early_ev_cc_feature_ack */
+      {E_CC_FEATURE_ACK, H_EARLY_EV_CC_FEATURE_ACK},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+    /*
+     * SIP_STATE_RECV_INVITE_ALERTING
+     */
+    {SIP_STATE_RECV_INVITE_ALERTING,
+     {
+      /* E_SIP_BYE                      ccsip_handle_disconnect_remote,          */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_SIP_CANCEL                   ccsip_handle_disconnect_remote,          */
+      {E_SIP_CANCEL, H_DISCONNECT_REMOTE},
+      /* E_CC_CONNECTED                 ccsip_handle_recvinvite_ev_cc_connected, */
+      {E_CC_CONNECTED, H_RECVINVITE_EV_CC_CONNECTED},
+      /* E_CC_RELEASE                   ccsip_handle_disconnect_local_unanswered, */
+      {E_CC_RELEASE, H_DISCONNECT_LOCAL_UNANSWERED},
+      /* E_SIP_INV_LOCALEXPIRES_TIMER   ccsip_handle_localexpires_timer */
+      {E_SIP_INV_LOCALEXPIRES_TIMER, H_HANDLE_LOCALEXPIRES_TIMER},
+      /* E_SIP_UPDATE                   ccsip_handle_early_ev_sip_update */
+      {E_SIP_UPDATE, H_EARLY_EV_SIP_UPDATE},
+      /* E_SIP_UPDATE_RESPONSE          ccsip_handle_early_ev_sip_update_response */
+      {E_SIP_UPDATE_RESPONSE, H_EARLY_EV_SIP_UPDATE_RESPONSE},
+      /* E_CC_UPDATE                    ccsip_handle_early_ev_cc_feature */
+      {E_CC_FEATURE, H_EARLY_EV_CC_FEATURE},
+      /* E_CC_FEATURE_ACK               ccsip_handle_early_ev_cc_feature_ack */
+      {E_CC_FEATURE_ACK, H_EARLY_EV_CC_FEATURE_ACK},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+      /* E_SIP_2xx                      ccsip_handle_recvinvite_ev_sip_2xx */
+      {E_SIP_2xx, H_RECVINVITE_EV_SIP_2XX},
+      /* E_SIP_FAILURE_RESPONSE         ccsip_handle_sentinvite_ev_sip_fxx, */
+      {E_SIP_FAILURE_RESPONSE, H_SENTINVITE_EV_SIP_FXX},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+    /*
+     * SIP_STATE_RECV_INVITE_CONNECTED
+     */
+    {SIP_STATE_RECV_INVITE_CONNECTED,
+     {
+      /* E_SIP_ACK                      ccsip_handle_recvinvite_ev_sip_ack, */
+      {E_SIP_ACK, H_RECVINVITE_EV_SIP_ACK},
+      /* E_SIP_BYE                      ccsip_handle_disconnect_remote,    */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_CC_RELEASE                   ccsip_handle_disconnect_local,     */
+      {E_CC_RELEASE, H_DISCONNECT_LOCAL},
+      /* E_SIP_UPDATE                   ccsip_handle_early_ev_sip_update */
+      {E_SIP_UPDATE, H_EARLY_EV_SIP_UPDATE},
+      /* E_SIP_UPDATE_RESPONSE          ccsip_handle_early_ev_sip_update_response */
+      {E_SIP_UPDATE_RESPONSE, H_EARLY_EV_SIP_UPDATE_RESPONSE},
+      /* E_CC_UPDATE                    ccsip_handle_early_ev_cc_feature */
+      {E_CC_FEATURE, H_EARLY_EV_CC_FEATURE},
+      /* E_CC_FEATURE_ACK               ccsip_handle_early_ev_cc_feature_ack */
+      {E_CC_FEATURE_ACK, H_EARLY_EV_CC_FEATURE_ACK},
+      /* E_SIP_INV_EXPIRES_TIMER        ccsip_handle_recvinvite_ev_expires_timer */
+      {E_SIP_INV_EXPIRES_TIMER, H_RECVINVITE_SENTOK_NO_SIP_ACK},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+
+    /*
+     * SIP_STATE_ACTIVE
+     */
+    {SIP_STATE_ACTIVE,
+     {
+      /* E_SIP_INVITE                   ccsip_handle_active_ev_sip_invite, */
+      {E_SIP_INVITE, H_ACTIVE_EV_SIP_INVITE},
+      /* E_SIP_BYE                      ccsip_handle_disconnect_remote,    */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_SIP_2xx                      ccsip_handle_active_2xx, */
+      {E_SIP_2xx, H_ACTIVE_2xx},
+      /* E_SIP_REFER                    ccsip_handle_refer_sip_message,       */
+      {E_SIP_REFER, H_REFER_SIP_MESSAGE},
+      /* E_SIP_FAILURE_RESPONSE         ccsip_handle_sentinvite_ev_sip_fxx, */
+      {E_SIP_FAILURE_RESPONSE, H_SENTINVITE_EV_SIP_FXX},
+      /* E_CC_RELEASE                   ccsip_handle_disconnect_local,     */
+      {E_CC_RELEASE, H_DISCONNECT_LOCAL},
+      /* E_CC_FEATURE                   ccsip_handle_active_ev_cc_feature, */
+      {E_CC_FEATURE, H_ACTIVE_EV_CC_FEATURE},
+      /* E_CC_FEATURE_ACK               ccsip_handle_active_ev_cc_feature_ack, */
+      {E_CC_FEATURE_ACK, H_ACTIVE_EV_CC_FEATURE_ACK},
+      /* E_SIP_UPDATE                   ccsip_handle_active_ev_sip_update */
+      {E_SIP_UPDATE, H_CONFIRM_EV_SIP_UPDATE},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+      /* E_CC_INFO                      ccsip_handle_ev_cc_info */
+      {E_CC_INFO, H_EV_CC_INFO},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+    /*
+     * SIP_STATE_SENT_MIDCALL_INVITE
+     */
+    {SIP_STATE_SENT_MIDCALL_INVITE,
+     {
+      /* E_SIP_INVITE                   ccsip_handle_active_ev_sip_invite,    */
+      {E_SIP_INVITE, H_ACTIVE_EV_SIP_INVITE},
+      /* E_SIP_BYE                      ccsip_handle_disconnect_remote, */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_SIP_2xx                      ccsip_handle_sentinvite_midcall_ev_sip_2xx, */
+      {E_SIP_2xx, H_SENTINVITE_MIDCALL_EV_SIP_2XX},
+      /* E_SIP_FAILURE_RESPONSE         ccsip_handle_sentinvite_ev_sip_fxx, */
+      {E_SIP_FAILURE_RESPONSE, H_SENTINVITE_EV_SIP_FXX},
+      /* E_SIP_INV_EXPIRES_TIMER     ccsip_handle_disconnect_local */
+      {E_SIP_INV_EXPIRES_TIMER, H_DISCONNECT_LOCAL},
+      /* E_CC_RELEASE                   ccsip_handle_disconnect_local, */
+      {E_CC_RELEASE, H_DISCONNECT_LOCAL},
+      /* E_CC_FEATURE                   ccsip_handle_sentinvite_midcall_ev_cc_feature, */
+      {E_CC_FEATURE, H_SENTINVITE_MIDCALL_EV_CC_FEATURE},
+      /* E_SIP_UPDATE                   ccsip_handle_active_ev_sip_update */
+      {E_SIP_UPDATE, H_CONFIRM_EV_SIP_UPDATE},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+      /* E_CC_INFO                      ccsip_handle_ev_cc_info */
+      {E_CC_INFO, H_EV_CC_INFO},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+    /*
+     * SIP_STATE_RECV_MIDCALL_INVITE_CCFEATUREACK_PENDING
+     */
+    {SIP_STATE_RECV_MIDCALL_INVITE_CCFEATUREACK_PENDING,
+     {
+      /* E_SIP_BYE                      ccsip_handle_disconnect_remote, */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_CC_RELEASE                   ccsip_handle_disconnect_media_change, */
+      {E_CC_RELEASE, H_DISCONNECT_MEDIA_CHANGE},
+      /* E_CC_FEATURE_ACK               ccsip_handle_recvmidcallinvite_ccfeatureackpending_ev_cc_feature_ack, */
+      {E_CC_FEATURE_ACK, H_RECVMIDCALLINVITE_CCFEATUREACKPENDING_EV_CC_FEATURE_ACK},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+      /* E_CC_FEATURE,                  ccsip_handle_default_recvreq_ack_pending_ev_cc_feature */
+      {E_CC_FEATURE, H_DEFAULT_RECVREQ_ACK_PENDING_EV_CC_FEATURE},
+      /* E_CC_INFO                      ccsip_handle_ev_cc_info */
+      {E_CC_INFO, H_EV_CC_INFO},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+
+    /*
+     * SIP_STATE_RECV_MIDCALLINVITE_SIPACK_PENDING
+     */
+    {SIP_STATE_RECV_MIDCALL_INVITE_SIPACK_PENDING,
+     {
+      /* E_SIP_ACK                      ccsip_handle_recvmidcallinvite_sipackpending_ev_sip_ack, */
+      {E_SIP_ACK, H_RECVMIDCALLINVITE_SIPACKPENDING_EV_SIP_ACK},
+      /* E_SIP_BYE                      ccsip_handle_disconnect_remote, */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_CC_RELEASE                   ccsip_handle_disconnect_local, */
+      {E_CC_RELEASE, H_DISCONNECT_LOCAL},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+      /* E_CC_FEATURE,                  ccsip_handle_default_recvreq_ack_pending_ev_cc_feature */
+      {E_CC_FEATURE, H_DEFAULT_RECVREQ_ACK_PENDING_EV_CC_FEATURE},
+      /* E_CC_INFO                      ccsip_handle_ev_cc_info */
+      {E_CC_INFO, H_EV_CC_INFO},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+    /*
+     * SIP_STATE_RELEASE
+     */
+    {SIP_STATE_RELEASE,
+     {
+      /* E_SIP_BYE,                     ccsip_handle_release_ev_sip_bye */
+      {E_SIP_BYE, H_BYE_RELEASE},
+      /* E_SIP_ACK                      ccsip_handle_recv_error_response_ev_sip_ack, */
+      {E_SIP_ACK, H_RECV_ERR_EV_SIP_ACK},
+      /* E_SIP_INVITE                   ccsip_handle_sentbye_recvd_invite, */
+      {E_SIP_INVITE, H_SENTBYE_EV_SIP_INVITE},
+      /* E_SIP_1xx                      ccsip_handle_sentbye_ev_sip_1xx, */
+      {E_SIP_1xx, H_SENTBYE_EV_SIP_1XX},
+      /* E_SIP_2xx                      ccsip_handle_sentbye_ev_sip_2xx, */
+      {E_SIP_2xx, H_SENTBYE_EV_SIP_2XX},
+      /* E_SIP_FAILURE_RESPONSE         ccsip_handle_sentbye_ev_sip_fxx, */
+      {E_SIP_FAILURE_RESPONSE, H_SENTBYE_EV_SIP_FXX},
+      /* E_SIP_SUPERVISION_DISCONNECT_TIMER
+       * ccsip_handle_sendbye_ev_supervision_disconnect, */
+      {E_SIP_SUPERVISION_DISCONNECT_TIMER, H_SENTBYE_SUPERVISION_DISCONNECT_TIMER},
+      /* E_CC_RELEASE_COMPLETE,         ccsip_handle_release_complete, */
+      {E_CC_RELEASE_COMPLETE, H_RELEASE_COMPLETE},
+      /* E_SIP_UPDATE                   ccsip_handle_sentbye_recvd_invite, */
+      {E_SIP_UPDATE, H_SENTBYE_EV_SIP_INVITE},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+      /* E_CC_FEATURE                   ccsip_handle_release_ev_cc_feature, */
+      {E_CC_FEATURE, H_RELEASE_EV_CC_FEATURE},
+      /* E_CC_RELEASE,                  ccsip_handle_release_ev_release, */
+      {E_CC_RELEASE, H_RELEASE_EV_RELEASE},
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+
+    /*
+     * SIP_STATE_BLIND_XFER_PENDING,
+     */
+
+    {SIP_STATE_BLIND_XFER_PENDING,
+     {
+      /* E_SIP_BYE,                     ccsip_handle_release_ev_sip_bye */
+      {E_SIP_BYE, H_BYE_RELEASE},
+      /* E_SIP_2xx                      ccsip_handle_sentblindntfy_ev_sip_2xx, */
+      {E_SIP_2xx, H_SENT_BLINDNTFY},
+      /* E_SIP_FAILURE_RESPONSE         ccsip_handle_sentbye_ev_sip_fxx, */
+      {E_SIP_FAILURE_RESPONSE, H_SENTBYE_EV_SIP_FXX},
+      /* E_CC_FEATURE                   ccsip_handle_send_blind_notify, */
+      {E_CC_FEATURE, H_BLIND_NOTIFY},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+
+      }
+     },
+
+    {SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING,
+     {
+      /* E_SIP_TIMER,                   ccsip_handle_default_sip_timer */
+      {E_SIP_TIMER, H_DEFAULT_SIP_TIMER},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+      /* E_CC_FEATURE              ccsip_handle_default_ev_cc_feature */
+      {E_CC_FEATURE, H_DEFAULT_EV_CC_FEATURE},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+
+    {SIP_STATE_SENT_OOD_REFER,
+     {
+      {E_SIP_1xx, H_OOD_REFER_RESPONSE_EV_SIP_1xx},
+      {E_SIP_2xx, H_OOD_REFER_RESPONSE_EV_SIP_2xx},
+      {E_SIP_3xx, H_OOD_REFER_RESPONSE_EV_SIP_fxx},
+      {E_SIP_FAILURE_RESPONSE, H_OOD_REFER_RESPONSE_EV_SIP_fxx},
+      /* E_CC_INFO                      ccsip_handle_ev_cc_info */
+      {E_CC_INFO, H_EV_CC_INFO},
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+
+    {SIP_STATE_RECV_UPDATEMEDIA_CCFEATUREACK_PENDING,
+     {
+      /* E_SIP_BYE                      ccsip_handle_disconnect_remote, */
+      {E_SIP_BYE, H_DISCONNECT_REMOTE},
+      /* E_CC_RELEASE                   ccsip_handle_disconnect_media_change, */
+      {E_CC_RELEASE, H_DISCONNECT_MEDIA_CHANGE},
+      /* E_CC_FEATURE_ACK               ccsip_handle_recvupdatenewmedia_ccfeatureackpending_ev_cc_feature_ack, */
+      {E_CC_FEATURE_ACK, H_RECVUPDATEMEDIA_CCFEATUREACKPENDING_EV_CC_FEATURE_ACK},
+      /* E_SIP_NOTIFY                   ccsip_handle_unsolicited_notify */
+      {E_SIP_NOTIFY, H_EV_SIP_UNSOLICITED_NOTIFY},
+      /* E_CC_FEATURE,                  ccsip_handle_default_recvreq_ack_pending_ev_cc_feature */
+      {E_CC_FEATURE, H_DEFAULT_RECVREQ_ACK_PENDING_EV_CC_FEATURE},
+      /* E_CC_INFO                      ccsip_handle_ev_cc_info */
+      {E_CC_INFO, H_EV_CC_INFO},
+
+      /* Initializing any events which are not used to Invalid events */
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT},
+      {H_INVALID_EVENT, H_DEFAULT}
+      }
+     },
+
+};
+
+static const sipSMEventActionFn_t
+    gSIPHandlerTable[SIPSPI_EV_INDEX_END - SIPSPI_EV_INDEX_BASE + 1] = {
+
+    /*0*//* H_IDLE_EV_SIP_INVITE,                                      */
+        ccsip_handle_idle_ev_sip_invite,
+
+    /*1*//* H_IDLE_EV_CC_SETUP,                                        */
+        ccsip_handle_idle_ev_cc_setup,
+
+    /*2*//* H_SENTINVITE_EV_SIP_1XX,                                   */
+        ccsip_handle_sentinvite_ev_sip_1xx,
+
+    /*3*//* H_SENTINVITE_EV_SIP_2XX,                                   */
+        ccsip_handle_sentinvite_ev_sip_2xx,
+
+    /*4*//* H_SENTINVITE_EV_SIP_FXX,                                   */
+        ccsip_handle_sentinvite_ev_sip_fxx,
+
+    /*5*//* H_DISCONNECT_LOCAL_EARLY,                                  */
+        ccsip_handle_disconnect_local_early,
+
+    /*6*//* H_DISCONNECT_REMOTE,                                       */
+        ccsip_handle_disconnect_remote,
+
+    /*7*//* H_SENTINVITECONNECTED_EV_CC_CONNECTED_ACK,                 */
+        ccsip_handle_sentinviteconnected_ev_cc_connected_ack,
+
+    /*8*//* H_DISCONNECT_LOCAL,                                        */
+        ccsip_handle_disconnect_local,
+
+    /*9*//* H_RECVINVITE_EV_CC_SETUP_ACK,                              */
+        ccsip_handle_recvinvite_ev_cc_setup_ack,
+
+    /*10*//* H_RECVINVITE_EV_CC_PROCEEDING,                            */
+        ccsip_handle_recvinvite_ev_cc_proceeding,
+
+    /*11*//* H_RECVINVITE_EV_CC_ALERTING,                              */
+        ccsip_handle_recvinvite_ev_cc_alerting,
+
+    /*12*//* H_RECVINVITE_EV_CC_CONNECTED,                             */
+        ccsip_handle_recvinvite_ev_cc_connected,
+
+    /*13*//* H_DISCONNECT_LOCAL_UNANSWERED,                            */
+        ccsip_handle_disconnect_local_unanswered,
+
+    /*14*//* H_RECVINVITE_EV_SIP_ACK,                                  */
+        ccsip_handle_recvinvite_ev_sip_ack,
+
+    /*15*//* H_ACTIVE_EV_SIP_INVITE,                                   */
+        ccsip_handle_active_ev_sip_invite,
+
+    /*16*//* H_ACTIVE_EV_CC_FEATURE,                                   */
+        ccsip_handle_active_ev_cc_feature,
+
+    /*17*//* H_ACCEPT_2XX,                                             */
+        ccsip_handle_accept_2xx,
+
+    /*18*//* H_REFER_SIP_MESSAGE,                                      */
+        ccsip_handle_refer_sip_message,
+
+    /*19*//* H_ACTIVE_EV_CC_FEATURE_ACK,                               */
+        ccsip_handle_active_ev_cc_feature_ack,
+
+    /*20*//* H_SENTINVITE_MIDCALL_EV_SIP_2XX,                          */
+        ccsip_handle_sentinvite_midcall_ev_sip_2xx,
+
+    /*21*//* H_SENTINVITE_MIDCALL_EV_CC_FEATURE,                       */
+        ccsip_handle_sentinvite_midcall_ev_cc_feature,
+
+    /*22*//* H_RECVMIDCALLINVITE_CCFEATUREACKPENDING_EV_CC_FEATURE_ACK */
+        ccsip_handle_recvmidcallinvite_ccfeatureackpending_ev_cc_feature_ack,
+
+    /*23*//* H_RECVMIDCALLINVITE__SIPACKPENDING_EV_SIP_ACK,            */
+        ccsip_handle_recvmidcallinvite_sipackpending_ev_sip_ack,
+
+    /*24*//* H_DEFAULT_SIP_MESSAGE,                                    */
+        ccsip_handle_default_sip_message,
+
+    /*25*//* H_DEFAULT_SIP_RESPONSE,                                   */
+        ccsip_handle_default_sip_response,
+
+    /*26*//* H_DEFAULT,                                                */
+        ccsip_handle_default,
+
+    /*27*//* H_SIP_INV_EXPIRES_TIMER                                   */
+        ccsip_handle_disconnect_local_early,
+
+    /*28*//* H_SIP_OPTIONS,                                            */
+        ccsip_handle_process_in_call_options_request,
+
+    /*29*//* H_SENTINVITE_EV_SIP_3XX,                                  */
+        ccsip_handle_sentinvite_ev_sip_3xx,
+
+    /*30*//*H_RECV_ERR_EV_SIP_ACK                                      */
+        ccsip_handle_recv_error_response_ev_sip_ack,
+
+    /*31*//*H_SENTBYE_EV_SIP_2XX                                       */
+        ccsip_handle_sentbye_ev_sip_2xx,
+
+    /*32*//*H_SENTBYE_EV_SIP_1XX                                       */
+        ccsip_handle_sentbye_ev_sip_1xx,
+
+    /*33*//*H_SENTBYE_EV_SIP_FXX                                       */
+        ccsip_handle_sentbye_ev_sip_fxx,
+
+    /*34*//*H_SENTBYE_EV_SIP_INVITE                                    */
+        ccsip_handle_sentbye_recvd_invite,
+
+    /*35*//*H_SENTBYE_SUPERVISION_DISCONNECT_TIMER                     */
+        ccsip_handle_sendbye_ev_supervision_disconnect,
+
+    /*36*//*H_RELEASE_COMPLETE                                         */
+        ccsip_handle_release_complete,
+
+    /*37*//*H_ACTIVE_2xx                                               */
+        ccsip_handle_active_2xx,
+
+    /*38*//*H_BLIND_NOTIFY                                             */
+        ccsip_handle_send_blind_notify,
+
+    /*39*//*H_SENT_BLINDNTFY                                           */
+        ccsip_handle_sentblindntfy_ev_sip_2xx,
+
+    /*40*//*H_BYE_RELEASE,                                             */
+        ccsip_handle_release_ev_sip_bye,
+
+    /*41*//*H_HANDLE_LOCALEXPIRES_TIMER,                               */
+        ccsip_handle_localexpires_timer,
+
+    /*42*//*H_DEFAULT_SIP_TIMER                                        */
+        ccsip_handle_default_sip_timer,
+
+    /*43*//*H_EARLY_EV_SIP_UPDATE                                      */
+        ccsip_handle_early_ev_sip_update,
+
+    /*44*//*H_EARLY_EV_SIP_UPDATE_RESPONSE                             */
+        ccsip_handle_early_ev_sip_update_response,
+
+    /*45*//*H_EARLY_EV_CC_FEATURE                                      */
+        ccsip_handle_early_ev_cc_feature,
+
+    /*46*//*H_EARLY_EV_CC_FEATURE_ACK                                  */
+        ccsip_handle_early_ev_cc_feature_ack,
+
+    /*47*//*H_CONFIRM_EV_SIP_UPDATE                                    */
+        ccsip_handle_active_ev_sip_update,
+
+    /*48*//*H_RECVUPDATEMEDIA_CCFEATUREACKPENDING_EV_CC_FEATURE_ACK */
+        ccsip_handle_recvupdatemedia_ccfeatureackpending_ev_cc_feature_ack,
+
+    /*49*//*H_SIP_GLARE_AVOIDANCE_TIMER                                */
+        ccsip_handle_timer_glare_avoidance,
+
+    /*50*//*H_RECVINVITE_SENTOK_NO_SIP_ACK                             */
+        ccsip_handle_recvinvite_ev_expires_timer,
+
+    /*51*//*H_EV_SIP_UNSOLICITED_NOTIFY                                */
+        ccsip_handle_unsolicited_notify,
+
+    /*52*//*H_RECVINVITE_EV_SIP_2XX,                                   */
+        ccsip_handle_recvinvite_ev_sip_2xx,
+
+    /*53*//*H_ICMP_UNREACHABLE,                                        */
+        ccsip_handle_icmp_unreachable,
+
+    /*54*//*H_DISCONNECT_MEDIA_CHANGE,                                 */
+        ccsip_handle_disconnect_media_change,
+
+    /*55*//*H_DEFAULT_EV_CC_FEATURE,                                   */
+        ccsip_handle_default_ev_cc_feature,
+
+    /*56*//*H_DEFAULT_RECVREQ_ACK_PENDING_EV_CC_FEATURE                */
+        ccsip_handle_default_recvreq_ack_pending_ev_cc_feature,
+
+    /*57*//*H_OOD_REFER_RESPONSE_EV_SIP_1xx                            */
+        ccsip_handle_sent_ood_refer_ev_sip_1xx,
+
+    /*58*//*H_OOD_REFER_RESPONSE_EV_SIP_2xx                            */
+        ccsip_handle_sent_ood_refer_ev_sip_2xx,
+
+    /*59*//*H_OOD_REFER_RESPONSE_EV_SIP_fxx                            */
+        ccsip_handle_sent_ood_refer_ev_sip_fxx,
+
+    /*60*//* H_RELEASE_EV_CC_FEATURE,                                  */
+        ccsip_handle_release_ev_cc_feature,
+
+    /*61*//* H_EV_CC_INFO,                                             */
+        ccsip_handle_ev_cc_info,
+
+    /*62*//* H_RELEASE_EV_RELEASE,                                  */
+        ccsip_handle_release_ev_release,
+};
+
+static uint32_t get_callref(const char *tag) {
+    int32_t i, callref = 0;
+    const char * ref;
+
+    for ( i = (strlen(tag)-1); i >= 0; i--) {
+        ref = &tag[i];
+        if ( *ref == '-' ) {
+           sscanf ( ref, "-%d", &callref);
+           break;
+        }
+    }
+    return callref;
+}
+
+sipSMAction_t
+get_handler_index (sipSMStateType_t isipsmstate, sipSMEventType_t isipsmevent)
+{
+    int16_t i;
+
+    if ((isipsmstate < SIP_STATE_BASE) || (isipsmstate > SIP_STATE_END) ||
+        (isipsmevent < SIPSPI_EV_BASE) || (isipsmevent > SIPSPI_EV_END)) {
+        buginf("\nvalue of event passed isipsmevent=%d value of state = %d, "
+               "SIP_STATE_BASE = %d, SIP_STATE_END = %d, SIPSPI_EV_BASE = %d,"
+               " SIPSPI_EV_END = %d",
+               isipsmstate, isipsmevent, SIP_STATE_BASE, SIP_STATE_END,
+               SIPSPI_EV_BASE, SIPSPI_EV_END);
+
+        return H_INVALID_EVENT;
+    }
+
+    for (i = 0; i < MAX_STATE_EVENTS; i++) {
+        if (g_sip_table[isipsmstate].validEvent[i].sipValidEvent == (int16_t)isipsmevent) {
+            return ((sipSMAction_t) g_sip_table[isipsmstate].validEvent[i].actionIndex);
+        }
+    }
+    switch (isipsmevent) {
+    case E_SIP_INVITE:
+    case E_SIP_UPDATE:
+    case E_SIP_ACK:
+    case E_SIP_BYE:
+    case E_SIP_REFER:
+    case E_SIP_CANCEL:
+        return H_DEFAULT_SIP_MESSAGE;
+    case E_SIP_1xx:
+    case E_SIP_2xx:
+    case E_SIP_3xx:
+    case E_SIP_FAILURE_RESPONSE:
+        return H_DEFAULT_SIP_RESPONSE;
+    case E_SIP_TIMER:
+        return H_DEFAULT_SIP_TIMER;
+
+    case E_SIP_OPTIONS:
+        return H_SIP_OPTIONS;
+
+    case E_SIP_GLARE_AVOIDANCE_TIMER:
+        return H_SIP_GLARE_AVOIDANCE_TIMER;
+
+    case E_SIP_ICMP_UNREACHABLE:
+        return H_ICMP_UNREACHABLE;
+
+    default:
+        break;
+    }
+    return H_DEFAULT;
+}
+
+
+void
+sip_sm_change_state (ccsipCCB_t *ccb, sipSMStateType_t new_state)
+{
+    CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Change state %s -> %s\n",
+            DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->dn_line, ccb->gsm_id, "sip_sm_change_state"),
+            sip_util_state2string(ccb->state),
+            sip_util_state2string(new_state));
+
+    if (ccb->state == SIP_STATE_RELEASE &&
+        new_state == SIP_STATE_IDLE) {
+        /* Just add call marker in the log */
+        DEF_DEBUG("===================================================\n");
+    }
+
+    /*
+     * If we are moving out of SIP_STATE_RELEASE, then stop the
+     * supervision timer if it has been started
+     */
+    if (ccb->state == SIP_STATE_RELEASE) {
+        (void) sip_platform_supervision_disconnect_timer_stop(ccb->index);
+    }
+
+    ccb->state = new_state;
+
+    if (ccb->state == SIP_STATE_RELEASE) {
+        (void) sip_platform_supervision_disconnect_timer_start(SUPERVISION_DISCONNECT_TIMEOUT,
+                                                               ccb->index);
+    }
+}
+
+/*
+ *  Function: sip_util_extract_sdp()
+ *
+ *  Parameters: ccb - The current call control block
+ *              message - The sip message to parse
+ *
+ *  Description: This routine parses the indicated message for SDP.
+ *
+ *  Returns: sip_sdp_status_t - An enum which contains several return codes
+ *
+ *  Note:    The SIP_SDP_DNS_FAIL will not be returned from this
+ *           function because GSM also handles DNS look up. The rest of
+ *           this module should still check for the SIP_SDP_DNS_FAIL value
+ *           for the return code of this function. They should be kept just
+ *           in case that there is a need for SIP to check DNS for some
+ *           reason in the future.
+ */
+static sipsdp_status_t
+sip_util_extract_sdp (ccsipCCB_t *ccb, sipMessage_t *message)
+{
+    const char *fname = "sip_util_extract_sdp";
+    // const char *content_type;
+    uint8_t         content_type = 0;
+    int             content_length = 0;
+    cc_sdp_t       *sip_msg_sdp = NULL;
+    u16             i = 0;
+    sipsdp_status_t retval = SIP_SDP_NOT_PRESENT;
+    char           *sdp_data = NULL; // message->mesg_body;
+    uint16_t        num_m_lines;
+
+    /*
+     * Check to see if SDP is present in this message
+     */
+    // Go through the different body types to see if there is an
+    // SDP body
+    if (message->num_body_parts == 0) {
+        content_length = 0;
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"\nmultipart/mixed No SDP Found!\n", DEB_F_PREFIX_ARGS(SIP_SDP, fname));
+    } else {
+        for (i = 0; i < message->num_body_parts; i++) {
+            if (message->mesg_body[i].msgContentTypeValue ==
+                SIP_CONTENT_TYPE_SDP_VALUE) {
+                content_type = SIP_CONTENT_TYPE_SDP_VALUE;
+                content_length = message->mesg_body[i].msgLength;
+                sdp_data = message->mesg_body[i].msgBody;
+                break;
+            }
+        }
+    }
+
+    /*
+     * Check for content
+     */
+    if ((content_type != SIP_CONTENT_TYPE_SDP_VALUE) ||
+        (content_length <= 0)) {
+        /* there is no SDP content or no content */
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY), ccb->index,
+                          ccb->dn_line, fname, "No SDP");
+        return (SIP_SDP_NOT_PRESENT);
+    }
+
+    /*
+     * Allocate a SDP buffer to work with (only destination buffer is needed).
+     *
+     * Here we are handing in an empty string to designate that we are not
+     * in the context of a peerconnection object
+     */
+    sipsdp_src_dest_create("", CCSIP_DEST_SDP_BIT, &sip_msg_sdp);
+    if ((sip_msg_sdp == NULL) || (sip_msg_sdp->dest_sdp == NULL)) {
+        /* Unable to get SDP */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SDP_CREATE_BUF_ERROR),
+                          fname);
+        return (SIP_SDP_ERROR);
+    }
+    /*
+     * Pasrse the SDP body
+     */
+    if (sdp_parse(sip_msg_sdp->dest_sdp, &sdp_data,
+                  (uint16_t)content_length) != SDP_SUCCESS) {
+        /* unable to parse the SDP */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_PARSE_SDP_ERROR), fname);
+
+        sipsdp_src_dest_free(CCSIP_DEST_SDP_BIT, &sip_msg_sdp);
+        return (SIP_SDP_ERROR);
+    }
+
+    /*
+     * Verify that there are media lines present
+     */
+    num_m_lines = sdp_get_num_media_lines(sip_msg_sdp->dest_sdp);
+    if (num_m_lines == 0) {
+        /* No media lines in the SDP body */
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                          ccb->index, ccb->dn_line, fname,
+                          "Process SDP, no media");
+        retval =  SIP_SDP_NO_MEDIA;
+    } else {
+        /* There is at least a media line in the SDP */
+        retval = SIP_SDP_SUCCESS;
+
+        /*
+         * Check to see if the sdp->session_id and version_id are
+         * the same.  If they are, it could be a session audit.
+         * In most cases, this is handled the same as
+         * SIP_SDP_SUCCESS.
+         */
+        if (sip_msg_sdp->dest_sdp) {
+            const char *new_session_id = NULL;
+            const char *new_version_id = NULL;
+
+            new_session_id = sdp_get_owner_sessionid(sip_msg_sdp->dest_sdp);
+            new_version_id = sdp_get_owner_version(sip_msg_sdp->dest_sdp);
+
+            if ((ccb->old_session_id) && (ccb->old_version_id) &&
+                (new_session_id) && (new_version_id)) {
+                if ((!(strcmp(ccb->old_session_id, new_session_id))) &&
+                    (!(strcmp(ccb->old_version_id, new_version_id)))) {
+                     retval = SIP_SDP_SESSION_AUDIT;
+                }
+            }
+
+            if (ccb->old_session_id != NULL) {
+                cpr_free(ccb->old_session_id);
+                ccb->old_session_id = NULL;
+            }
+            if (ccb->old_version_id != NULL) {
+                cpr_free(ccb->old_version_id);
+                ccb->old_version_id = NULL;
+            }
+            if ((new_session_id) && (new_version_id)) {
+                ccb->old_session_id = cpr_strdup(new_session_id);
+                ccb->old_version_id = cpr_strdup(new_version_id);
+            }
+        }
+    }
+    /* free the SDP buffer */
+    sipsdp_src_dest_free(CCSIP_DEST_SDP_BIT, &sip_msg_sdp);
+    return (retval);
+}
+
+/*
+ *  Function: ccsip_save_local_msg_body
+ *
+ *  Parameters: ccb      - The pointer to ccsipCCB to save message body sent
+ *                         by GSM.
+ *              msg_body - pointer to a new msg body to be saved.
+ *
+ *  Description: This routine saves the message bodies sent by GSM via CCAPI.
+ *               If there is a previous message, the previous messages will
+ *               be freed before the current one is saved.
+ *
+ *  Returns: void
+ *
+ */
+static void
+ccsip_save_local_msg_body (ccsipCCB_t *ccb, cc_msgbody_info_t *msg_body)
+{
+    if ((msg_body == NULL) || (msg_body->num_parts == 0)) {
+        return;
+    }
+
+    /*
+     * Move the new bodies to the local_msg_body, the previous
+     * msg. bodies will be freed by the move function
+     */
+    cc_mv_msg_body_parts(&ccb->local_msg_body, msg_body);
+}
+
+/*
+ *  Function:  sip_redirect
+ *
+ *  Parameters: ccb, response to invite request, status code of
+ *              response.
+ *
+ *  Description: Will create a new invite with contact information
+ *               received in Redirection response to previous invite
+ *               Will clear up the call if all the contacts have been
+ *               used and no 2xx final response has been received.
+ *
+ *  Returns:
+ *
+ */
+void
+sip_redirect (ccsipCCB_t *ccb, sipMessage_t *response, uint16_t status_code)
+{
+    const char     *fname = "sip_redirect";
+    sipRedirectInfo_t *redirect_info;
+    const char     *contact;
+    sipLocation_t  *sipLocation;
+    sipUrl_t       *pContactURL = NULL;
+    char           *diversion[MAX_DIVERSION_HEADERS];
+    char           *str_temp = NULL;
+    char            ui_str[STATUS_LINE_MAX_LEN];
+    int             i = 0;
+    boolean         sent_invite = FALSE;
+    cpr_ip_addr_t   cc_remote_ipaddr;
+    uint16_t        diversion_count, cc_diversion_count, max_headers;
+    size_t          to_length;
+    char            tmp_str[STATUS_LINE_MAX_LEN];
+    char            stored_to_header[MAX_SIP_URL_LENGTH];
+    char           *temp = NULL;
+
+
+    CPR_IP_ADDR_INIT(cc_remote_ipaddr);
+
+    while (!sent_invite) {
+        if (ccb->redirect_info == NULL) {
+
+            /* Redirected for the first time allocating redirect_info */
+            redirect_info = (sipRedirectInfo_t *)
+                cpr_malloc(sizeof(sipRedirectInfo_t));
+            if (redirect_info == NULL) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                                  ccb->dn_line, fname, "malloc(redirect_info)");
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+                return;
+            }
+
+            redirect_info->sipContact = NULL;
+            redirect_info->next_choice = 0;
+            ccb->redirect_info = redirect_info;
+        } else {
+            redirect_info = ccb->redirect_info;
+        }
+
+        /*
+         * Now we are clearing the record-route information since we
+         * want redirected invite to use contact header. New transaction
+         * will start now and that will be using new record-route information,
+         * if any.
+         */
+        if (ccb->record_route_info) {
+            sippmh_free_record_route(ccb->record_route_info);
+            ccb->record_route_info = NULL;
+        }
+
+        if (redirect_info->next_choice >= SIP_MAX_LOCATIONS) {
+            /* Exhausted all Choices */
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "Exhausted all Contacts");
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            return;
+        }
+
+
+        if ((status_code >= SIP_RED_MULT_CHOICES &&
+             status_code <= SIP_RED_USE_PROXY) &&
+            (ccb->first_pass_3xx == TRUE)) {
+            /* 300, 301, 302 and 305 must have contact header */
+
+            /*
+             * We have already initialized the redirect_info with the
+             * contact info once, so the next time through the loop
+             * skip this part.
+             */
+            ccb->first_pass_3xx = FALSE;
+
+            if ((platGetPhraseText(STR_INDEX_CALL_REDIRECTED,
+                                          (char *)tmp_str,
+                                          STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) {
+                memset(ui_str, 0, sizeof(ui_str));
+                snprintf(ui_str, sizeof(ui_str), "%s (in %d)",
+                         tmp_str, status_code);
+                ui_set_call_status(ui_str, ccb->dn_line, ccb->gsm_id);
+            }
+
+            if (redirect_info->sipContact) {
+                /* Delete previously stored contact */
+                sippmh_free_contact(redirect_info->sipContact);
+                redirect_info->sipContact = NULL;
+            }
+
+            contact = sippmh_get_cached_header_val(response, CONTACT);
+            redirect_info->sipContact = sippmh_parse_contact(contact);
+            if ((redirect_info->sipContact == NULL)
+                || (contact == NULL)) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                  ccb->index, ccb->dn_line, fname,
+                                  "sippmh_parse_contact()");
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+                return;
+            }
+            /*
+            if (ccb->contact_info) {
+                sippmh_free_contact(ccb->contact_info);
+            }
+            ccb->contact_info = redirect_info->sipContact;
+            */
+
+            sipLocation = redirect_info->sipContact->locations[0];
+            redirect_info->next_choice = 1;
+
+            //if we are a part of a transfer we are going to need this.
+            //save the Contact value returned in 30x as the AOR for
+            //value to be populated in a later REFER's Refer-To
+            ccb->sip_referTo = strlib_update(ccb->sip_referTo, contact);
+
+        } else {
+            /* We should try next location in previous Contact */
+
+            if ((redirect_info->sipContact == NULL) ||
+                (redirect_info->next_choice >=
+                 redirect_info->sipContact->num_locations)) {
+                /* Exhausted all Choices */
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                  ccb->index, ccb->dn_line, fname,
+                                  "Exhausted all Contacts");
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+                return;
+            }
+
+            sipLocation = redirect_info->sipContact->locations
+                              [redirect_info->next_choice++];
+        }
+
+        /*
+         * Get the CC-Diversion parameter (if any).
+         */
+        for (i = 0; i < MAX_DIVERSION_HEADERS; i++) {
+            diversion[i] = NULL;
+        }
+        /*
+         * We need to to compliment to old CC-Diversion and CC_Redirect.
+         * We will either be getting CC_Diversion or Diversion or
+         * CC_Redirect, which are all basically same.
+         *
+         * So instead of changing it on the parser level I would prefer
+         * to do it at SIP level.  The following code first looks for
+         * Diversion then looks for CC_Diversion and then at last
+         * priority looks for CC_Redirect
+         */
+        diversion_count = sippmh_get_num_particular_headers(response,
+                                                            SIP_HEADER_DIVERSION,
+                                                            SIP_HEADER_DIVERSION,
+                                                            diversion, MAX_DIVERSION_HEADERS);
+        max_headers = MAX_DIVERSION_HEADERS - diversion_count;
+        cc_diversion_count = sippmh_get_num_particular_headers(response,
+                                                               SIP_HEADER_CC_DIVERSION,
+                                                               SIP_HEADER_CC_DIVERSION,
+                                                               &diversion[diversion_count],
+                                                               max_headers);
+        max_headers = MAX_DIVERSION_HEADERS - diversion_count - cc_diversion_count;
+        (void) sippmh_get_num_particular_headers(response,
+                                                 SIP_HEADER_CC_REDIRECT,
+                                                 SIP_HEADER_CC_REDIRECT,
+                                                 &diversion[diversion_count + cc_diversion_count],
+                                                 max_headers);
+        for (i = 0; i < MAX_DIVERSION_HEADERS; i++) {
+            if (diversion[i]) {
+                int len = 0;
+
+                len = strlen(diversion[i]);
+                if (ccb->diversion[i]) {
+                    cpr_free(ccb->diversion[i]);
+                    ccb->diversion[i] = NULL;
+                }
+                ccb->diversion[i] = (char *) cpr_malloc(len + 2);
+                if (ccb->diversion[i]) {
+                    sstrncpy(ccb->diversion[i], diversion[i], len + 1);
+                } else {
+                    CCSIP_DEBUG_ERROR(DEB_L_C_F_PREFIX"No memory left;"
+                                      "Ignoring CC-Diversion header #%d %d\n",
+                                      ccb->dn_line, ccb->gsm_id, fname,
+                                      ccb->index, i + 1);
+                }
+            }
+        }
+
+        /*
+         * Ignore TEL URLs so loop through until we hit SIP URL.
+         */
+        while ((sipLocation) &&
+               (redirect_info->next_choice < SIP_MAX_LOCATIONS) &&
+               (sipLocation->genUrl->schema != URL_TYPE_SIP)) {
+            sipLocation = redirect_info->sipContact->locations
+                              [redirect_info->next_choice++];
+        }
+
+        if (!sipLocation) {
+            /* Exhausted all Choices */
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "Exhausted all Contacts");
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            return;
+        }
+
+
+        pContactURL = sipLocation->genUrl->u.sipUrl;
+
+        /*
+         * Initially, set the port to the port contained in the ContactURL.  Then
+         * do a DNS Lookup on the host.  If we get a new port in the response, we
+         * will use that when building the Request URI.
+         */
+        ccb->dest_sip_port = pContactURL->port;
+
+        if (!pContactURL->port_present) {
+            /* Set the IP-level destination ipaddr and port in the ccb */
+            dns_error_code = sipTransportGetServerAddrPort(pContactURL->host,
+                                                           &cc_remote_ipaddr,
+                                                           (uint16_t *) &ccb->dest_sip_port,
+                                                           NULL, FALSE);
+        } else {
+            dns_error_code = dnsGetHostByName(pContactURL->host, &cc_remote_ipaddr, 100, 1);
+        }
+        if (dns_error_code == 0) {
+            util_ntohl(&(ccb->dest_sip_addr), &cc_remote_ipaddr);
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipTransportGetServerAddrPort or dnsGetHostByName()");
+
+            /*
+             * If DNS Lookup fails and there are more contacts, loop back up and try the next one.
+             * If we're out of contacts, then just release the call.
+             */
+            if (redirect_info->next_choice < SIP_MAX_LOCATIONS) {
+                continue;
+            } else {
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            }
+            return;
+        }
+
+        /*
+         * Get rid of transaction block for the ccb, we may have
+         * tried previous proxy in the Contact list
+         */
+        clean_method_request_trx(ccb, sipMethodInvite, TRUE);
+
+        /*
+         * Make new Req-URI and To values
+         */
+
+        temp = strstr(ccb->sip_to, ";tag");
+        to_length = (temp != NULL) ? (strlen(ccb->sip_to) - strlen(temp)) : MAX_SIP_URL_LENGTH;
+        if (to_length < MAX_SIP_URL_LENGTH) {
+            sstrncpy(stored_to_header, ccb->sip_to, to_length + 1);
+        } else {
+            sstrncpy(stored_to_header, ccb->sip_to, MAX_SIP_URL_LENGTH);
+        }
+
+        str_temp = strlib_open(ccb->sip_to, MAX_SIP_URL_LENGTH);
+        snprintf(str_temp, MAX_SIP_URL_LENGTH, "%s", stored_to_header);
+        ccb->sip_to = strlib_close(str_temp);
+
+        if (pContactURL->maddr) {
+            if (pContactURL->user) {
+                snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                         "sip:%s%s%s@%s:%d;maddr=%s%s",
+                         pContactURL->user,
+                         (pContactURL->password ? ":" : ""),
+                         (pContactURL->password ? pContactURL->password : ""),
+                         pContactURL->host,
+                         ccb->dest_sip_port,
+                         pContactURL->maddr,
+                         (pContactURL->is_phone ? ";user=phone" : ""));
+            } else {
+                snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                         pContactURL->is_phone ? "sip:%s:%d;maddr=%s;user=phone"
+                         : "sip:%s:%d;maddr=%s",
+                         pContactURL->host, ccb->dest_sip_port,
+                         pContactURL->maddr);
+            }
+        } else {
+            if (pContactURL->user) {
+                snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                         "sip:%s%s%s@%s:%d%s",
+                         pContactURL->user,
+                         (pContactURL->password ? ":" : ""),
+                         (pContactURL->password ? pContactURL->password : ""),
+                         pContactURL->host,
+                         ccb->dest_sip_port,
+                         (pContactURL->is_phone ? ";user=phone" : ""));
+            } else {
+                snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                         pContactURL->is_phone ? "sip:%s:%d;user=phone" : "sip:%s:%d",
+                         pContactURL->host, ccb->dest_sip_port);
+            }
+        }
+
+
+        ccb->ReqURIOriginal = strlib_update(ccb->ReqURIOriginal, ccb->ReqURI);
+
+        ccb->sip_to_tag = strlib_update(ccb->sip_to_tag, ""); // Removing To Tag
+        ccb->authen.cred_type = 0;
+
+        ccb->last_recv_request_cseq        = 0;
+        ccb->last_recv_request_cseq_method = sipMethodInvalid;
+
+        /*
+         * Send out a new INVITE
+         */
+        if (sipSPISendInvite(ccb, SIP_INVITE_TYPE_REDIRECTED, FALSE) == TRUE) {
+            sent_invite = TRUE;
+        } else {
+            status_code = SIP_1XX_TRYING;
+        }
+    }
+    return;
+
+}
+
+/*
+ *  Function:  parseAlertingHeader
+ *
+ *  Parameters: ccb: call control block
+ *              header: The Alert-Info header
+ *
+ *  Description: Parses the alert-info header to
+ *               determine if the value is a known
+ *               internal ringer or tone.
+ *
+ *  Note: Defaults to inside ring so that is what will be
+ *        played if any errors are encountered
+ *        during processing or if the header is not a
+ *        recognized value.
+ *
+ */
+void
+parseAlertingHeader (ccsipCCB_t *ccb, const char *header)
+{
+    char alertHeader[MAX_SIP_URL_LENGTH];
+    char *pAlertHeader;
+    char *input = NULL;
+    unsigned int counter = 0;
+
+    /*
+     * Since alertHeader is a const char and
+     * we need to modify it, copy the header
+     * into a char * Easier to do this than
+     * modify SIP core to pass around a char *
+     */
+    sstrncpy(alertHeader, header, MAX_SIP_URL_LENGTH);
+    pAlertHeader = alertHeader;
+
+    /*
+     * Check for malformed url.
+     * Is opening < present?
+     */
+    if (*pAlertHeader == '<') {
+        pAlertHeader++;
+
+        /* Eat any leading white space */
+        while (isspace((int)*pAlertHeader)) {
+            pAlertHeader++;
+        }
+
+        /* Is trailing > present? */
+        input = pAlertHeader;
+        pAlertHeader = strchr(input, '>');
+        if (pAlertHeader != NULL) {
+            /*
+             * Overwrite > with NULL to terminate
+             * ringer name
+             */
+            *pAlertHeader = NUL;
+
+            /*
+             * Check input against the list of
+             * internal ringers. The bellcore
+             * ringers were added to the end of
+             * the ringer list so add the offset
+             * to match them up correctly
+             */
+            for (counter = 0; counter <= VCM_BELLCORE_MAX - VCM_RING_OFFSET; counter++) {
+                if (strstr(input, ring_names[counter]) != NULL) {
+                    ccb->alert_info = ALERTING_RING;
+                    ccb->alerting_ring = (vcm_ring_mode_t) (counter +
+                                                            VCM_RING_OFFSET);
+                    return;
+                }
+            }
+
+            /*
+             * Check input against the list of internal
+             * tones.
+             */
+            for (counter = 0; counter < VCM_MAX_DIALTONE; counter++) {
+                if (strstr(input, tone_names[counter]) != NULL) {
+                    ccb->alert_info = ALERTING_TONE;
+                    ccb->alerting_tone = (vcm_tones_t) counter;
+                    return;
+                }
+            }
+        }
+    }
+
+    /*
+     * Error case. Malformed URL, external tone/ring pattern
+     * or unknown internal tone/ring pattern. Mimic old
+     * behavior.
+     */
+    ccb->alert_info = ALERTING_OLD;
+    ccb->alerting_ring = VCM_INSIDE_RING;
+}
+
+/*
+ *  Function:  ccsip_is_special_name_to_mask_display_number
+ *
+ *  Parameters:  name - name to be checked for special string.
+ *
+ *  Description: The function checks the given name whether it matches
+ *               a special display name or not.  It is used to determined
+ *               whether its associated number should be displayed or not.
+ *
+ *  Return:      TRUE  - the given name is a special name.
+ *               FALSE - the given name is not a special name.
+ */
+boolean
+ccsip_is_special_name_to_mask_display_number (const char *name)
+{
+    const char *special_string;
+
+    if (name == NULL) {
+        /* No name given ? */
+        return (FALSE);
+    }
+    /*
+     * Check for special name such as Conference or Barge. The CCM may
+     * sends string that is longer than the key words to match. Compare
+     * exactly (not even including NULL character).
+     */
+    special_string = platform_get_phrase_index_str(UI_CONFERENCE);
+    if ((cpr_strncasecmp(name, special_string, strlen(special_string)) == 0) ||
+        (cpr_strncasecmp(name, CONFERENCE_STR, CONFERENCE_STR_LEN) == 0)) {
+        return (TRUE);
+    }
+
+    /*
+     * The Barge 2 byte code is not in the phrase index table currently. Just
+     * use the define value.
+     */
+    if (cpr_strncasecmp(name, INDEX_STR_BARGE, 2) == 0) {
+        return (TRUE);
+    }
+
+    /*
+     * Detect private display name for restricted call party number.
+     */
+    if (cpr_strncasecmp(name, INDEX_STR_PRIVATE, 2) == 0) {
+        return (TRUE);
+    }
+
+    /*
+     * Detect monitoring display number.
+     */
+    if (cpr_strncasecmp(name, INDEX_STR_MONITORING, 2) == 0) {
+        return (TRUE);
+    }
+
+    /*
+     * Detect coaching display number.
+     */
+    if (cpr_strncasecmp(name, INDEX_STR_COACHING, 2) == 0) {
+        return (TRUE);
+    }
+
+    return (FALSE);
+}
+
+
+/*
+ *  Function:    unescape_UserInfo
+ *
+ *  Parameters:  esc_str: input escaped string containing "userinfo@host" or "userinfo"
+ *               unesc_str: output unescaped string with escape chars unescaped
+ *               unesc_str_len: length of unescaped string
+ *  Description: Parses the input string to check if escaped chars are present and
+ *               unescape them.
+ *  Return:      TRUE if escaped input exists and unescaped in output. Else FALSE.
+ *
+ *  Note:        -memory must be allocated by caller and freed as needed by caller. This
+ *               function expects enough space for the string.
+ *               -CSCsz30816: This function previously unescapes only up to @ and returns
+ *               the unescaped data (along with the un-inspected host data). However,
+ *               various usages in the code calls this with possible input string of
+ *               "userinfo@host" or "userinfo" where "userinfo" could contain '@' which
+ *               correctly needs not be escaped. Since the input is flexible as described,
+ *               we've changed to escape the entire input string although the
+ *               host portion, when present, should never has an escaped character anyway.
+ */
+
+static boolean
+unescape_UserInfo (const char *esc_str, char *unesc_str,
+                   unsigned int unesc_str_len)
+{
+    char *user_info;
+
+    /* Look for esc char % in the provided string */
+    user_info = strpbrk(esc_str, "%");
+
+    if (user_info) {
+        if (unesc_str_len > strlen(esc_str)) {
+            unesc_str_len = strlen(esc_str);
+        }
+        sippmh_convertEscCharToChar(esc_str, unesc_str_len, unesc_str);
+        return TRUE;
+    }
+
+    return (FALSE);
+}
+
+/*
+ *  Function:  ccsip_identify_best_rpid
+ *
+ *  Parameters: ccb: call control block
+ *              calling: boolean value indicating if message is a
+ *                       request or response.
+ *
+ *  Returns:    boolean indicating if screened RPID found.
+ *
+ *  Description: Identify the "best" Remote-Party-Id
+ *               The top-most screened Remote-Party-ID header
+ *               takes precedence. If none are screened, just
+ *               use the top-most Remote-Party-Id header.
+ */
+static boolean
+ccsip_identify_best_rpid (ccsipCCB_t *ccb, boolean request)
+{
+    unsigned int j;
+    sipRemotePartyId_t *rpid;
+
+    ccb->best_rpid = ccb->rpid_info->rpid[0];
+
+    if (!ccb->best_rpid) {
+        return FALSE;
+    }
+
+    for (j = 0; j < ccb->rpid_info->num_rpid; j++) {
+        rpid = ccb->rpid_info->rpid[j];
+        if (rpid &&
+            // screen check is disabled so that mid call party updates can be displayed
+            //rpid->screen &&
+            //!cpr_strncasecmp(rpid->screen, SCREEN_YES, strlen(SCREEN_YES)) &&
+            rpid->loc->genUrl->schema == URL_TYPE_SIP) {
+            /* party_type check is disabled so that the phone is more liberal in what it accepts for RPID */
+            /*
+             * If RPID in a request, party must be set to calling.
+             * If RPID in a response, party must be set to called
+             */
+            //if (rpid->party_type) {
+            //  if ((request && (!cpr_strncasecmp(rpid->party_type, PARTY_TYPE_CALLING,
+            //                                strlen(PARTY_TYPE_CALLING)))) ||
+            //     (!request && (!cpr_strncasecmp(rpid->party_type, PARTY_TYPE_CALLED,
+            //                                strlen(PARTY_TYPE_CALLED)))))
+            //  {
+            ccb->best_rpid = rpid;
+            return TRUE;
+            //  }
+            //}
+        }
+    }
+    return FALSE;
+}
+
+static void
+ccsip_phrase_specifier (int16_t phrase, string_t * string, uint16_t len)
+{
+    char           *temp_str;
+    char            tmp_str[STATUS_LINE_MAX_LEN];
+
+    temp_str = strlib_open(*string, len);
+    if (temp_str) {
+        if (phrase == STR_INDEX_ANONYMOUS_SPACE) {
+            if ((platGetPhraseText(STR_INDEX_ANONYMOUS_SPACE,
+                                          (char *)tmp_str,
+                                          STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) {
+                sstrncpy(temp_str, tmp_str, len);
+            }
+        } else {
+            sstrncpy(temp_str, platform_get_phrase_index_str(phrase), len);
+        }
+    }
+    *string = strlib_close(temp_str);
+}
+
+/*
+ *  Function:  ccsip_check_set_privacy_screen
+ *
+ *  Parameters:
+ *          name : name returned here based on privacy and screen
+ *          number : number returned here based on privacy and screen
+ *          input_name: input name
+ *          input_num: input number
+ *          privacy: privacy
+ *          screen:  screen information
+ *          connected_party: indicates that name/number are of the
+ *                           called/calling party
+ *
+ *  Returns:  TRUE if Number should be private
+ *
+ *  Description: This function checkes privacy and screen information
+ *   and based on that name and number parameter is calculated.
+ *
+ *  Note: To align with SCCP display the number is not displayed if
+ *        it is private. The Java UI code does not display anything
+ *        if the string is an empty string. So at the beginning of
+ *        the function set the number to "" and only update the number
+ *        string if the number is to be displayed. In the case where
+ *        the name and number represent the calling or called party,
+ *        we need to provide the number to GSM in the event that this
+ *        is the consultative call leg of an xfer. The xfer state machine
+ *        requires a DN in order to complete the xfer. The connected_party
+ *        boolean indicates whether the name/number are from the
+ *        called or calling party.
+ */
+static boolean
+ccsip_check_set_privacy_screen (string_t *name, string_t *number,
+                                char *input_name, char *input_num,
+                                char *privacy, char *screen,
+                                boolean connected_party)
+{
+    char *name_str;
+    boolean number_private = FALSE;
+
+    /* Initialize to empty required by UI */
+    *name = strlib_update(*name, "");
+
+    *number = strlib_update(*number, "");
+
+    if (!privacy || cpr_strncasecmp(privacy, PRIVACY_FULL, sizeof(PRIVACY_FULL)) == 0) {
+        /* Ignore screen parameter now
+         * (cpr_strcasecmp(screen, SCREEN_NO) == 0)) {
+         */
+        ccsip_phrase_specifier(UI_PRIVATE, name, MAX_SIP_DISPLAYNAME_LENGTH);
+        if (connected_party) {
+            /*
+             * name/number from connected party. number is required.
+             */
+            ccsip_phrase_specifier(STR_INDEX_ANONYMOUS_SPACE, number,
+                                   MAX_SIP_URL_LENGTH * 2);
+        }
+        return TRUE;
+    }
+
+    if (cpr_strncasecmp(privacy, PRIVACY_URI, sizeof(PRIVACY_URI)) == 0) {
+
+        if ((input_name != NULL) && (*input_name != NUL)) {
+            *name = strlib_update(*name, input_name);
+        } else {
+            ccsip_phrase_specifier(UI_UNKNOWN, name, MAX_SIP_DISPLAYNAME_LENGTH);
+        }
+        if (connected_party) {
+            /*
+             * name/number from connected party. number is required.
+             */
+            ccsip_phrase_specifier(STR_INDEX_ANONYMOUS_SPACE, number,
+                                   MAX_SIP_URL_LENGTH * 2);
+        }
+        number_private = TRUE;
+    } else if (cpr_strncasecmp(privacy, PRIVACY_NAME, sizeof(PRIVACY_NAME)) == 0) {
+
+        if (input_num) {
+            *number = strlib_update(*number, input_num);
+        }
+        ccsip_phrase_specifier(UI_PRIVATE, name, MAX_SIP_DISPLAYNAME_LENGTH);
+
+    } else {
+
+        if ((input_name != NULL) && (*input_name != NUL)) {
+            *name = strlib_update(*name, input_name);
+        } else {
+            if (!input_num || (*input_num == NUL)) {
+                ccsip_phrase_specifier(UI_UNKNOWN, name, MAX_SIP_DISPLAYNAME_LENGTH);
+            }
+        }
+
+        if (input_num) {
+            *number = strlib_update(*number, input_num);
+        }
+    }
+
+    name_str = strlib_open(*name, MAX_SIP_DISPLAYNAME_LENGTH);
+    if (name_str) {
+        sip_sm_dequote_string(name_str, MAX_SIP_DISPLAYNAME_LENGTH);
+    }
+    *name = strlib_close(name_str);
+    return number_private;
+}
+
+/*
+ *  Function:  ccsip_parse_diversion_header
+ *
+ *  Parameters: ccb: call control block
+ *              msg: SIP message to examine for RPID header
+ *
+ *  Returns:    boolean
+ *
+ *  Description: Diversion header is used when the call is forwarded to this
+ *         number. There are 2 different items that are extracted from diversion
+ *         header, 1) originally called party 2) last redirecting party.
+ *         On the Screen originally called party is shown as "By:.." and
+ *         last redirecting party is shown as "From:..."
+ */
+static boolean
+ccsip_parse_diversion_header (ccsipCCB_t *ccb, sipMessage_t *msg)
+{
+    char           *diversion_headers[MAX_DIVERSION_HEADERS];
+    unsigned int    diversion_count;
+    sipDiversion_t *diversion_header;
+
+    /*
+     * Free up previous diversion header info.
+     */
+    sippmh_free_diversion_info(ccb->div_info);
+
+    ccb->div_info = (sipDiversionInfo_t *)
+        cpr_malloc(sizeof(sipDiversionInfo_t));
+
+    if (!ccb->div_info) {
+        return FALSE;
+    }
+
+    memset(ccb->div_info, 0, sizeof(sipDiversionInfo_t));
+    memset(diversion_headers, 0, MAX_DIVERSION_HEADERS * sizeof(char *));
+
+    ccb->div_info->last_redirect_name = strlib_empty();
+    ccb->div_info->last_redirect_number = strlib_empty();
+    ccb->div_info->orig_called_name = strlib_empty();
+    ccb->div_info->orig_called_number = strlib_empty();
+
+
+    diversion_count = sippmh_get_num_particular_headers(msg,
+                                                        SIP_HEADER_DIVERSION,
+                                                        SIP_HEADER_DIVERSION,
+                                                        diversion_headers,
+                                                        MAX_DIVERSION_HEADERS);
+
+    if (diversion_count < 1) {
+        return FALSE;
+    }
+
+    ccb->call_type = CC_CALL_FORWARDED;
+
+    /* We need 1st and last diversion headers only */
+    diversion_header = sippmh_parse_diversion(diversion_headers[0], SIP_HEADER_DIVERSION);
+
+    if (diversion_header) {
+        (void) ccsip_check_set_privacy_screen(&(ccb->div_info->last_redirect_name),
+                                              &(ccb->div_info->last_redirect_number),
+                                              diversion_header->locations->name,
+                                              diversion_header->locations->genUrl->u.sipUrl->user,
+                                              diversion_header->privacy,
+                                              diversion_header->screen, FALSE);
+
+        sippmh_free_diversion(diversion_header);
+    }
+
+    /* Parse last diversion header */
+    diversion_header = sippmh_parse_diversion(diversion_headers[diversion_count - 1],
+                                              SIP_HEADER_DIVERSION);
+
+    if (diversion_header) {
+        (void) ccsip_check_set_privacy_screen(&(ccb->div_info->orig_called_name),
+                                              &(ccb->div_info->orig_called_number),
+                                              diversion_header->locations->name,
+                                              diversion_header->locations->genUrl->u.sipUrl->user,
+                                              diversion_header->privacy,
+                                              diversion_header->screen, FALSE);
+
+        sippmh_free_diversion(diversion_header);
+    }
+
+    return TRUE;
+}
+
+/*
+ *  Function:  ccsip_parse_rpid
+ *
+ *  Parameters: ccb: call control block
+ *              msg: SIP message to examine for RPID header
+ *
+ *  Returns:    boolean
+ *
+ *  Description: Gets number of RPID headers from the SIP message,
+ *               parses each RPID header, and sets the parsed RPID
+ *               header into the CCB.
+ */
+static boolean
+ccsip_parse_rpid (ccsipCCB_t *ccb, sipMessage_t *msg)
+{
+    char           *rpid_headers[MAX_REMOTE_PARTY_ID_HEADERS];
+    unsigned int    rpid_line_count;
+    const char     *rpid_str = NULL;
+    unsigned int    j;
+
+    /*
+     * Free up the previous RPID info and its associated data before,
+     * parsing a new one.
+     */
+    sippmh_free_remote_party_id_info(ccb->rpid_info);
+    ccb->best_rpid = NULL;
+    ccb->rpid_info = (sipRemotePartyIdInfo_t *)
+        cpr_malloc(sizeof(sipRemotePartyIdInfo_t));
+    if (!ccb->rpid_info) {
+        return FALSE;
+    }
+
+    /* Parse Remote-Party-ID */
+    memset(ccb->rpid_info, 0, sizeof(sipRemotePartyIdInfo_t));
+    memset(rpid_headers, 0, MAX_REMOTE_PARTY_ID_HEADERS * sizeof(char *));
+
+    rpid_line_count = sippmh_get_num_particular_headers(msg,
+                                                        SIP_HEADER_REMOTE_PARTY_ID,
+                                                        SIP_HEADER_REMOTE_PARTY_ID,
+                                                        rpid_headers,
+                                                        MAX_REMOTE_PARTY_ID_HEADERS);
+
+    if (rpid_line_count < 1) {
+        return FALSE;
+    }
+
+    for (j = 0; (j < rpid_line_count) && (j < MAX_REMOTE_PARTY_ID_HEADERS); j++) {
+        rpid_str = rpid_headers[j];
+        if ((rpid_str) && (rpid_str[0])) {
+            ccb->rpid_info->rpid[j] = sippmh_parse_remote_party_id(rpid_str);
+        }
+    }
+    ccb->rpid_info->num_rpid = rpid_line_count;
+    return TRUE;
+}
+
+
+/*
+ *  Function:  ccsip_set_url_domain
+ *
+ *  Parameters: host: domain name from SIP header to be checked
+ *              callingNumber: calling number to receive appended domain
+ *              calledNumber: the called (destination) number
+ *              line: line used for outbound call. 0 if inbound call.
+ *
+ *  Returns:    string_t: The updated calling number string
+ *
+ *  Description: Determines if host string received in a SIP URL should
+ *               be appended to the calling number for UI display and
+ *               storage in the missed/received calls directory.
+ *               Domain from the receive SIP URL will be appended to the
+ *               calling number under the following conditions.
+ *               1. Received domain is FQDN and receiving line's proxy address
+ *                  is a dotted ip address.
+ *               2. Received domain is FQDN and receiving line's proxy address
+ *                  is a FQDN that differs from the received domain.
+ *               3. If received domain is dotted ip, it is NOT appended to
+ *                  the calling number under any condtion.
+ *
+ */
+static string_t ccsip_set_url_domain (char *host, string_t callingNumber, string_t calledNumber, line_t line)
+{
+    char       *target;
+    char        addr_error;
+    uint32_t    address;
+    char        buffer[MAX_SIP_URL_LENGTH];
+    boolean     include_domain = FALSE;
+
+    if (host == NULL) {
+        return callingNumber;
+    }
+
+    /*
+     * First check if host is dotted ip.
+     */
+    address = IPNameCk(host, &addr_error);
+    if (!address) {
+        /*
+         * Host is not a dotted ip. Treat as a domain name.
+         * Validate the domain name and then compare the domain name
+         * to what is configured for the line.
+         */
+        target = cpr_strdup(host);
+        if (target != NULL) {
+            if (sipSPI_validate_hostname(target)) {
+                /*
+                 * Get proxy address config for the receiving line. Need to first determine
+                 * the line based on the called number.
+                 */
+                if (line == 0) {
+                    /*
+                     * Incoming call, line needs to be dermined based on the called number.
+                     * Otherwise, this is an outbound call and the line was provided by GSM.
+                     */
+                    line =  sip_config_get_line_by_called_number(1, calledNumber);
+                }
+
+                if (line == 0) {
+                    /*
+                     * Line not found based on called number. This call will be released by
+                     * GSM. Use full URL until release occurs.
+                     */
+                     include_domain = TRUE;
+                } else {
+                    buffer[0] = '\0';
+                    config_get_line_string(CFGID_PROXY_ADDRESS, buffer, line, MAX_SIP_URL_LENGTH);
+                    address = IPNameCk(buffer, &addr_error);
+                    if (address) {
+                        /* Configured domain is dotted ip. */
+                        include_domain = TRUE;
+                    } else {
+                        if (strncmp(host, buffer, MAX_SIP_URL_LENGTH) != 0) {
+                            /*
+                             * Configured domain differs from received domain
+                             */
+                            include_domain = TRUE;
+                        }
+                    }
+                }
+                if (include_domain == TRUE) {
+                    callingNumber = strlib_append(callingNumber, "@");
+                    callingNumber = strlib_append(callingNumber, host);
+                }
+            }
+            cpr_free(target);
+        }
+    }
+    return callingNumber;
+}
+
+/*
+ *  Function:  ccsip_set_alt_callback_number
+ *
+ *  Parameters: ccb: call control block
+ *
+ *  Returns:    char *: pointer to alt_callback_number.
+ *
+ *  Description: Checks to see the x-cisco-callback-number params
+ *               Copies the value to ccb->altCallingNumber
+ *
+ *  assumes  ccb->best_rpid->loc->genUrl valid
+ */
+
+void ccsip_set_alt_callback_number(ccsipCCB_t *ccb)
+{
+  int param_idx=0;
+  char *ptr;
+
+       while ( (ptr = ccb->best_rpid->loc->genUrl->other_params[param_idx++]) != NULL )
+       {
+               if ( ! strncasecmp ( ptr, RPID_CALLBACK, RPID_CALLBACK_LEN)) {
+                       ccb->altCallingNumber =
+                               strlib_update(ccb->altCallingNumber, ptr + RPID_CALLBACK_LEN);
+                       return;
+               }
+       }
+
+        ccb->altCallingNumber = strlib_update(ccb->altCallingNumber, "");
+}
+
+/*
+ *  Function:  ccsip_set_caller_id_from_rpid
+ *
+ *  Parameters: ccb: call control block
+ *              calling: boolean value indicating if message is a
+ *                       request or response.
+ *              display_enabled: boolean indicating if callerid is blocked.
+ *
+ *  Returns:    boolean: TRUE indicates call id was obtained from RPID.
+ *
+ *  Description: Determines if RPID support is enabled. If so, uses utilities
+ *               to parse out the RPID header(s) and identifies the best
+ *               RPID header to use for UI updates. Depending on the direction
+ *               of the call (inbound or outbound) and the RPID parameter
+ *               settings (privacy, screen), sets the calling/called display
+ *               name and number of the CCB based on the RPID.
+ */
+static boolean
+ccsip_set_caller_id_from_rpid (ccsipCCB_t *ccb, boolean request, boolean update_ccb, boolean *display_enabled)
+{
+    int         rpid_flag = RPID_DISABLED;
+    char       *sip_rpid_user = NULL;
+    char       *pUser = NULL;
+    string_t   *name;
+    string_t   *number;
+    boolean     display_number = TRUE;
+    boolean     private_num = FALSE;
+    line_t      line = 0;
+
+
+    *display_enabled = TRUE;
+
+    /* If RPID is not enabled in config, return */
+    config_get_value(CFGID_REMOTE_PARTY_ID, &rpid_flag, sizeof(rpid_flag));
+    if (rpid_flag == RPID_DISABLED) {
+        return FALSE;
+    }
+
+    /* If a screened RPID is not found, return */
+    if (!ccsip_identify_best_rpid(ccb, request)) {
+        return FALSE;
+    }
+
+    if (ccb->flags & INCOMING) {
+        name = &ccb->callingDisplayName;
+        number = &ccb->callingNumber;
+    } else {
+        name = &ccb->calledDisplayedName;
+        number = &ccb->calledNumber;
+        line = ccb->dn_line;
+    }
+
+    sip_rpid_user = ccb->best_rpid->loc->genUrl->u.sipUrl->user;
+
+    pUser = sippmh_parse_user(sip_rpid_user);
+    if (pUser) {
+        sip_rpid_user = pUser;
+    }
+
+    private_num = ccsip_check_set_privacy_screen(name, number,
+                                                 ccb->best_rpid->loc->name,
+                                                 sip_rpid_user,
+                                                 ccb->best_rpid->privacy,
+                                                 ccb->best_rpid->screen,
+                                                 TRUE);
+
+    /*
+     * This check is used to determine if the calling/called number should be displayed.
+     * This is true in 2 cases.If privacy=full or privacy=uri. We do not currently check
+     * for privacy=name.
+     */
+    if (private_num) {
+        /*
+         * Need to distinguish between privacy=uri and privacy=full
+         */
+        if (cpr_strncasecmp(ccb->best_rpid->privacy, PRIVACY_FULL, sizeof(PRIVACY_FULL)) == 0) {
+           /*
+            * This is used for anonymous call block
+            */
+           *display_enabled = display_number = FALSE;
+        } else {
+            /*
+             * Just used for blocking the calling/called number display
+             */
+            display_number = FALSE;
+        }
+    }
+
+    /*
+     * Update the alt Calling number if received in the RPID
+     */
+    if ( (!private_num) )
+    {
+        ccsip_set_alt_callback_number(ccb);
+    }
+
+      /*
+     * Append host portion of url if needed. If in CCM mode, skip this step.
+     * We hardcode to check line 1. If line 1 is in CCM mode, all lines are CCM mode.
+     */
+    if (sip_regmgr_get_cc_mode(1) == REG_MODE_NON_CCM) {
+        *number = ccsip_set_url_domain(ccb->best_rpid->loc->genUrl->u.sipUrl->host, *number,
+                                       ccb->calledNumber, line);
+    }
+
+    if (pUser) {
+        cpr_free(pUser);
+    }
+
+
+    if (update_ccb) {
+        if (ccb->flags & INCOMING) {
+            ccb->displayCallingNumber = display_number;
+        } else {
+            ccb->displayCalledNumber = display_number;
+        }
+    }
+
+    return TRUE;
+}
+/*
+ *  Function:  ccsip_send_callinfo
+ *
+ *  Parameters: ccb: call control block
+ *              update_caller_id: boolean indicates caller ID updating.
+ *              delay_update: indicates that the call info. event sent
+ *                            to GSM to update UI can be delayed.
+ *
+ *  Returns:    void
+ *
+ *  Description: The internal function used to send GSM call info.
+ */
+static void
+ccsip_send_callinfo (ccsipCCB_t *ccb, boolean update_caller_id,
+                     boolean delay_update)
+{
+    cc_feature_data_t data;
+    char            unescape_str_temp[MAX_SIP_URL_LENGTH];
+    const char     *name;
+    const char     *number;
+    const char     *altNumber = strlib_empty();
+    boolean         display_number;
+
+    if (!ccb->in_call_info) {
+        data.call_info.feature_flag = 0;
+        data.call_info.security = CC_SECURITY_UNKNOWN;
+        data.call_info.policy = CC_POLICY_UNKNOWN;
+        if (ccb->flags & INCOMING) {
+            data.call_info.orientation = CC_ORIENTATION_FROM;
+        } else {
+            data.call_info.orientation = CC_ORIENTATION_TO;
+        }
+        data.call_info.ui_state = CC_UI_STATE_NONE;
+        data.call_info.dusting = FALSE;
+        data.call_info.global_call_id[0] = 0;
+    } else {
+        cc_feature_data_call_info_t *feat_data = &ccb->in_call_info->data.call_info_feat_data;
+
+        data.call_info.feature_flag = feat_data->feature_flag;
+        data.call_info.security = feat_data->security;
+        data.call_info.policy = feat_data->policy;
+        data.call_info.orientation = feat_data->orientation;
+        data.call_info.ui_state = feat_data->ui_state;
+        data.call_info.caller_id.call_instance_id = feat_data->caller_id.call_instance_id;
+        data.call_info.dusting = feat_data->dusting;
+        sstrncpy(data.call_info.global_call_id,
+                 ccb->in_call_info->data.call_info_feat_data.global_call_id, CC_GCID_LEN);
+    }
+
+    data.call_info.caller_id.orig_rpid_number = strlib_empty();
+    if (!update_caller_id) {
+        data.call_info.feature_flag &= ~CC_CALLER_ID;
+        data.call_info.caller_id.called_name = strlib_empty();
+        data.call_info.caller_id.called_number = strlib_empty();
+        data.call_info.caller_id.calling_name = strlib_empty();
+        data.call_info.caller_id.calling_number = strlib_empty();
+        data.call_info.caller_id.alt_calling_number = strlib_empty();
+    } else {
+        data.call_info.feature_flag |= CC_CALLER_ID;
+        if (ccb->flags & INCOMING) {
+            /* Convert escaped userinfo in the URL to unescaped form */
+            if (unescape_UserInfo(ccb->callingDisplayName, unescape_str_temp,
+                    MAX_SIP_URL_LENGTH)) {
+                ccb->callingDisplayName = strlib_update(ccb->callingDisplayName,
+                                                        unescape_str_temp);
+            }
+            if (unescape_UserInfo(ccb->callingNumber, unescape_str_temp,
+                    MAX_SIP_URL_LENGTH)) {
+                ccb->callingNumber = strlib_update(ccb->callingNumber,
+                                                   unescape_str_temp);
+            }
+            name = ccb->callingDisplayName;
+            number = ccb->callingNumber;
+           strlib_free(altNumber);
+            altNumber = ccb->altCallingNumber;
+            display_number = ccb->displayCallingNumber;
+        } else {
+            /* Convert escaped userinfo in the URL to unescaped form */
+            if (unescape_UserInfo(ccb->calledDisplayedName, unescape_str_temp,
+                    MAX_SIP_URL_LENGTH)) {
+                ccb->calledDisplayedName = strlib_update(ccb->calledDisplayedName,
+                                                         unescape_str_temp);
+            }
+            if (unescape_UserInfo(ccb->calledNumber, unescape_str_temp,
+                    MAX_SIP_URL_LENGTH)) {
+                ccb->calledNumber = strlib_update(ccb->calledNumber,
+                                                  unescape_str_temp);
+            }
+            name = ccb->calledDisplayedName;
+            number = ccb->calledNumber;
+            display_number = ccb->displayCalledNumber;
+        }
+
+        if (data.call_info.orientation == CC_ORIENTATION_FROM) {
+            data.call_info.caller_id.calling_name = name;
+            data.call_info.caller_id.calling_number = number;
+            data.call_info.caller_id.alt_calling_number = altNumber;
+            data.call_info.caller_id.display_calling_number = display_number;
+
+            data.call_info.caller_id.called_name = strlib_empty();
+            data.call_info.caller_id.called_number = strlib_empty();
+            data.call_info.caller_id.display_called_number = FALSE;
+        } else {
+            data.call_info.caller_id.called_name = name;
+            data.call_info.caller_id.called_number = number;
+            data.call_info.caller_id.display_called_number = display_number;
+
+            data.call_info.caller_id.calling_name = strlib_empty();
+            data.call_info.caller_id.calling_number = strlib_empty();
+            data.call_info.caller_id.alt_calling_number = strlib_empty();
+            data.call_info.caller_id.display_calling_number = FALSE;
+            if(ccb->best_rpid != NULL && ccb->best_rpid->loc->genUrl->u.sipUrl->user != NULL)
+                data.call_info.caller_id.orig_rpid_number = (const char *) ccb->best_rpid->loc->genUrl->u.sipUrl->user;
+        }
+    }
+
+    /* Include diversion information
+     * Note that a valid ccb->div_info pointer here does not indicate that call is of
+     * type Forward. It simply means a call to parse diversion header had been made.
+     */
+    if (ccb->div_info) {
+        data.call_info.caller_id.last_redirect_name = ccb->div_info->last_redirect_name;
+        data.call_info.caller_id.last_redirect_number = ccb->div_info->last_redirect_number;
+        data.call_info.caller_id.orig_called_name = ccb->div_info->orig_called_name;
+        data.call_info.caller_id.orig_called_number = ccb->div_info->orig_called_number;
+    } else {
+        data.call_info.caller_id.last_redirect_name = strlib_empty();
+        data.call_info.caller_id.last_redirect_number = strlib_empty();
+        data.call_info.caller_id.orig_called_name = strlib_empty();
+        data.call_info.caller_id.orig_called_number = strlib_empty();
+    }
+    data.call_info.caller_id.call_type = ccb->call_type;
+
+    /* Set UP update delay flag */
+    data.call_info.feature_flag &= ~(CC_DELAY_UI_UPDATE);
+    if (delay_update) {
+        data.call_info.feature_flag |= CC_DELAY_UI_UPDATE;
+    }
+    data.call_info.callref = ccb->callref;
+    sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_CALLINFO, &data);
+}
+
+/*
+ *  Function:  ccsip_update_callinfo
+ *
+ *  Parameters: ccb: call control block
+ *              msg: SIP message to examine for RPID
+ *              calling: boolean value indicating if message is a
+ *                       request or response.
+ *              delay_update: indicates that the call info. event sent
+ *                            to GSM to update UI can be delayed.
+ *
+ *  Returns:    void
+ *
+ *  Description: Utility function used by the various SIP state handlers
+ *               to pull RPID and callinfo data from a received SIP message and set the
+ *               call info fields (called/calling name and number fields, security,
+ *               orientation, ui-state)  of the CCB. A call info  feature indication
+ *               is sent to the GSM to update the phone UI display.
+ */
+static void
+ccsip_update_callinfo (ccsipCCB_t *ccb, sipMessage_t *msg, boolean check_rpid,
+                       boolean request, boolean delay_update)
+{
+    boolean update_caller_id = FALSE;
+    boolean display_number = TRUE;
+
+    if (!msg) {
+        return;
+    }
+
+    if (check_rpid) {
+        if (ccsip_parse_rpid(ccb, msg)) {
+            if (ccsip_set_caller_id_from_rpid(ccb, request, TRUE, &display_number)) {
+                update_caller_id = TRUE;
+            }
+        }
+    }
+
+    ccsip_process_call_info_header(msg, ccb);
+
+    ccsip_send_callinfo(ccb, update_caller_id, delay_update);
+}
+
+/*
+ *  Function:  ccsip_check_display_validity
+ *
+ *  Parameters: ccb: call control block
+ *              reuqest: sip message
+ *
+ *  Returns:    boolean
+ *              True - Display valid
+ *              False - Display not valid
+ *
+ *  Description: Check if anonymous call block feature is enabled
+ *               against privacy settings received in the RPID and
+ *               make a decision if the call can continue
+ */
+static boolean
+ccsip_check_display_validity(ccsipCCB_t *ccb, sipMessage_t *request)
+{
+     int temp = 0;
+     boolean display_number = TRUE;
+     /*
+      * Check for Anonymous call blocking. If low bit is set,
+      * then do not allow call. Note that we must allow both upper and lowercase
+      * ANON strings, hence the use of strcasestr
+      */
+     config_get_value(CFGID_ANONYMOUS_CALL_BLOCK, &temp, sizeof(temp));
+     if (temp & 1) {
+          if (ccsip_parse_rpid(ccb, request)) {
+             if (ccsip_set_caller_id_from_rpid(ccb, TRUE, FALSE, &display_number)) {
+                 if (!display_number) {
+                     return FALSE;
+                 }
+             }
+         }
+     }
+     return TRUE;
+}
+/*
+ *
+ ***** SIP_STATE_IDLE
+ *
+ */
+void
+ccsip_handle_idle_ev_sip_invite (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "idle_ev_sip_invite";
+    char           *callingDisplayNameTemp;
+    char           *sip_to_tag_temp;
+    char           *sip_to_temp;
+    sipMessage_t   *request;
+    const char     *from = NULL;
+    const char     *to = NULL;
+    const char     *callID = NULL;
+    const char     *contact = NULL;
+    const char     *record_route = NULL;
+    const char     *expires = NULL;
+    const char     *alert_info = NULL, *allow = NULL;
+    const char     *require = NULL, *supported = NULL;
+    char           *replaceshdr = NULL;
+    sipReplaces_t  *replaces_t = NULL;
+    sipLocation_t  *to_loc = NULL;
+    sipLocation_t  *from_loc = NULL;
+    sipUrl_t       *sipFromUrl = NULL;
+    sipUrl_t       *sipToUrl = NULL;
+    uint32_t        local_expires_timeout = 0;
+    int             delta = 0;
+    uint16_t        request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    uint32_t        gmt_time;
+    uint32_t        diff_time;
+    int32_t         gmt_rc;
+    callid_t        cc_call_id = CC_NO_CALL_ID;
+    ccsipCCB_t     *refererccb = NULL;
+    ccsipCCB_t     *replaces_ccb = NULL;
+    line_t          dn_line;
+    sipsdp_status_t sdp_status;
+    boolean         check_send_487 = FALSE;
+    char            unescape_str_temp[MAX_SIP_URL_LENGTH];
+    boolean         display_number = FALSE;
+    string_t        recv_info_list = strlib_empty();
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    /* Request check and store */
+    if (sip_sm_request_check_and_store(ccb, request, sipMethodInvite, FALSE,
+                                       &request_check_reason_code,
+                                       request_check_reason_phrase, FALSE) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       request_check_reason_code,
+                                       request_check_reason_phrase, NULL);
+        free_sip_message(request);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+    }
+
+    ccb->flags |= INCOMING;
+
+    /* To: and From: header */
+    from = sippmh_get_cached_header_val(request, FROM);
+    ccb->sip_from = strlib_update(ccb->sip_from, from);
+    to = sippmh_get_cached_header_val(request, TO);
+    ccb->sip_to = strlib_update(ccb->sip_to, to);
+
+    /* CallID: header */
+    callID = sippmh_get_cached_header_val(request, CALLID);
+    sstrncpy(ccb->sipCallID, callID, sizeof(ccb->sipCallID));
+
+    /* Require: header */
+    require = sippmh_get_cached_header_val(request, REQUIRE);
+    if (require) {
+        char *unsupported_tokens = NULL;
+
+        ccb->required_tags = sippmh_parse_supported_require(require,
+                                                            &unsupported_tokens);
+
+        if (unsupported_tokens != NULL) {
+            ccb->sip_unsupported = strlib_update(ccb->sip_unsupported,
+                                                 unsupported_tokens);
+            cpr_free(unsupported_tokens);
+        }
+
+        if (ccb->required_tags & (~(SUPPORTED_TAGS))) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unsupported Require Header in INVITE\n", fname);
+            ccb->sip_require = strlib_update(ccb->sip_require, require);
+            sipSPISendInviteResponse(ccb, SIP_CLI_ERR_EXTENSION,
+                                     SIP_CLI_ERR_EXTENSION_PHRASE,
+                                     0, NULL,
+                                     FALSE, /* no SDP */ TRUE /* reTx */);
+            ccb->wait_for_ack = TRUE;
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            return;
+        }
+    }
+
+    /* Supported: header */
+    supported = sippmh_get_cached_header_val(request, SUPPORTED);
+    if (supported) {
+        ccb->supported_tags = sippmh_parse_supported_require(supported, NULL);
+    }
+
+    /* Allow: header */
+    allow = sippmh_get_header_val(request, SIP_HEADER_ALLOW, NULL);
+    if (allow) {
+        ccb->allow_methods = sippmh_parse_allow_header(allow);
+    }
+
+    /* Contact: header */
+    contact = sippmh_get_cached_header_val(request, CONTACT);
+    if (contact) {
+        if (ccb->contact_info) {
+            sippmh_free_contact(ccb->contact_info);
+        }
+        ccb->contact_info = sippmh_parse_contact(contact);
+
+        if ((ccb->contact_info == NULL) || // contact in msg, parse error
+            (sipSPICheckContact(contact) < 0)) { // If contact is invalid
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "sipSPICheckContact()");
+            (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                           SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                           SIP_WARN_MISC,
+                                           SIP_CLI_ERR_BAD_REQ_CONTACT_FIELD,
+                                           ccb);
+            ccb->wait_for_ack = TRUE;
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            return;
+        }
+    }
+
+    /* Record-Route: header */
+    record_route = sippmh_get_cached_header_val(request, RECORD_ROUTE);
+    if (record_route) {
+        if (ccb->record_route_info) {
+            sippmh_free_record_route(ccb->record_route_info);
+        }
+        ccb->record_route_info = sippmh_parse_record_route(record_route);
+        if (ccb->record_route_info == NULL) {
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "sippmh_parse_record_route()");
+            (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                           SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                           SIP_WARN_MISC,
+                                           SIP_CLI_ERR_BAD_REQ_RECORD_ROUTE,
+                                           ccb);
+            ccb->wait_for_ack = TRUE;
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            return;
+        }
+    }
+    // Save received URI in ReqURIOriginal
+    ccb->ReqURIOriginal = strlib_update(ccb->ReqURIOriginal, ccb->ReqURI);
+
+    /* Parse RPID header */
+    (void) ccsip_parse_rpid(ccb, request);
+
+
+    /* Parse Diversion header */
+    (void) ccsip_parse_diversion_header(ccb, request);
+
+    /*
+     * Parse From
+     */
+    from_loc = sippmh_parse_from_or_to((char *)ccb->sip_from, TRUE);
+    if (!from_loc) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_FROM));
+        sipSPISendInviteResponse(ccb, SIP_CLI_ERR_BAD_REQ,
+                                 SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                 SIP_WARN_MISC,
+                                 SIP_CLI_ERR_BAD_REQ_FROMURL_ERROR,
+                                 FALSE, /* no SDP */ TRUE /* reTx */);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+    }
+    if (from_loc->tag && (strlen(from_loc->tag) >= MAX_SIP_TAG_LENGTH)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                          ccb->dn_line, fname, "Length of From Tag");
+        sipSPISendInviteResponse(ccb, SIP_CLI_ERR_BAD_REQ,
+                                 SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                 SIP_WARN_MISC,
+                                 SIP_CLI_ERR_BAD_REQ_FROMURL_ERROR,
+                                 FALSE, /* no SDP */ TRUE /* reTx */);
+        sippmh_free_location(from_loc);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+    }
+
+    if (from_loc->genUrl->schema == URL_TYPE_SIP) {
+        sipFromUrl = from_loc->genUrl->u.sipUrl;
+    }
+
+    /*
+     * Parse To
+     */
+    to_loc = sippmh_parse_from_or_to((char *)ccb->sip_to, TRUE);
+    if (!to_loc) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO));
+        sipSPISendInviteResponse(ccb, SIP_CLI_ERR_BAD_REQ,
+                                 SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                 SIP_WARN_MISC,
+                                 SIP_CLI_ERR_BAD_REQ_ToURL_ERROR,
+                                 FALSE, /* no SDP */ TRUE /* reTx */);
+        sippmh_free_location(from_loc);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+    }
+
+    if (to_loc->genUrl->schema == URL_TYPE_SIP) {
+        sipToUrl = to_loc->genUrl->u.sipUrl;
+    }
+    // Check/Generate tags
+    if (to_loc->tag) {
+        /* ccb->sip_to_tag = strlib_update(ccb->sip_to_tag,
+         * sip_sm_purify_tag(to_loc->tag));
+         */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          "Initial invite with to_tag");
+        sipSPISendInviteResponse(ccb, SIP_CLI_ERR_CALLEG,
+                                 SIP_CLI_ERR_CALLEG_PHRASE,
+                                 0, NULL,
+                                 FALSE, /* no SDP */ TRUE /* reTx */);
+        sippmh_free_location(to_loc);
+        sippmh_free_location(from_loc);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+
+    } else {
+        sip_to_tag_temp = strlib_open(ccb->sip_to_tag, MAX_SIP_TAG_LENGTH);
+        if (sip_to_tag_temp) {
+            sip_util_make_tag(sip_to_tag_temp);
+        }
+        ccb->sip_to_tag = strlib_close(sip_to_tag_temp);
+
+        sip_to_temp = strlib_open(ccb->sip_to, MAX_SIP_URL_LENGTH);
+        if (sip_to_temp) {
+            sstrncat(sip_to_temp, ";tag=",
+                    MAX_SIP_URL_LENGTH - strlen(sip_to_temp));
+            if (ccb->sip_to_tag) {
+                sstrncat(sip_to_temp, ccb->sip_to_tag,
+                        MAX_SIP_URL_LENGTH - strlen(sip_to_temp));
+            }
+        }
+        ccb->sip_to = strlib_close(sip_to_temp);
+    }
+
+    if (from_loc->tag) {
+        ccb->sip_from_tag = strlib_update(ccb->sip_from_tag,
+                                          sip_sm_purify_tag(from_loc->tag));
+        ccb->callref = get_callref(ccb->sip_from_tag);
+    }
+
+    if (ccb->ReqURI) {
+        ccb->calledNumber = strlib_update(ccb->calledNumber, ccb->ReqURI);
+    } else if (sipToUrl) {
+        if (sipToUrl->user) {
+            char *pUser = NULL;
+
+            pUser = sippmh_parse_user(sipToUrl->user);
+            if (pUser && (pUser[0] != '\0')) {
+                ccb->calledNumber = strlib_update(ccb->calledNumber, pUser);
+                cpr_free(pUser);
+            } else {
+                /* An error occurred, copy the whole thing.. */
+                ccb->calledNumber = strlib_update(ccb->calledNumber,
+                                                  sipToUrl->user);
+                if (pUser)
+                    cpr_free(pUser);
+            }
+        }
+    }
+
+    /*
+     * Make sure we have a valid called_number.
+     */
+    if ((ccb->calledNumber == NULL) || (ccb->calledNumber[0] == '\0')) {
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_NOT_FOUND,
+                                           SIP_CLI_ERR_NOT_FOUND_PHRASE,
+                                           0, NULL, ccb);
+
+        sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+        ccb->wait_for_ack = FALSE;
+        sip_sm_change_state(ccb, SIP_STATE_IDLE);
+        sip_sm_call_cleanup(ccb);
+        return;
+    }
+
+    if (!ccsip_set_caller_id_from_rpid(ccb, TRUE, TRUE, &display_number)) {
+        if (sipFromUrl) {
+            if (sipFromUrl->user) {
+                char *pUser;
+
+                pUser = sippmh_parse_user(sipFromUrl->user);
+                if (pUser) {
+                    ccb->callingNumber = strlib_update(ccb->callingNumber, pUser);
+                    cpr_free(pUser);
+                } else {
+                    /* An error occurred, copy the whole thing.. */
+                    ccb->callingNumber = strlib_update(ccb->callingNumber, sipFromUrl->user);
+                }
+                if (from_loc->genUrl->schema == URL_TYPE_SIP &&
+                    sipFromUrl->host &&
+                    ccb->callingNumber &&
+                    sip_regmgr_get_cc_mode(1) == REG_MODE_NON_CCM) {
+                    /*
+                     * If we have a host name, calling number, and are not in CCM mode,
+                     * check to see if the domain should be appended to the calling number
+                     * for display to the user.
+                     */
+                    ccb->callingNumber = ccsip_set_url_domain(sipFromUrl->host, ccb->callingNumber,
+                                                              ccb->calledNumber, 0);
+                }
+            } else {
+                ccb->callingNumber = strlib_update(ccb->callingNumber,
+                                                   "Unknown Number");
+            }
+        }
+
+        if (from_loc->name) {
+            callingDisplayNameTemp = strlib_open(ccb->callingDisplayName,
+                                                 MAX_SIP_DISPLAYNAME_LENGTH);
+            if (callingDisplayNameTemp) {
+                sstrncpy(callingDisplayNameTemp, from_loc->name,
+                         MAX_SIP_DISPLAYNAME_LENGTH);
+                sip_sm_dequote_string(callingDisplayNameTemp, MAX_SIP_DISPLAYNAME_LENGTH);
+            }
+            ccb->callingDisplayName = strlib_close(callingDisplayNameTemp);
+
+        } else {
+            char *pUser;
+
+            pUser = NULL;
+            if (sipFromUrl) {
+                pUser = sippmh_parse_user(sipFromUrl->user);
+            }
+
+            if (pUser) {
+                ccb->callingDisplayName = strlib_update(ccb->callingDisplayName,
+                                                        pUser);
+                cpr_free(pUser);
+            } else {
+                /* An error occurred, copy the whole thing.. */
+                if (sipFromUrl) {
+                    if (sipFromUrl->user) {
+                        ccb->callingDisplayName = strlib_update(ccb->callingDisplayName,
+                                                                sipFromUrl->user);
+                    }
+                }
+            }
+        }
+    }
+
+    sippmh_free_location(to_loc);
+    sippmh_free_location(from_loc);
+
+    // If there is a Replace aheader then it must be a Transfer request
+    // Store the call_id of the other call so that we can release the call when
+    // we get 200 OK on consultation call
+    (void) sippmh_get_num_particular_headers(request, SIP_HEADER_REPLACES,
+                                             NULL, &replaceshdr, MAX_REPLACES_HEADERS);
+    ccb->wastransferred = FALSE;
+
+
+    dn_line = ccb->dn_line;
+
+    if ((Basic_is_phone_forwarded(dn_line) == NULL) && NULL != replaceshdr) {
+        char tempreplace[MAX_SIP_URL_LENGTH];
+        boolean is_previous_call_id = FALSE;
+        line_t  previous_call_index = 0;
+
+        memset(tempreplace, 0, MAX_SIP_URL_LENGTH);
+        sstrncpy(tempreplace, "Replaces=", sizeof(tempreplace));
+        sstrncat(tempreplace, replaceshdr, (sizeof(tempreplace) - sizeof("Replaces=")));
+        replaces_t = sippmh_parse_replaces(tempreplace, FALSE);
+        if (NULL != replaces_t) {
+            //Check if a call exists that matches the callid, to and from tags found in the replaces header
+            if ((cpr_strcasecmp(replaces_t->toTag, ccb->sip_to_tag)==0
+                 && cpr_strcasecmp(replaces_t->fromTag, ccb->sip_from_tag)==0
+                 && cpr_strcasecmp(replaces_t->callid, ccb->sipCallID)==0) ||
+                ((refererccb = sip_sm_get_ccb_by_callid(replaces_t->callid)) != NULL
+                && cpr_strcasecmp(replaces_t->toTag, refererccb->sip_to_tag) == 0
+                && cpr_strcasecmp(replaces_t->fromTag, refererccb->sip_from_tag) == 0)) {
+                ccb->sipxfercallid = strlib_update(ccb->sipxfercallid, replaces_t->callid);
+                sippmh_free_replaces(replaces_t);
+                ccb->wastransferred = TRUE;
+                check_send_487 = TRUE;
+            } else {
+                is_previous_call_id = sip_sm_is_previous_call_id(replaces_t->callid,
+                                                                 &previous_call_index);
+                replaces_ccb = sip_sm_get_ccb_by_callid(replaces_t->callid);
+                if (replaces_ccb != NULL && replaces_ccb->state == SIP_STATE_RELEASE) {
+                    is_previous_call_id = TRUE;
+                    CCSIP_DEBUG_STATE("%s: Replaces Header, matching call found. \n", fname);
+                }
+                if (is_previous_call_id) {
+                    // The Callid refers to a previous call
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                      ccb->index, ccb->dn_line, fname,
+                                      "Replaces Header, Callid refers to a previous call");
+                    sipSPISendInviteResponse(ccb, SIP_FAIL_DECLINE,
+                                             SIP_FAIL_DECLINE_PHRASE,
+                                             0, NULL,
+                                             FALSE, /* no SDP */ TRUE /* reTx */);
+
+                } else {
+                    // Could not find a matching call. If the CFWD is disabled
+                    // send back the error. If it is enabled, it will be processed
+                    // below.
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                      ccb->index, ccb->dn_line, fname,
+                                      "Replaces Header, No matching call found");
+                    sipSPISendInviteResponse(ccb, SIP_CLI_ERR_CALLEG,
+                                             SIP_CLI_ERR_CALLEG_PHRASE,
+                                             0, NULL,
+                                             FALSE, /* no SDP */ TRUE /* reTx */);
+                }
+                ccb->wait_for_ack = TRUE;
+                sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+                sippmh_free_replaces(replaces_t);
+                return;
+            }
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "Bad Replaces Header, Ending transferred call");
+            sipSPISendInviteResponse(ccb, SIP_CLI_ERR_BAD_REQ,
+                                     SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                     SIP_WARN_MISC,
+                                     SIP_CLI_ERR_BAD_REQ_PHRASE_REPLACES,
+                                     FALSE, /* no SDP */ TRUE /* reTx */);
+            ccb->wait_for_ack = TRUE;
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            return;
+
+        }
+    }
+
+    /*
+     * Extract SDP
+     */
+    sdp_status = sip_util_extract_sdp(ccb, request);
+
+    switch (sdp_status) {
+    case SIP_SDP_SUCCESS:
+    case SIP_SDP_SESSION_AUDIT:
+        ccb->oa_state = OA_OFFER_RECEIVED;
+        break;
+
+    case SIP_SDP_DNS_FAIL:
+        sipSPISendInviteResponse(ccb, SIP_SERV_ERR_INTERNAL,
+                                 SIP_SERV_ERR_INTERNAL_PHRASE,
+                                 SIP_WARN_MISC,
+                                 "DNS lookup failed for media destination",
+                                 FALSE, FALSE);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+
+    case SIP_SDP_ERROR:
+        sipSPISendInviteResponse(ccb, SIP_CLI_ERR_BAD_REQ,
+                                 SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                 SIP_WARN_MISC,
+                                 SIP_CLI_ERR_BAD_REQ_SDP_ERROR,
+                                 FALSE, /* no SDP */ TRUE /* reTx */);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+
+    case SIP_SDP_NO_MEDIA:
+        sipSPISendInviteResponse(ccb, SIP_CLI_ERR_BAD_REQ,
+                                 SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                 SIP_WARN_MISC,
+                                 SIP_CLI_ERR_BAD_REQ_SDP_ERROR,
+                                 FALSE, /* no SDP */ TRUE /* reTx */);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+
+    case SIP_SDP_NOT_PRESENT:
+    default:
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Waiting for SDP in ACK\n", DEB_F_PREFIX_ARGS(SIP_SDP, fname));
+        break;
+    }
+
+
+    /* see if we are forwarded to another location */
+    if (Basic_is_phone_forwarded(dn_line)) {
+        size_t  escaped_char_str_len = 8;
+        char    pDiversionStr[MAX_SIP_URL_LENGTH];
+        int     len = 0;
+        int     blocking;
+        boolean private_flag = FALSE;
+        char    line_name[MAX_LINE_NAME_SIZE];
+        char    display_name[MAX_LINE_NAME_SIZE];
+        char    src_addr_str[MAX_IPADDR_STR_LEN];
+
+
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                          ccb->index, ccb->dn_line, fname,
+                          "Call forwarded, sending redirect");
+
+        ccb->dn_line = dn_line;
+        /*
+         * If Caller ID Blocking is OFF or emergency route is ON,
+         * set private to TRUE.  Otherwise, FALSE.
+         */
+        config_get_value(CFGID_CALLERID_BLOCKING, &blocking, sizeof(blocking));
+        if ((blocking & 1) && (ccb->routeMode != RouteEmergency)) {
+            private_flag = TRUE;
+        }
+
+        /*
+         * Diversion Header format:
+         *      "display_name" <sip:line_name@addr>;reason=unconditional;
+         *                                      privacy="full" or "off";screen=yes
+         */
+        config_get_string((CFGID_LINE_NAME + ccb->dn_line - 1), line_name, sizeof(line_name));
+        sip_config_get_display_name(ccb->dn_line, display_name, sizeof(display_name));
+        ipaddr2dotted(src_addr_str, &ccb->src_addr);
+        snprintf(pDiversionStr, MAX_SIP_URL_LENGTH, "\"%s\" <sip:", display_name);
+        escaped_char_str_len += strlen(display_name);
+        escaped_char_str_len += sippmh_convertURLCharToEscChar(line_name, strlen(line_name),
+                                    pDiversionStr + escaped_char_str_len,
+                                    MAX_SIP_URL_LENGTH - escaped_char_str_len,
+                                    FALSE);
+        snprintf(pDiversionStr + escaped_char_str_len,
+                 MAX_SIP_URL_LENGTH - escaped_char_str_len,
+                 "@%s>;reason=unconditional;privacy=%s;screen=yes",
+                 src_addr_str, (private_flag ? "full" : "off"));
+
+        len = strlen(pDiversionStr);
+        ccb->diversion[0] = (char *) cpr_malloc(len + 1);
+
+        if (ccb->diversion[0]) {
+            sstrncpy(ccb->diversion[0], pDiversionStr, len + 1);
+        } else {
+            CCSIP_DEBUG_ERROR(DEB_L_C_F_PREFIX "No memory left; %d"
+                              "Can not create CC-Diversion header for CFWDAll\n",
+                              ccb->dn_line, ccb->gsm_id, fname, ccb->index);
+        }
+        sipSPISendInviteResponse302(ccb);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+    }
+
+    /*
+     * Start Local Expires timer
+     */
+    expires = sippmh_get_header_val(request, SIP_HEADER_EXPIRES, NULL);
+    config_get_value(CFGID_TIMER_INVITE_EXPIRES, &local_expires_timeout,
+                     sizeof(local_expires_timeout));
+    if (expires) {
+//CPR TODO: need reference for
+        gmt_rc = gmt_string_to_seconds((char *)expires, (unsigned long *)&gmt_time);
+        if (gmt_rc != -1) {
+            // We only want to update the expires timeout if it is lower
+            // than our predefined threshold.  We don't want to allow people
+            // to keep us hung up for infinite periods of time
+            if (gmt_rc == 1) {
+                // We got a numeric entry in the expires field
+                if (gmt_time < local_expires_timeout) {
+                    local_expires_timeout = gmt_time;
+                }
+//CPR TODO: need reference for
+            } else if (diff_current_time(gmt_time, (unsigned long *) &diff_time) == 0) {
+                // We got a GMT string in the expires field
+                if (diff_time < local_expires_timeout) {
+                    local_expires_timeout = diff_time;
+                }
+            }
+        }
+    } else {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                          ccb->index, ccb->dn_line, fname,
+                          "Using config timer_expires");
+    }
+
+    if (local_expires_timeout > 0) {
+        config_get_value(CFGID_TIMER_T1, &delta, sizeof(delta));
+        delta = (2 * delta) / 1000;
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Starting INVITE Local Expires %d"
+                          "timer (%d sec)\n",
+                                                 DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->dn_line, ccb->gsm_id, fname),
+                          ccb->index, local_expires_timeout + delta);
+        /*
+         * Add delta to the Expires timer so that the callee gives the
+         * caller a chance to send out the CANCEL message before sending
+         * out a 408 INVITE response.
+         */
+        (void) sip_platform_localexpires_timer_start((local_expires_timeout + delta) * 1000,
+                                                     ccb->index, &(ccb->dest_sip_addr),
+                                                     (uint16_t) ccb->dest_sip_port);
+    }
+
+    /* check for alert-info in header */
+    alert_info = sippmh_get_header_val(request, SIP_HEADER_ALERT_INFO, NULL);
+    ccb->alert_info = ALERTING_NONE;
+    if (alert_info) {
+        parseAlertingHeader(ccb, alert_info);
+    }
+
+    /* check for and process call-info in header */
+    ccsip_process_call_info_header(request, ccb);
+
+    /*
+     * Parse join info if it exists and get
+     * the gsm id for the join target call
+     */
+    if (ccsip_get_join_info(ccb, request) == FALSE) {
+        /*
+         * There was something wrong with the joinhdr
+         * or join target call is not active
+         */
+        sipSPISendInviteResponse(ccb, SIP_CLI_ERR_BAD_REQ,
+                                 SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                 SIP_WARN_MISC,
+                                 SIP_CLI_ERR_BAD_REQ_FROMURL_ERROR,
+                                 FALSE, /* no SDP */ TRUE /* reTx */);
+        ccb->wait_for_ack = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+    }
+
+    sipSPISendInviteResponse100(ccb, TRUE);
+
+    /* Inform CSM */
+    cc_call_id = cc_get_new_call_id();
+    ccb->gsm_id = cc_call_id;
+    // send XFER Request
+    if (ccb->wastransferred) {
+        refererccb = sip_sm_get_ccb_by_callid(ccb->sipxfercallid);
+        if (NULL != refererccb) {
+            cc_feature_data_t data;
+
+            data.xfer.method = CC_XFER_METHOD_REFER;
+            data.xfer.cause = CC_CAUSE_XFER_REMOTE;
+            data.xfer.target_call_id = cc_call_id;
+            data.xfer.dialstring[0] = '\0';
+            sip_cc_feature(refererccb->gsm_id, refererccb->dn_line,
+                           CC_FEATURE_XFER, (void *) &data);
+        }
+    }
+
+    if (refererccb == NULL) {
+        dn_line = ccb->dn_line;
+    } else {
+        dn_line = refererccb->dn_line;
+    }
+
+    /* Convert escaped userinfo in the URL to unescaped form */
+    if (unescape_UserInfo(ccb->calledNumber, unescape_str_temp, MAX_SIP_URL_LENGTH)) {
+        ccb->calledNumber = strlib_update(ccb->calledNumber, unescape_str_temp);
+    }
+
+    if (unescape_UserInfo(ccb->callingNumber, unescape_str_temp, MAX_SIP_URL_LENGTH)) {
+        ccb->callingNumber = strlib_update(ccb->callingNumber, unescape_str_temp);
+    }
+
+    if (unescape_UserInfo(ccb->callingDisplayName, unescape_str_temp, MAX_SIP_URL_LENGTH)) {
+        ccb->callingDisplayName = strlib_update(ccb->callingDisplayName, unescape_str_temp);
+    }
+
+    if (unescape_UserInfo(ccb->altCallingNumber, unescape_str_temp, MAX_SIP_URL_LENGTH)) {
+        ccb->altCallingNumber = strlib_update(ccb->altCallingNumber, unescape_str_temp);
+    }
+
+    /* Info Package stuff */
+    ccsip_parse_send_info_header(request, &recv_info_list);
+
+
+
+    if (ccb->wastransferred) {
+        sip_cc_setup(cc_call_id, dn_line,
+                     ccb->callingDisplayName,
+                     ccb->callingNumber,
+                     ccb->altCallingNumber,
+                     ccb->displayCallingNumber,
+                     ccb->calledDisplayedName,
+                     ccb->calledNumber,
+                     ccb->displayCalledNumber,
+                     ccb->div_info->orig_called_name,
+                     ccb->div_info->orig_called_number,
+                     ccb->div_info->last_redirect_name,
+                     ccb->div_info->last_redirect_number,
+                     ccb->call_type,
+                     ccb->alert_info, ccb->alerting_ring,
+                     ccb->alerting_tone, ccb->in_call_info, TRUE,
+                     recv_info_list, request);
+    } else {
+        sip_cc_setup(cc_call_id, dn_line,
+                     ccb->callingDisplayName,
+                     ccb->callingNumber,
+                     ccb->altCallingNumber,
+                     ccb->displayCallingNumber,
+                     ccb->calledDisplayedName,
+                     ccb->calledNumber,
+                     ccb->displayCalledNumber,
+                     ccb->div_info->orig_called_name,
+                     ccb->div_info->orig_called_number,
+                     ccb->div_info->last_redirect_name,
+                     ccb->div_info->last_redirect_number,
+                     ccb->call_type,
+                     ccb->alert_info, ccb->alerting_ring,
+                     ccb->alerting_tone, ccb->in_call_info, FALSE,
+                     recv_info_list, request);
+    }
+
+    strlib_free(recv_info_list);
+
+    sip_sm_change_state(ccb, SIP_STATE_RECV_INVITE);
+
+    if (check_send_487) {
+        /* check to see if we need to send a 487 back to original Invite
+         * that replaces this call. If it is not connected yet, send the
+         * 487 and release the call to GSM.
+         */
+        ccsipCCB_t *other_ccb;
+
+        other_ccb = sip_sm_get_ccb_by_callid(ccb->sipxfercallid);
+        if ((other_ccb != NULL) &&
+            ((other_ccb->state >= SIP_STATE_RECV_INVITE) &&
+             (other_ccb->state < SIP_STATE_RECV_INVITE_CONNECTED))) {
+            sipSPISendInviteResponse(other_ccb, SIP_CLI_ERR_REQ_CANCEL,
+                                     SIP_CLI_ERR_REQ_CANCEL_PHRASE, 0, NULL,
+                                     FALSE /* no SDP */ , TRUE /* reTx */);
+            sip_cc_release(other_ccb->gsm_id, other_ccb->dn_line,
+                           CC_CAUSE_NORMAL, NULL);
+            other_ccb->wait_for_ack = TRUE;
+            sip_sm_change_state(other_ccb, SIP_STATE_RELEASE);
+        }
+    }
+}
+
+/*
+ * This function is called when one of the following occurs:
+ *
+ * 1) Attempting to send out an INVITE and for whatever reason
+ *    the call to get the IP address for the main proxy fails.
+ * 2) An ICMP unreachable event is received and there are no
+ *    more DNS entries for the main proxy.
+ * 3) Retries have been exhausted on the main proxy and there
+ *    are no more DNS entries for the main proxy.
+ */
+boolean
+ccsip_attempt_backup_proxy (ccsipCCB_t *ccb)
+{
+    char     ip_addr_str[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t ipaddr;
+    int      tempPort = 0;
+    char     tmp_str[STATUS_LINE_MAX_LEN];
+
+    CPR_IP_ADDR_INIT(ipaddr);
+    /*
+     *  Get IPAddress and Port for backup proxy
+     */
+    sipTransportGetBkupServerAddress(&ipaddr, ccb->dn_line, ip_addr_str);
+    if (util_check_if_ip_valid(&ipaddr)) {
+         util_ntohl(&(ccb->dest_sip_addr), &ipaddr);
+
+        /*
+         * If the Proxy Backup Port isn't contained in the
+         * config table, use the PROXYN port instead.
+         */
+        tempPort = sipTransportGetBkupServerPort(ccb->dn_line);
+        if (tempPort != 0) {
+            ccb->dest_sip_port = tempPort;
+        } else {
+            ccb->dest_sip_port = sipTransportGetPrimServerPort(ccb->dn_line);
+        }
+
+        ccb->proxySelection = SIP_PROXY_BACKUP;
+
+        /* Note counter must be at least 2 or better to work as directed
+         *  This count is decremented by one on each successful response to
+         *  an invite. If we receive backup_active successful responses to
+         *  an invite go back to the max retry count on the main or primary proxy.
+         *  Why 12 you ask. Depending on the number of SIP messages received per
+         *  call this equates to maybe 4 phone calls before going back to
+         *  the full re-try count on the primary/main proxy.
+         */
+        gGlobInfo.backup_active = 12;
+
+        /*
+         * Let user know we have failed over to backup proxy
+         */
+        if ((platGetPhraseText(STR_INDEX_PROXY_UNAVAIL,
+                                      (char *)tmp_str,
+                                      STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) {
+            ui_set_call_status(tmp_str, ccb->dn_line, ccb->gsm_id);
+        }
+        return (TRUE);
+    }
+
+    return (FALSE);
+}
+
+/**
+ * This fucntion sends mid-call INVITE.
+ *
+ * @param[in] ccb             Pointer to ccsipCCB_t structure.
+ * @param[in] hold_initiated  boolean indicating whther this is
+ *                            a mid-call INVITE for HOLD feature.
+ *
+ * @pre               (ccb      not_eq NULL)
+ *
+ * @return            true or false
+ */
+boolean
+send_resume_or_hold_request (ccsipCCB_t *ccb, boolean hold_initiated)
+{
+    ccb->authen.cred_type = 0;
+    ccb->authen.new_flag = TRUE;
+    ccb->hold_initiated = hold_initiated;
+    if (sipSPISendInviteMidCall(ccb, FALSE /* doesn't expire */) != TRUE) {
+        return FALSE;
+    }
+    /* Pre-fill the ARP table */
+    ADD_TO_ARP_CACHE(ccb->dest_sip_addr);
+    return TRUE;
+}
+
+void
+ccsip_handle_idle_ev_cc_setup (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "idle_ev_cc_setup";
+    char           *dialString;
+    char           *referred_by_blind;
+    uint32_t        dialStringLength = 0;
+    char           *calledNumberTemp;
+    char           *outputString = NULL;
+    uint32_t        usernameLength = 0;
+    char           *hostnameString = NULL;
+    uint32_t        hostnameLength = 0;
+    char            proxy_ipaddr_str[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t   proxy_ipaddr;
+
+    char           *extraString = NULL;
+    uint32_t        extraLength = 0;
+    uint32_t        n = 0;
+    static char     dialtranslate[MAX_SIP_URL_LENGTH];
+    char            temp[MAX_IPADDR_STR_LEN];
+    char            line_name[MAX_LINE_NAME_SIZE];
+    int             port;
+    boolean         sendInvite = FALSE;
+    char           *tmpPtr = NULL;
+    cpr_ip_type     ip_type = CPR_IP_ADDR_IPV4;
+    boolean         replace = FALSE;
+
+    CPR_IP_ADDR_INIT(proxy_ipaddr);
+
+    ccb->gsm_id  = event->u.cc_msg->msg.setup.call_id;
+    ccb->dn_line = event->u.cc_msg->msg.setup.line;
+
+    /*
+     * Handlle replace info if there is any before taking in any
+     * dial string.
+     */
+    if (ccsip_is_replace_setup(event->u.cc_msg->msg.setup.replaces)) {
+        replace = TRUE;
+        if (!ccsip_set_replace_info(ccb, &event->u.cc_msg->msg.setup)) {
+            /* The replace info failed */
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            CCSIP_DEBUG_STATE(DEB_F_PREFIX"ignore setup, no replace info for"
+                              " replace setup request\n", DEB_F_PREFIX_ARGS(SIP_REP, fname));
+            return;
+        }
+    }
+
+    ccsip_set_join_info(ccb, &event->u.cc_msg->msg.setup);
+
+    dialString = (char *) event->u.cc_msg->msg.setup.caller_id.called_number;
+    referred_by_blind = event->u.cc_msg->msg.setup.redirect.redirects[0].number;
+    dialStringLength = strlen(dialString);
+    if ((0 == dialStringLength) && !replace) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No Digits to dial", fname);
+        sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+    } else if (!dialStringLength && replace) {
+        dialString = (char *) event->u.cc_msg->msg.setup.caller_id.calling_number;
+        dialStringLength = strlen(dialString);
+    }
+
+    /*
+     * Save away what they sent us so that we can display it to them later
+     *
+     * Note: Currently only dialString which is obtained from
+     *       event->u.cc_msg->msg.setup.caller_id.called_number is needed
+     *       by SIP stack. Make a duplicate copy of the field. If more
+     *       fields are needed from the caller_id from the setup msg.
+     *       then the better use of the cc_mv_caller_id() API to move
+     *       caller IDs from the setup to local storage is more efficient.
+     */
+    ccb->calledDisplayedName = strlib_update(ccb->calledDisplayedName,
+                                             dialString);
+
+    memset(proxy_ipaddr_str, 0, MAX_IPADDR_STR_LEN);
+    memset(temp, 0, sizeof(temp));
+
+
+    sip_util_get_new_call_id(ccb);
+    ccb->featuretype = CC_FEATURE_NONE;
+    /*
+     * Get the listen port from the Transport Interface
+     * instead of the direct config.
+     */
+    ccb->local_port = sipTransportGetListenPort(ccb->dn_line, ccb);
+
+    /*
+     * Normalize the name.  This means adding <SIP: to the start of the
+     * name if they did not include the <SIP: and also inserting the
+     * domain name if none was found
+     */
+
+    /* Make a copy of referred_by, if present.
+     * This is a HACK to add referred_by in case of blind transfer.
+     * As with current requirement for MCI we drop the call as soon
+     * as refer is sent.  We do not wait for transfer to be successful
+     * but do send a notify and ack it This should not be needed once
+     * we go to actual REFER draft.
+     */
+    if (event->u.cc_msg->msg.setup.redirect.redirects[0].number[0] != '\0') {
+        ccb->sip_referredBy = strlib_update(ccb->sip_referredBy,
+                                            referred_by_blind);
+        ccb->blindtransferred = TRUE;
+    }
+    if (event->u.cc_msg->msg.setup.redirect.redirects[0].number[0] != '\0') {
+        strlib_free(event->u.cc_msg->msg.setup.redirect.redirects[0].number);
+    }
+
+    /*
+     * See if the string needs to be rewritten by applying the dial template
+     */
+    ccb->routeMode = RouteDefault;
+
+#define CAST_N (int32_t *)
+
+    (void) MatchDialTemplate(ccb->calledDisplayedName, ccb->dn_line, CAST_N & n, dialtranslate,
+                             sizeof(dialtranslate), (RouteMode *) & (ccb->routeMode), NULL);
+    dialString = dialtranslate;
+    dialStringLength = strlen(dialString);
+
+    /*
+     * Throw away any display name part before LAQUOT(<),  if there is one
+     * Ex: "Test Test" <sip:31@172.18.192.230> - Display string is "Test Test"
+     * Ex: Test Test <sip:31@172.18.192.230>   - Display string is "Test Test"
+     * Ex: <sip:31@172.18.192.230>  - No Display string
+     * Ex: sip:31@172.18.192.230 - No Display string
+     * Ex: 31@172.18.192.230     - No Display string
+     *
+     * the last two examples have no LAQUOT and RAQUOT in them
+     *
+     */
+
+    tmpPtr = strchr(dialString, '<');
+    if (tmpPtr) {
+        dialString = tmpPtr;
+        dialStringLength = strlen(dialString);
+    }
+
+    /*
+     * Throw away any part of <SIP: that they might have entered
+     */
+    if ((dialStringLength > 0) && (dialString[0] == '<')) {
+        dialString++;
+        dialStringLength--;
+    }
+
+    /*
+     * For the SIP: part, we have to have 4 characters
+     */
+    if (!cpr_strncasecmp(dialString, "sip:", 4)) {
+        dialStringLength -= 4;
+        dialString += 4;
+    }
+
+    /*
+     * Parse the remainder of the string looking for the host name
+     */
+    for (n = 0; n <= dialStringLength; n++) {
+        /*
+         * If we hit the end of the string without encountering a ; or >
+         * then we want to put the entire string into the user name if
+         * no host name was encountered or if we found a '@' along the
+         * way we want to put the residual into the host name
+         */
+        if (n == dialStringLength) {
+            if (hostnameString == NULL) {
+                usernameLength = n;
+            } else {
+                hostnameLength = n - usernameLength - 1; //Accounts for @
+            }
+        } else if ((dialString[n] == '@') && (hostnameString == NULL)) {
+            /*
+             * We encountered an @ separator for the host name, so take
+             * everything before the @ and put it into the user name
+             */
+            usernameLength = n;
+            hostnameString = dialString + n + 1;
+        } else if ((dialString[n] == ';') || (dialString[n] == '>')) {
+            /*
+             * We hit the separator character (; or >).  Take what we have
+             * seen and append it to the host name (if we already saw the @)
+             * or to the user name if not
+             */
+            if (hostnameString == NULL) {
+                usernameLength = n;
+            } else {
+                hostnameLength = n - usernameLength - 1; // Accounts for @
+            }
+            extraString = dialString + n;
+            extraLength = dialStringLength - n;
+            break;
+        }
+    }
+    /*
+     * At this point we have
+     *   usernameLength - Number of characters in the user name
+     *                    starting from dialString[0]
+     *   hostnameString - NULL if no @ was encountered, otherwise it
+     *                      points to the start of the host name string
+     *   hostnameLength - 0 if no host name characters were encountered
+     *   extraString - Points to any extra parameters
+     *   extraLength - number of extra parameter characters
+     */
+    switch (ccb->routeMode) {
+    case RouteEmergency:{
+            /*
+             * If we have failed over to the backup we need
+             * to not reselect the emergency proxy
+             */
+            if (ccb->proxySelection != SIP_PROXY_BACKUP) {
+                // Get the Emergency Proxy
+                sipTransportGetEmerServerAddress(ccb->dn_line, proxy_ipaddr_str);
+                if (hostnameLength == 0) {
+                    hostnameString = proxy_ipaddr_str;
+                    hostnameLength = strlen(proxy_ipaddr_str);
+                }
+                if (proxy_ipaddr_str[0]) {
+                    if (!str2ip((const char *)proxy_ipaddr_str, &proxy_ipaddr)) {
+                        /* Fill in address and port in CCB */
+                        util_ntohl(&(ccb->dest_sip_addr), &proxy_ipaddr);
+                        /*
+                         * If the Proxy Emergency Port isn't contained in the
+                         * config table, use the PROXYN port instead.
+                         */
+                        port = sipTransportGetEmerServerPort(ccb->dn_line);
+                        if (port) {
+                            ccb->dest_sip_port = (uint32_t)port;
+                        } else {
+                            ccb->dest_sip_port = sipTransportGetPrimServerPort(ccb->dn_line);
+                        }
+                        break;
+                    }
+                }
+            }
+            // Otherwise Emergency proxy is not in configuration follow thru. Let
+            // static analysis know this is intentional
+         /*FALLTHROUGH*/}
+    default:
+        ip_type = sipTransportGetPrimServerAddress(ccb->dn_line, temp);
+        if (hostnameLength == 0) {
+            hostnameString = temp;
+            hostnameLength = strlen(hostnameString);
+        }
+        sipTransportGetServerIPAddr(&(ccb->dest_sip_addr),ccb->dn_line);
+        if (util_check_if_ip_valid(&(ccb->dest_sip_addr))) {
+            ccb->dest_sip_port = sipTransportGetPrimServerPort(ccb->dn_line);
+        } else {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Unable to reach proxy, attempting backup.\n",
+                             DEB_F_PREFIX_ARGS(SIP_PROXY, fname));
+            if (!ccsip_attempt_backup_proxy(ccb)) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Attempt to reach backup proxy failed.\n",
+                                 DEB_F_PREFIX_ARGS(SIP_PROXY, fname));
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"INVITE will be broadcast.\n",
+                    DEB_F_PREFIX_ARGS(SIP_PROXY, fname));
+            }
+        }
+    }
+
+    /* Pre-fill the ARP table */
+    ADD_TO_ARP_CACHE(ccb->dest_sip_addr);
+
+    /*
+     * Construct the actual dial out string
+     */
+    calledNumberTemp = strlib_open(ccb->calledNumber, (MAX_SIP_URL_LENGTH * 2));
+    outputString = calledNumberTemp;
+    sstrncpy(outputString, "<sip:", MAX_SIP_URL_LENGTH);
+    outputString += 5;
+    if (usernameLength) {
+        outputString += sippmh_convertURLCharToEscChar(dialString,
+                                                       usernameLength,
+                                                       outputString,
+                                                       (MAX_SIP_URL_LENGTH * 2) - 5, FALSE);
+        *outputString++ = '@';
+    }
+    if ((hostnameLength) && (hostnameString)) {
+        if (ip_type == CPR_IP_ADDR_IPV6) {
+            *outputString++ = '[';
+        }
+        memcpy(outputString, hostnameString, hostnameLength);
+        outputString += hostnameLength;
+        if (ip_type == CPR_IP_ADDR_IPV6) {
+            *outputString++ = ']';
+        }
+    }
+    if (extraLength) {
+        memcpy(outputString, extraString, extraLength);
+        /*
+         * Null terminate the string so we can quickly look for a >
+         */
+        outputString[extraLength] = 0;
+        /*
+         * If there was no > in what they gave us, put one in for them
+         */
+        if (strchr(outputString, '>') == NULL) {
+            outputString[extraLength++] = '>';
+        }
+        outputString += extraLength;
+    } else {
+        *outputString++ = '>';
+    }
+    /*
+     * Null terminate the string for good measure and note how long it is
+     */
+    *outputString = 0;
+    ccb->calledNumber = strlib_close(calledNumberTemp);
+    if (ccb->calledNumber) {
+        ccb->calledNumberLen = (uint16_t) strlen(ccb->calledNumber);
+    }
+
+    CCSIP_DEBUG_STATE(DEB_F_PREFIX"All digits collected.  Placing the call\n",
+        DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+    config_get_line_string(CFGID_LINE_NAME, line_name, ccb->dn_line, sizeof(line_name));
+    ccb->callingNumber = strlib_update(ccb->callingNumber, line_name);
+
+    CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"SIPSM %d: Setup\n",
+                      DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname), ccb->index);
+
+    /* Copy the call-info into the CCB */
+    ccsip_store_call_info(&event->u.cc_msg->msg.setup.call_info, ccb);
+
+    /* Send INVITE */
+
+    /* Save the GSM's msg. bodies for future used */
+    ccsip_save_local_msg_body(ccb, &event->u.cc_msg->msg.setup.msg_body);
+
+    /*
+     * CC_REDIRECT_REASON_DEFLECTION shows that this is an attended transfer
+     */
+    // Note that the extra body parts will be automatically deleted
+    if (event->u.cc_msg->msg.setup.redirect.redirects[0].redirect_reason
+        == CC_REDIRECT_REASON_DEFLECTION) {
+        sendInvite = sipSPISendInvite(ccb, SIP_INVITE_TYPE_TRANSFER, TRUE);
+    } else {
+        sendInvite = sipSPISendInvite(ccb, SIP_INVITE_TYPE_NORMAL, TRUE);
+    }
+
+    if (sendInvite == TRUE) {
+        sip_sm_change_state(ccb, SIP_STATE_SENT_INVITE);
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPISendInvite failed", fname);
+        if (event->u.cc_msg->msg.setup.redirect.redirects[0].redirect_reason
+            == CC_REDIRECT_REASON_DEFLECTION) {
+            /*
+             * Sending replaces invite for attended transfer has failed.
+             * Clean up here because a release complete from GSM
+             * is not guaranteed here like in the case of pre-mature
+             * attended transfer request. A cause value of normal
+             * causes GSM to clean up the UI as well.
+             */
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL, NULL);
+            sip_sm_call_cleanup(ccb);
+        } else {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        }
+
+    }
+
+}
+
+
+/*
+ *
+ ***** SIP_STATE_SENT_INVITE
+ *
+ */
+void
+ccsip_handle_sentinvite_ev_sip_1xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "sentinvite_ev_sip_1xx";
+    sipMessage_t   *response;
+    sipRespLine_t  *respLine;
+    int             status_code = 0;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+
+    /* Get the status code */
+    respLine = sippmh_get_response_line(response);
+    if (respLine) {
+        status_code = respLine->status_code;
+        SIPPMH_FREE_RESPONSE_LINE(respLine);
+    }
+
+    /*
+     * Update the Tags here so that they will be correct if
+     * the user transfers a ringing call.
+     */
+    sip_sm_200and300_update(ccb, response, status_code);
+
+    sip_decrement_backup_active_count(ccb);
+
+    /* Mark the CCB as having received a 1xx response */
+    ccb->flags |= RECD_1xx;
+
+    if (status_code != SIP_1XX_TRYING) {
+        /* Reset credentials flag since INVITE was successfully processed */
+        ccb->authen.cred_type = 0;
+    }
+
+    switch (status_code) {
+
+    case SIP_1XX_TRYING:
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY), ccb->index,
+                          ccb->dn_line, fname, sip_util_state2string(ccb->state),
+                          "SIP 100 TRYING");
+        /*
+         * Update connected party info from RPID and Call-Info header.
+         * Do not delay call info update to UI with proceeding. There is
+         * no media event with the proceeding that can cause UI update
+         * automatically.
+         */
+        ccsip_update_callinfo(ccb, response, TRUE, FALSE, FALSE);
+        free_sip_message(response);
+        sip_cc_proceeding(ccb->gsm_id, ccb->dn_line);
+        return;
+
+    case SIP_1XX_RINGING:
+        {
+            sipsdp_status_t sdp_status;
+            CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: %s <- SIP 180 RINGING\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                                                         ccb->index, sip_util_state2string(ccb->state));
+
+            /* check for alert-info in header.
+             * Commented out until DSP upgrade
+             alert_info = sippmh_get_header_val(request, SIP_HEADER_ALERT_INFO,
+                                                NULL);
+             ccb->alert_info = ALERTING_NONE;
+             if (alert_info) {
+             parseAlertingHeader(ccb, alert_info);
+             }
+             */
+            sdp_status = sip_util_extract_sdp(ccb, response);
+
+            switch (sdp_status) {
+            case SIP_SDP_SUCCESS:
+            case SIP_SDP_SESSION_AUDIT:
+                ccb->oa_state = OA_IDLE;
+                /* ccsip_update_callinfo needs to occur before cc_alerting */
+                ccsip_update_callinfo(ccb, response, TRUE, FALSE, TRUE);
+                sip_cc_alerting(ccb->gsm_id, ccb->dn_line, response, TRUE);
+                break;
+
+            case SIP_SDP_NOT_PRESENT:
+                /* ccsip_update_callinfo needs to occur before cc_alerting */
+                ccsip_update_callinfo(ccb, response, TRUE, FALSE, TRUE);
+                sip_cc_alerting(ccb->gsm_id, ccb->dn_line, NULL, 0);
+                break;
+
+            case SIP_SDP_DNS_FAIL:
+            case SIP_SDP_NO_MEDIA:
+            case SIP_SDP_ERROR:
+            default:
+                sipSPISendCancel(ccb);
+                free_sip_message(response);
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+                return;
+            }
+            /*
+             * Update connected party info from RPID and Call-Info header.
+             * The call update to UI can be delayed due to altert processing
+             * can potentially manipulate media or port.
+             */
+            free_sip_message(response);
+            return;
+
+        }                       /* case SIP_1XX_RINGING */
+    case SIP_1XX_SESSION_PROGRESS:
+        {
+            sipsdp_status_t sdp_status;
+
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY), ccb->index,
+                              ccb->dn_line, fname, sip_util_state2string(ccb->state),
+                              "SIP 183 IN BAND SESSION PROGRESS");
+
+            sdp_status = sip_util_extract_sdp(ccb, response);
+
+            switch (sdp_status) {
+            case SIP_SDP_SUCCESS:
+            case SIP_SDP_SESSION_AUDIT:
+                ccb->oa_state = OA_IDLE;
+                break;
+
+            case SIP_SDP_NOT_PRESENT:
+                // In this case no SDP is present in the 183 message.
+                // Call flows exist where a callee may send 183 with no SDP and
+                // the right way to handle it is to remain in the same state.
+                // DDTS for reference is CSCdu17240
+                /* Update connected party info from RPID and Call-Info header */
+                ccsip_update_callinfo(ccb, response, TRUE, FALSE, FALSE);
+                free_sip_message(response);
+                return;
+
+            case SIP_SDP_DNS_FAIL:
+            case SIP_SDP_NO_MEDIA:
+            case SIP_SDP_ERROR:
+            default:
+                sipSPISendCancel(ccb);
+                free_sip_message(response);
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+                return;
+            }
+
+            /*
+             * Update connected party info from RPID and Call-Info header.
+             * The call update to UI can be delayed due to altert processing
+             * can potentially manipulate media or port.
+             */
+            ccsip_update_callinfo(ccb, response, TRUE, FALSE, TRUE);
+            sip_cc_alerting(ccb->gsm_id, ccb->dn_line, response, TRUE);
+
+            /*UI-STATE tag with value of "BUSY" is being sent to indicate the busy line
+             *and to support callback feature. GSM needs to be informed so it can
+             *trigger the LSM to transition to BUSY state and display appropriate
+             *softkeys.
+             */
+            if ((ccb->in_call_info) &&
+                (ccb->in_call_info->data.call_info_feat_data.feature_flag & CC_UI_STATE) &&
+                (ccb->in_call_info->data.call_info_feat_data.ui_state == CC_UI_STATE_BUSY)) {
+                 CCSIP_DEBUG_STATE(DEB_F_PREFIX"DETECTED UI_STATE=BUSY IN 183.\n",
+                     DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+                 sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_UI_STATE_BUSY, NULL);
+            }
+            free_sip_message(response);
+            return;
+        }                       /* case SIP_1XX_SESSION_PROGRESS */
+
+    case SIP_1XX_CALL_FWD:
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: %s <- SIP 181 CALL IS BEING"
+                          "FORWARDED\n",
+                                                 DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                          ccb->index, sip_util_state2string(ccb->state));
+        /* Update connected party info from RPID and Call-Info header */
+        ccsip_update_callinfo(ccb, response, TRUE, FALSE, FALSE);
+        free_sip_message(response);
+        sip_cc_proceeding(ccb->gsm_id, ccb->dn_line);
+        return;
+
+    case SIP_1XX_QUEUED:
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: %s <- SIP 182 QUEUED\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                          ccb->index, sip_util_state2string(ccb->state));
+        /* Update connected party info from RPID and Call-Info header */
+        ccsip_update_callinfo(ccb, response, TRUE, FALSE, FALSE);
+        free_sip_message(response);
+        sip_cc_proceeding(ccb->gsm_id, ccb->dn_line);
+        return;
+
+    default:
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: %s <- SIP BAD 1xx\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                          ccb->index, sip_util_state2string(ccb->state));
+        free_sip_message(response);
+        return;
+    }
+}
+
+
+void
+ccsip_handle_sentinvite_ev_sip_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "sentinvite_ev_sip_2xx";
+    sipMessage_t   *response;
+    const char     *contact = NULL;
+    sipsdp_status_t sdp_status;
+    string_t        recv_info_list = strlib_empty();
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+
+    /* Check if this is an INVITE response */
+    if (!sip_sm_is_invite_response(response)) {
+        sipMethod_t method = sipMethodInvalid;
+        int         response_code = 0;
+
+        // If this is a 202 response to a REFER, we should handle it separately
+        if (sipGetResponseCode(response, &response_code) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipGetResponseCode");
+            free_sip_message(response);
+            return;
+        }
+        if (sipGetResponseMethod(response, &method) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipGetResponseMethod");
+            free_sip_message(response);
+            return;
+        }
+
+        if (response_code == SIP_ACCEPTED && method == sipMethodRefer) {
+            ccsip_handle_accept_2xx(ccb, event);
+            return;
+        }
+        free_sip_message(response);
+        clean_method_request_trx(ccb, method, TRUE);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED), ccb->index,
+                          ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        return;
+    }
+
+    /*
+     * Record the "tag=" parameter.
+     * Update To/From (to capture tag). Also Contact, and Record-Route
+     */
+    sip_sm_200and300_update(ccb, response, SIP_STATUS_SUCCESS);
+
+    /* Reset credentials flag since INVITE was successfully processed */
+    ccb->authen.cred_type = 0;
+
+    sip_decrement_backup_active_count(ccb);
+    (void) sip_platform_expires_timer_stop(ccb->index);
+    /* Check Contact header */
+    contact = sippmh_get_cached_header_val(response, CONTACT);
+    if (contact) {
+        if (sipSPICheckContact(contact) < 0) {
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "sipSPICheckContact()");
+            free_sip_message(response);
+            ccb->authen.cred_type = 0;
+            sipSPISendBye(ccb, NULL, NULL);
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            clean_method_request_trx(ccb, sipMethodAck, FALSE);
+            return;
+        }
+    }
+
+    /* Extract destination SDP and related fields */
+    sdp_status = sip_util_extract_sdp(ccb, response);
+
+    switch (sdp_status) {
+    case SIP_SDP_SUCCESS:
+    case SIP_SDP_SESSION_AUDIT:
+        ccb->oa_state = OA_IDLE;
+        break;
+
+    case SIP_SDP_NOT_PRESENT:
+        break;
+
+    case SIP_SDP_DNS_FAIL:
+    case SIP_SDP_NO_MEDIA:
+    case SIP_SDP_ERROR:
+    default:
+        /* First Ack and then send Bye to the far end */
+        if (sipSPISendAck(ccb, response) == FALSE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPISendAck");
+        }
+        /* Update connected party info from RPID and Call-Info header */
+        ccsip_update_callinfo(ccb, response, TRUE, FALSE, FALSE);
+
+        free_sip_message(response);
+        sipSPISendBye(ccb, NULL, NULL);
+        sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+        if (ccb->wastransferred) {
+            /*
+             * The referred call was implicitly subscribed for the
+             * notification.  Since we get the Error from target we
+             * need to send this notify to transferor
+             */
+            cc_feature_data_t data;
+
+            data.notify.cause = CC_CAUSE_ERROR;
+            data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+            data.notify.method = CC_XFER_METHOD_REFER;
+            data.notify.blind_xferror_gsm_id = 0;
+            sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_NOTIFY,
+                           (void *) &data);
+        }
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        return;
+    }
+
+    /*
+     * Parse the diversion header which could be present, as in case of
+     * Auto Pick up
+     */
+    ccsip_parse_diversion_header (ccb, response);
+
+    /* Info Package stuff */
+    ccsip_parse_send_info_header(response, &recv_info_list);
+
+    /*
+     * Update connected party info from RPID and Call-Info header.
+     * The call update to UI can be delayed due to connected processing
+     * manipulates media or port.
+     */
+    ccsip_update_callinfo(ccb, response, TRUE, FALSE, TRUE);
+    sip_cc_connected(ccb->gsm_id, ccb->dn_line, recv_info_list, response);
+
+    strlib_free(recv_info_list);
+
+    /* Deallocate the memory for the response */
+    free_sip_message(response);
+    sip_sm_change_state(ccb, SIP_STATE_SENT_INVITE_CONNECTED);
+
+    /*
+     * The referred call was implicitely subscribed for the notification.
+     * Since we get the 200 OK from target we need to send this notify
+     * to transferor. So make a feature request to the GSM
+     */
+    if ((ccb->wastransferred) || (ccb->blindtransferred == TRUE)) {
+        cc_feature_data_t data;
+
+        data.notify.cause = CC_CAUSE_OK;
+        data.notify.cause_code = SIP_SUCCESS_SETUP;
+        data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+        data.notify.method = CC_XFER_METHOD_REFER;
+        data.notify.blind_xferror_gsm_id =
+            sip_sm_get_blind_xfereror_ccb_by_gsm_id(ccb->gsm_id);
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_NOTIFY,
+                       (void *) &data);
+        strlib_free(ccb->sipxfercallid);
+        ccb->sipxfercallid = strlib_empty();
+    } else if (ccb->flags & SENT_INVITE_REPLACE) {
+        strlib_free(ccb->sipxfercallid);
+        ccb->sipxfercallid = strlib_empty();
+    }
+}
+
+
+/*
+ *
+ ***** SIP_STATE_RELEASING
+ *
+ */
+
+void
+ccsip_handle_sentbye_recvd_invite (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    sipMessage_t *request;
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+    (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_CALLEG,
+                                   SIP_CLI_ERR_CALLEG_PHRASE, 0, NULL, NULL);
+    free_sip_message(request);
+}
+
+void
+ccsip_handle_release_complete (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "release_complete";
+
+    if (ccb->blind_xfer_call_id == CC_NO_CALL_ID) {
+        if (!ccb->wait_for_ack) {
+            if ((ccb->flags & RECD_BYE) && (ccb->last_request)) {
+                (void) sipSPISendByeOrCancelResponse(ccb, ccb->last_request, sipMethodBye);
+                ccb->flags &= ~RECD_BYE;
+            }
+            if (!(sip_platform_msg_timer_outstanding_get(ccb->index))) {
+                sip_sm_call_cleanup(ccb);
+            }
+        } else {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"INFO: waiting for Invite Response Ack "
+                             "before clearing call\n", DEB_F_PREFIX_ARGS(SIP_ACK, fname));
+            /*
+             * Restart the disconnect timer. Call to start will also stop
+             * the timer if it is currently running.
+             */
+            (void) sip_platform_supervision_disconnect_timer_start(
+                       SUPERVISION_DISCONNECT_TIMEOUT, ccb->index);
+        }
+    } else {
+        /*
+         * Wait for the transfered call to finish and then we have
+         * to send Notify for the transferred call to the transferror.
+         * HACK to please customer.
+         */
+        (void) sip_platform_supervision_disconnect_timer_stop(ccb->index);
+        sip_sm_change_state(ccb, SIP_STATE_BLIND_XFER_PENDING);
+    }
+}
+
+void
+ccsip_handle_sentbye_ev_sip_1xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char   *fname = "sentbye_ev_sip_1xx";
+    sipMessage_t *response;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+
+    /* Check if this is an BYE/CANCEL response */
+    if (!sip_sm_is_bye_or_cancel_response(response)) {
+        if (sip_sm_is_invite_response(response)) {
+            // It could be an INVITE response if user opted to end the call
+            // before we received a 1xx from the remote end
+            // Send the deferred CANCEL now
+            if (ccb->flags & SEND_CANCEL) {
+                sipSPISendCancel(ccb);
+            }
+        }
+        free_sip_message(response);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED), ccb->index,
+                          ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        return;
+    }
+    /*
+     * Restart the disconnect timer. Call to start will also stop
+     * the timer if it is currently running.
+     */
+    (void) sip_platform_supervision_disconnect_timer_start(
+                SUPERVISION_DISCONNECT_TIMEOUT, ccb->index);
+    free_sip_message(response);
+}
+
+void
+ccsip_handle_sentbye_ev_sip_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char   *fname = "sentbye_ev_sip_2xx";
+    sipMessage_t *response;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+
+    /* Check if this is not a response to either CANCEL or BYE */
+    if (!sip_sm_is_bye_or_cancel_response(response)) {
+        /* Check if this is an INVITE response */
+
+        //Check if the response to an INVITE and we have sent out a CANCEL
+        if (sip_sm_is_invite_response(response) &&
+            (get_method_request_trx_index(ccb, sipMethodCancel, TRUE) != -1)) {
+            /*
+             * We have an outgoing CANCEL and 200 OK(INVITE) cross on the wire.
+             *
+             * We may have sent a CANCEL and around the same time,
+             * UAS may have responded with a 200 OK(INVITE) and
+             * so the CANCEL and 200 OK(INVITE) cross on the wire.
+             * So, the CANCEL is now useless.
+             *
+             * Actions:
+             * send ACK to satisfy 200 OK(INVITE)
+             * send BYE to initiate hangup since CANCEL is now useless
+             */
+           char *to_tag, *sip_to_temp;
+
+           to_tag = strstr(ccb->sip_to,";tag");
+           /*
+            * Add the to_tag to the Ack and Bye message if it is not
+            * already present.
+            */
+           if (!to_tag) {
+                sip_to_temp = strlib_open(ccb->sip_to, MAX_SIP_URL_LENGTH);
+                if (sip_to_temp) {
+                    sstrncat(sip_to_temp, ";tag=",
+                             MAX_SIP_URL_LENGTH - strlen(sip_to_temp));
+                    if (ccb->sip_to_tag) {
+                        sstrncat(sip_to_temp, ccb->sip_to_tag,
+                        MAX_SIP_URL_LENGTH - strlen(sip_to_temp));
+                   }
+               }
+                ccb->sip_to = strlib_close(sip_to_temp);
+           }
+
+            if (sipSPISendAck(ccb, NULL) == FALSE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "sipSPISendAck");
+            }
+            sipSPISendBye(ccb, NULL, NULL);
+
+            CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX" %d %s Cross-over situation CANCEL/200 OK(INVITE).\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_ACK, ccb->dn_line, ccb->gsm_id, fname),
+                              ccb->index, sip_util_state2string(ccb->state));
+        } else {
+            //This 200 OK is not related to BYE, CANCEL, nor INVITE
+            //We still need to account for it remove the transaction
+            sipMethod_t method = sipMethodInvalid;
+
+            if (sipGetResponseMethod(response, &method) < 0) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "sipGetResponseMethod");
+                free_sip_message(response);
+                return;
+            }
+
+            clean_method_request_trx(ccb, method, TRUE);
+        }
+
+        free_sip_message(response);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        return;
+    }
+    (void) sip_platform_expires_timer_stop(ccb->index);
+    if (!ccb->send_delayed_bye) {
+        sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+    }
+
+    if (!ccb->wait_for_ack) {
+        sip_sm_call_cleanup(ccb);
+    } else {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"INFO: waiting for Invite Response Ack "
+                         "before clearing call\n", DEB_F_PREFIX_ARGS(SIP_ACK, fname));
+        /*
+         * Restart the disconnect timer.  Call to start will also stop
+         * the timer if it is currently running.
+         */
+        (void) sip_platform_supervision_disconnect_timer_start(
+                    SUPERVISION_DISCONNECT_TIMEOUT, ccb->index);
+    }
+    free_sip_message(response);
+}
+
+
+/*
+ * Just respond to the Bye in the Release state.
+ */
+void
+ccsip_handle_release_ev_sip_bye (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "blindxfr_ev_sip_bye";
+    sipMessage_t   *request;
+    uint16_t        request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    sipMethod_t     method = sipMethodInvalid;
+
+    memset(request_check_reason_phrase, 0, SIP_WARNING_LENGTH);
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    /* Request check and store */
+    sipGetRequestMethod(request, &method);
+    if (sip_sm_request_check_and_store(ccb, request, method, TRUE,
+                                       &request_check_reason_code,
+                                       request_check_reason_phrase, FALSE) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       request_check_reason_code,
+                                       request_check_reason_phrase, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+    (void) sipSPISendByeOrCancelResponse(ccb, request, sipMethodBye);
+}
+
+
+void
+ccsip_handle_sentblindntfy_ev_sip_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    sipMessage_t *response;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+    (void) sip_platform_expires_timer_stop(ccb->index);
+
+    if (ccb->flags & FINAL_NOTIFY) {
+        sip_sm_call_cleanup(ccb);
+    } else {
+        clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+    }
+
+    free_sip_message(response);
+}
+
+
+void
+ccsip_handle_sendbye_ev_supervision_disconnect (ccsipCCB_t *ccb,
+                                                sipSMEvent_t *event)
+{
+
+    (void) sip_platform_expires_timer_stop(ccb->index);
+    sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+    sip_sm_call_cleanup(ccb);
+}
+
+/**
+ *
+ * Handler for SIP_STATE_RELEASE features. Currently only CANCEL feature is
+ * supported in this state.
+ *
+ * @param line, ccb, event
+ *
+ * @return  void
+ *
+ * @pre     (event not NULL)
+ */
+/*
+ * SIP_STATE_RELEASE
+ */
+void
+ccsip_handle_release_ev_cc_feature (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "release_ev_cc_feature";
+    cc_features_t   feature_type;
+
+    feature_type = event->u.cc_msg->msg.feature.feature_id;
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY),
+                      ccb->index, ccb->dn_line, fname,
+                      sip_util_state2string(ccb->state),
+                      cc_feature_name(feature_type));
+
+    switch (feature_type) {
+    case CC_FEATURE_CANCEL:
+        break;
+    default:
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FEATURE_UNSUPPORTED),
+                          ccb->index, ccb->dn_line, fname);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type, NULL,
+                           CC_CAUSE_ERROR);
+        break;
+    }
+}
+
+/**
+ *
+ * Handler for event RELEASE at SIP_STATE_RELEASE.
+ * sipstack is waiting for RELEASE_COMPLETE from gsm, but it got RELEASE from gsm.
+ * so we know that gsm also want to release the call.
+ * to avoid defects like CSCtg46399, we need to send RELEASE_COMPLETE to both sipstack & gsm,
+ * then sipstack & gsm will not wait for each other, and the call can be cleared on both sides.
+ *
+ */
+void
+ccsip_handle_release_ev_release (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "release_ev_release";
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY),
+                      ccb->index, ccb->dn_line, fname,
+                      sip_util_state2string(ccb->state),
+                      "sipstack at SIP_STATE_RELEASE received a RELEASE event from gsm");
+
+    /* send RELEASE_COMPLETE to sipstack */
+    ccsip_handle_release_complete(ccb, event);
+
+    /* send RELEASE_COMPLETE to gsm */
+    ccsip_handle_sendbye_ev_supervision_disconnect(ccb, event);
+}
+
+
+void
+ccsip_handle_recv_error_response_ev_sip_ack (ccsipCCB_t *ccb,
+                                             sipSMEvent_t *event)
+{
+    sipMessage_t *response;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+
+    ccb->wait_for_ack = FALSE;
+
+    if (ccb->send_delayed_bye) {
+        // If we need to send a bye, do it now. Do not change state. Clean up
+        // the CCB once we receive the OK for the BYE
+        sipSPISendBye(ccb, NULL, NULL);
+    } else {
+        sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+        sip_sm_call_cleanup(ccb);
+    }
+
+    free_sip_message(response);
+}
+
+void
+ccsip_handle_sentbye_ev_sip_fxx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "sentbye_ev_sip_fxx";
+    sipMessage_t   *response;
+    sipRespLine_t  *respLine = NULL;
+    int             status_code = 0;
+    const char     *authenticate = NULL;
+    credentials_t   credentials;
+    sip_authen_t   *sip_authen = NULL;
+    char           *author_str = NULL;
+    boolean         good_authorization = FALSE;
+    const char     *rsp_method = NULL;
+    char           *alsoString = NULL;
+    sipMethod_t     method = sipMethodInvalid;
+    enum {
+        INVALID,
+        RESP_OF_BYE,
+        RESP_OF_CANCEL,
+        RESP_OF_NOTIFY,
+        RESP_OF_INVITE
+    } resp_type = INVALID;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+    if (sipGetResponseMethod(response, &method) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipGetResponseMethod");
+        free_sip_message(response);
+        return;
+    }
+    switch (method) {
+    case sipMethodInvite:
+        resp_type = RESP_OF_INVITE;
+        rsp_method = SIP_METHOD_INVITE;
+        break;
+    case sipMethodBye:
+        resp_type = RESP_OF_BYE;
+        rsp_method = SIP_METHOD_BYE;
+        break;
+    case sipMethodCancel:
+        resp_type = RESP_OF_CANCEL;
+        rsp_method = SIP_METHOD_CANCEL;
+        break;
+    case sipMethodNotify:
+        resp_type = RESP_OF_NOTIFY;
+        rsp_method = SIP_METHOD_NOTIFY;
+        break;
+    default:
+        resp_type = INVALID;
+        break;
+    }
+
+    if (INVALID == resp_type) {
+        free_sip_message(response);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        if (ccb->state == SIP_STATE_BLIND_XFER_PENDING) {
+            /* Got an error Response for Notify Message
+             * Just clean up at this point.
+             */
+            sip_sm_call_cleanup(ccb);
+        }
+        return;
+    }
+
+    sip_decrement_backup_active_count(ccb);
+    /* Get the status code */
+    respLine = sippmh_get_response_line(response);
+    if (respLine) {
+        status_code = respLine->status_code;
+        SIPPMH_FREE_RESPONSE_LINE(respLine);
+    }
+
+
+    if ((strcmp(rsp_method, SIP_METHOD_INVITE) == 0) &&
+        (status_code == SIP_SERV_ERR_INTERNAL)) {
+        /*
+         * This is likely a late arriving 500 response to INVITE.
+         * We are already in the releasing stage of the call having
+         * sent a CANCEL that crossed with the 500 response.
+         * Just ACK the 500 and return.
+         */
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Acking delayed 500 response to INVITE "
+                          "request\n", DEB_F_PREFIX_ARGS(SIP_ACK, fname));
+        sipSPISendFailureResponseAck(ccb, response, FALSE, 0);
+        return;
+    }
+
+
+    switch (status_code) {
+    case SIP_CLI_ERR_UNAUTH:
+    case SIP_CLI_ERR_PROXY_REQD:
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: %s\n",
+                                                 DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                                                 ccb->index, AUTH_BUGINF(status_code));
+
+        if (cred_get_credentials_r(ccb, &credentials) == FALSE) {
+            CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"retries exceeded: %d/%d\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                                                         ccb->index, ccb->authen.cred_type, MAX_RETRIES_401);
+
+            free_sip_message(response);
+            sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR);
+            sip_sm_call_cleanup(ccb);
+            return;
+        }
+
+        authenticate = sippmh_get_header_val(response, AUTH_HDR(status_code), NULL);
+        if (authenticate != NULL) {
+            CCSIP_DEBUG_STATE(DEB_F_PREFIX"Authenticate header %s= %s\n", DEB_F_PREFIX_ARGS(SIP_STATE, fname),
+                              AUTH_HDR_STR(status_code), authenticate);
+            ccb->retx_counter = 0;
+            sip_authen = sippmh_parse_authenticate(authenticate);
+            if (sip_authen) {
+                ccb->authen.new_flag = FALSE;
+                ccb->authen.cnonce[0] = '\0';
+                if (sipSPIGenerateAuthorizationResponse(sip_authen, ccb->ReqURI,
+                                                        rsp_method, credentials.id,
+                                                        credentials.pw,
+                                                        &author_str,
+                                                        &(ccb->authen.nc_count),
+                                                        ccb))
+                {
+                    good_authorization = TRUE;
+                    if (ccb->authen.authorization != NULL) {
+                        cpr_free(ccb->authen.authorization);
+                        ccb->authen.authorization = NULL;
+                    }
+                    if (ccb->authen.sip_authen != NULL) {
+                        sippmh_free_authen(ccb->authen.sip_authen);
+                        ccb->authen.sip_authen = NULL;
+                    }
+                    ccb->authen.authorization = (char *)
+                        cpr_malloc(strlen(author_str) * sizeof(char) + 1);
+
+                    /*
+                     * Cache the Authorization header so that it can be used for
+                     * later requests
+                     */
+                    if (ccb->authen.authorization != NULL) {
+                        sstrncpy(ccb->authen.authorization, author_str,
+                                 strlen(author_str) * sizeof(char) + 1);
+                        ccb->authen.status_code = status_code;
+                        ccb->authen.sip_authen = sip_authen;
+                    }
+
+                    cpr_free(author_str);
+                } else {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Authorization header "
+                                      "build unsuccessful\n", fname);
+                }
+                /*
+                 * CSCds70538
+                 * Do not free the sip_authen structure if the Authorization
+                 * build was successful. We will need these values to generate
+                 * an Authorization header for the BYE response
+                 */
+                if (good_authorization == FALSE) {
+                    sippmh_free_authen(sip_authen);
+                }
+            }
+            if (strcmp(rsp_method, SIP_METHOD_BYE) == 0) {
+                clean_method_request_trx(ccb, sipMethodBye, TRUE);
+                if (ccb->referto[0]) {
+                    alsoString = (char *) cpr_malloc(MAX_SIP_URL_LENGTH);
+                    if (!alsoString) {
+                        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                          ccb->index, ccb->dn_line, fname,
+                                          "malloc(also string)");
+                        sipSPISendBye(ccb, NULL, NULL);
+                        return;
+                    }
+                    sstrncpy(alsoString, ccb->referto, MAX_SIP_URL_LENGTH);
+                    sipSPISendBye(ccb, alsoString, NULL);
+                    cpr_free(alsoString);
+                    return;
+                }
+                /* Send BYE message */
+                sipSPISendBye(ccb, NULL, NULL);
+            } else if (strcmp(rsp_method, SIP_METHOD_CANCEL) == 0){
+                sipSPISendCancel(ccb);
+            } else {
+                (void) sipSPISendNotify(ccb, ccb->xfer_status);
+            }
+
+        } else {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"401/407 response missing "
+                              "Authenticate\n", fname);
+            sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR);
+            sip_sm_call_cleanup(ccb);
+        }
+        free_sip_message(response);
+        return;
+
+
+    case SIP_CLI_ERR_LOOP_DETECT:
+    case SIP_CLI_ERR_BUSY_HERE:
+    case SIP_CLI_ERR_MANY_HOPS:
+    case SIP_CLI_ERR_AMBIGUOUS:
+    case SIP_CLI_ERR_REQ_CANCEL:
+        if (sipSPISendAck(ccb, NULL) == FALSE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPISendAck");
+        }
+
+        (void) sip_platform_expires_timer_stop(ccb->index);
+        if (!ccb->send_delayed_bye) {
+            sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+        }
+
+        if (!ccb->wait_for_ack) {
+            sip_sm_call_cleanup(ccb);
+        } else {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"waiting for Invite Response Ack "
+                             "before clearing call\n", DEB_F_PREFIX_ARGS(SIP_ACK, fname));
+            /*
+             * Restart the disconnect timer. Call to start will also stop
+             * the timer if it is currently running.
+             */
+            (void) sip_platform_supervision_disconnect_timer_start(
+                        SUPERVISION_DISCONNECT_TIMEOUT, ccb->index);
+        }
+
+        free_sip_message(response);
+        return;
+
+    case SIP_SERV_ERR_NOT_IMPLEM:
+        /* It's an error response to a notify. rfc3515 tells us:
+         * Terminating a subscription,
+         * either by explicitly unsubscribing or rejecting NOTIFY, is not an
+         * indication that the referenced request should be withdrawn or
+         * abandoned.
+         */
+
+        if (strcmp (rsp_method,SIP_METHOD_NOTIFY) == 0){
+            //ignore error
+            CCSIP_DEBUG_STATE(DEB_F_PREFIX"Ignoring NOTIFY error. But clean up "
+                              "transaction.\n", DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+            clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+
+            if (ccb->flags & FINAL_NOTIFY) {
+                sip_sm_call_cleanup(ccb);
+                CCSIP_DEBUG_STATE(DEB_F_PREFIX"Ignoring NOTIFY error. but "
+                                  "cleaning up call.\n", DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+            }
+            free_sip_message(response);
+            return;
+        }
+    /*FALLTHROUGH*/
+
+    default:
+        sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR);
+        sip_sm_call_cleanup(ccb);
+        free_sip_message(response);
+        return;
+    }
+}
+
+/*
+ *  Function: ccsip_handle_sentinvite_ev_sip_3xx
+ *
+ *  Parameters: CCB and the event
+ *
+ *  Description:  Sends ack for 3xx events and appropriate invite
+ *
+ *  Returns:   void
+ *
+ */
+void
+ccsip_handle_sentinvite_ev_sip_3xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "sentinvite_ev_sip_3xx";
+    sipMessage_t   *response;
+    sipRespLine_t  *respLine;
+    uint16_t        status_code = 0;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+
+    sip_decrement_backup_active_count(ccb);
+
+    /* Get the status code */
+    respLine = sippmh_get_response_line(response);
+    if (respLine) {
+        status_code = respLine->status_code;
+        SIPPMH_FREE_RESPONSE_LINE(respLine);
+    }
+
+    switch (status_code) {
+    case SIP_RED_MULT_CHOICES /* 300 */:
+    case SIP_RED_MOVED_PERM   /* 301 */:
+    case SIP_RED_MOVED_TEMP   /* 302 */:
+    case SIP_RED_USE_PROXY    /* 305 */:
+        /*
+         * Record the "tag=" parameter.
+         * Update To/From (to capture tag). Also Contact, and Record-Route
+         */
+        sip_sm_update_to_from_on_callsetup_finalresponse(ccb, response);
+        sip_sm_update_contact_recordroute(ccb, response, status_code,
+                                          FALSE /* not midcall */);
+
+        /*
+         * Send ACK to the original INVITE destination or to the address
+         * specified by the Record-Route
+         */
+        sipSPISendFailureResponseAck(ccb, response, FALSE, 0);
+
+        /* Reset credentials flag before attempting redirect */
+        ccb->authen.cred_type = 0;
+        ccb->first_pass_3xx = TRUE;
+        sip_redirect(ccb, response, status_code);
+
+        break;
+    default:
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d %d unsupported\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                          ccb->index, status_code);
+        break;
+    }
+
+    free_sip_message(response);
+}
+
+void
+handle_error_for_state (ccsipCCB_t *ccb, int status_code)
+{
+    ccsipCCB_t   *referccb = NULL;
+    cc_causes_t   fail_reason = CC_CAUSE_MIN;
+
+    if (ccb->state == SIP_STATE_SENT_INVITE) {
+        if (status_code == SIP_CLI_ERR_BUSY_HERE) {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_BUSY, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+
+        } else if (status_code == SIP_SERV_ERR_UNAVAIL) {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_CONGESTION, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+
+        } else if (status_code == SIP_CLI_ERR_NOT_AVAIL) {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_TEMP_NOT_AVAILABLE, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+
+        } else if ((status_code == SIP_CLI_ERR_MEDIA) ||
+                   (status_code == SIP_CLI_ERR_NOT_ACCEPT_HERE) ||
+                   (status_code == SIP_FAIL_NOT_ACCEPT)) {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_PAYLOAD_MISMATCH, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+
+        } else if (status_code == SIP_SERV_ERR_INTERNAL) {
+            // This is used to indicate a successful cfwdall interaction
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_REMOTE_SERVER_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+
+        } else if (((status_code == SIP_CLI_ERR_BAD_REQ) ||
+                    (status_code == SIP_CLI_ERR_CALLEG)) &&
+                   (ccb->wastransferred)) {
+            /* clean up here because a release complete from GSM
+             * is not guaranteed here like in the case of pre-mature
+             * attended transfer request. A cause value of normal
+             * causes GSM to clean up the UI as well.
+             */
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL, NULL);
+            sip_sm_call_cleanup(ccb);
+        } else if (status_code == SIP_CLI_ERR_NOT_FOUND) {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NOT_FOUND, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        } else if (status_code == SIP_CLI_ERR_REQ_CANCEL) {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NO_USER_ANS, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        } else {
+            /*
+             * This should take care of miscellaneous errors including 400
+             * bad request when its not a transfer. No special handling is
+             * done in case of 491 SIP_CLI_ERR_REQ_PENDING as that error is
+             * treated like any other 4xx in this state
+             */
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        }
+        if (ccb->blindtransferred == TRUE) {
+            cc_feature_data_t data;
+
+            data.notify.cause = CC_CAUSE_ERROR;
+            data.notify.cause_code = status_code;
+            data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+            data.notify.method = CC_XFER_METHOD_REFER;
+            data.notify.blind_xferror_gsm_id = sip_sm_get_blind_xfereror_ccb_by_gsm_id(ccb->gsm_id);
+            sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_NOTIFY,
+                           (void *) &data);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        }
+
+        /* make sure to send the Notify if we are in a transfer
+         * scenario. This way the transferor can cleanup properly.
+         */
+        if (ccb->wastransferred) {
+            referccb = sip_sm_get_target_call_by_gsm_id(ccb->gsm_id);
+            if (referccb != NULL) {
+                ccb->flags |= FINAL_NOTIFY;
+                (void) sipSPISendNotify(referccb, status_code);
+                ccb->xfer_status = status_code;
+            }
+        }
+    } else if (ccb->state == SIP_STATE_ACTIVE) {
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, CC_FEATURE_NONE, NULL,
+                           CC_CAUSE_ERROR);
+    } else if (ccb->state == SIP_STATE_SENT_MIDCALL_INVITE) {
+        if (status_code == SIP_CLI_ERR_BUSY_HERE) {
+            fail_reason = CC_CAUSE_BUSY;
+        } else if ((status_code == SIP_CLI_ERR_MEDIA) ||
+                   (status_code == SIP_CLI_ERR_NOT_ACCEPT_HERE) ||
+                   (status_code == SIP_FAIL_NOT_ACCEPT)) {
+            fail_reason = CC_CAUSE_PAYLOAD_MISMATCH;
+        } else if (status_code == SIP_CLI_ERR_REQ_PENDING) {
+            // The other side has sent us a 491. We will let GSM handle this error and
+            // we shall fall back to the state we were in prior to being in any of the
+            // states. These are:
+            fail_reason = CC_CAUSE_REQUEST_PENDING;
+        } else if (status_code == SIP_SERV_ERR_UNAVAIL) {
+            fail_reason = CC_CAUSE_SERV_ERR_UNAVAIL;
+        } else {
+            fail_reason = CC_CAUSE_ERROR;
+        }
+
+        /* ack the feature */
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, ccb->featuretype, NULL,
+                           fail_reason);
+        if (status_code == SIP_CLI_ERR_REQ_TIMEOUT) {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        } else {
+            /* Other error goes back to active, let GSM decide */
+            sip_sm_change_state(ccb, SIP_STATE_ACTIVE);
+        }
+    } else if (ccb->state == SIP_STATE_BLIND_XFER_PENDING) {
+        if (status_code == SIP_CLI_ERR_REQ_TIMEOUT) {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        }
+    }
+    if (ccb->wastransferred) {
+        /*
+         * The referred call was implicitely subscribed for the notification.
+         * Since we get the Error from target we need to send this notify
+         * to transferor.
+         */
+        cc_feature_data_t data;
+
+        data.notify.cause = fail_reason;
+        data.notify.cause_code = status_code;
+        data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+        data.notify.method = CC_XFER_METHOD_REFER;
+        data.notify.blind_xferror_gsm_id = 0;
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_NOTIFY,
+                       (void *)&data);
+    }
+}
+
+
+void
+ccsip_handle_sentinvite_ev_sip_fxx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "sentinvite_ev_sip_fxx";
+    sipMessage_t   *response;
+    sipRespLine_t  *respLine = NULL;
+    uint16_t        status_code = 0;
+    const char     *authenticate = NULL;
+    credentials_t   credentials;
+    sip_authen_t   *sip_authen = NULL;
+    char           *author_str = NULL;
+    boolean         good_authorization = FALSE;
+    sipMethod_t     method = sipMethodInvalid;
+    const char     *rsp_method = NULL;
+
+    enum {
+        INVALID,
+        RESP_OF_INVITE,
+        RESP_OF_REFER,
+        RESP_OF_NOTIFY
+    } resp_type = INVALID;
+
+    response = event->u.pSipMessage;
+
+    /* Check if this is an INVITE response */
+    if (sipGetResponseMethod(response, &method) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipGetResponseMethod");
+        free_sip_message(response);
+        return;
+    }
+    switch (method) {
+    case sipMethodInvite:
+        resp_type = RESP_OF_INVITE;
+        rsp_method = SIP_METHOD_INVITE;
+
+        /* Stop the expires timer started to await this response */
+        (void) sip_platform_expires_timer_stop(ccb->index);
+
+        /* Update connected party info from RPID and Call-Info header */
+        ccsip_update_callinfo(ccb, response, TRUE, FALSE, FALSE);
+
+        break;
+    case sipMethodRefer:
+        resp_type = RESP_OF_REFER;
+        rsp_method = SIP_METHOD_REFER;
+        break;
+    case sipMethodNotify:
+        resp_type = RESP_OF_NOTIFY;
+        rsp_method = SIP_METHOD_NOTIFY;
+        break;
+    default:
+        resp_type = INVALID;
+        break;
+    }
+
+    if (INVALID == resp_type) {
+        free_sip_message(response);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        return;
+    }
+
+    /* Get the status code */
+    respLine = sippmh_get_response_line(response);
+    if (respLine) {
+        status_code = respLine->status_code;
+        SIPPMH_FREE_RESPONSE_LINE(respLine);
+    }
+
+    /*
+     * Update the Tags here so that they will be correct if
+     * the user transfers a ringing call.
+     */
+    sip_sm_200and300_update(ccb, response, status_code);
+
+    // If the resp_type is RESP_OF_REFER and the feature type is one of the
+    // extendedrefer features, and the reason code is not a failure of authentication
+    // then indicate the error condition back to GSM
+    if (resp_type == RESP_OF_REFER) {
+        switch (ccb->featuretype) {
+        case CC_FEATURE_B2BCONF:
+        case CC_FEATURE_SELECT:
+        case CC_FEATURE_CANCEL:
+            if (status_code != SIP_CLI_ERR_UNAUTH && status_code != SIP_CLI_ERR_PROXY_REQD) {
+                CCSIP_DEBUG_STATE(DEB_F_PREFIX"Received error response for ext refer\n",
+                    DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+                sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, ccb->featuretype, NULL,
+                                   CC_CAUSE_ERROR);
+                clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+                free_sip_message(response);
+                return;
+            }
+        /*FALLTHROUGH*/
+        default:
+            break;
+        }
+    }
+
+    sip_decrement_backup_active_count(ccb);
+
+    switch (status_code) {
+    case SIP_CLI_ERR_UNAUTH:
+    case SIP_CLI_ERR_PROXY_REQD:
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"SIP_CLI_ERR_PROXY_REQD: %d: %s\n",
+                                                 DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                                                 ccb->index, AUTH_BUGINF(status_code));
+
+        /* Send ACK */
+        if (method == sipMethodInvite) {
+            sipSPISendFailureResponseAck(ccb, response, FALSE, 0);
+        }
+
+        if (cred_get_credentials_r(ccb, &credentials) == FALSE) {
+            CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"retries exceeded: %d/%d\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                              ccb->authen.cred_type, MAX_RETRIES_401);
+            /*
+             * Retries exceeded for 40X could be due to remote end
+             * not responding to our Request, and while we are retrying,
+             * proxy could ask us to reauthenticate. So, we may exceed
+             * retries for credentials due to some other reason than just
+             * auth failure, but we do not care. status_code is passed.
+             */
+
+            handle_error_for_state(ccb, status_code);
+            free_sip_message(response);
+            return;
+        }
+
+        authenticate = sippmh_get_header_val(response, AUTH_HDR(status_code),
+                                             NULL);
+        if (authenticate != NULL) {
+            CCSIP_DEBUG_STATE(DEB_F_PREFIX"Authenticate header %s= %s\n", DEB_F_PREFIX_ARGS(SIP_AUTH, fname),
+                              AUTH_HDR_STR(status_code), authenticate);
+
+            ccb->retx_counter = 0;
+
+            sip_authen = sippmh_parse_authenticate(authenticate);
+            if (sip_authen) {
+                ccb->authen.new_flag = FALSE;
+                /* We are sure at this point that it is either a response of
+                 * INVITE or REFER and nothing else
+                 */
+                ccb->authen.cnonce[0] = '\0';
+                ccb->authen.nc_count = 0; // New nonce, hence reset
+                if (sipSPIGenerateAuthorizationResponse(sip_authen,
+                                                        ccb->ReqURI,
+                                                        rsp_method,
+                                                        credentials.id,
+                                                        credentials.pw,
+                                                        &author_str,
+                                                        &(ccb->authen.nc_count), ccb)) {
+
+                    good_authorization = TRUE;
+
+                    if (ccb->authen.authorization != NULL) {
+                        cpr_free(ccb->authen.authorization);
+                        ccb->authen.authorization = NULL;
+                    }
+
+                    if (ccb->authen.sip_authen != NULL) {
+                        sippmh_free_authen(ccb->authen.sip_authen);
+                        ccb->authen.sip_authen = NULL;
+                    }
+
+                    ccb->authen.authorization = (char *) cpr_malloc(strlen(author_str) *
+                                                                    sizeof(char) + 1);
+
+                    /*
+                     * Cache the Authorization header so that it can be used for
+                     * later requests
+                     */
+                    if (ccb->authen.authorization != NULL) {
+                        sstrncpy(ccb->authen.authorization, author_str,
+                                 strlen(author_str) * sizeof(char) + 1);
+                        ccb->authen.status_code = status_code;
+                        ccb->authen.sip_authen  = sip_authen;
+                    }
+
+                    /* tell GSM we are trying */
+                    if (ccb->state == SIP_STATE_SENT_INVITE) {
+                        sip_cc_proceeding(ccb->gsm_id, ccb->dn_line);
+                    }
+                    cpr_free(author_str);
+                } else {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Authorization header "
+                                      "build unsuccessful\n", fname);
+                }
+                /*
+                 * CSCds70538
+                 * Do not free the sip_authen structure if the Authorization
+                 * build was successful. We will need these values to generate
+                 * an Authorization header for the BYE response
+                 */
+                if (good_authorization == FALSE) {
+                    sippmh_free_authen(sip_authen);
+                }
+            }
+
+            switch (resp_type) {
+            case RESP_OF_REFER:
+                /* We do not need to inform about this error to GSM */
+                // Delete the previous transaction block
+                clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+
+                {
+                    boolean refer_sent = FALSE;
+
+                    switch (ccb->featuretype) {
+                    case CC_FEATURE_B2BCONF:
+                    case CC_FEATURE_SELECT:
+                    case CC_FEATURE_CANCEL:
+                        break;
+                    default:
+                        break;
+                    }
+
+                    if (refer_sent) {
+                        break;
+                    }
+                }
+
+                if (sipSPISendRefer(ccb, (char *) ccb->sip_referTo, SIP_REF_XFER) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                      ccb->index, ccb->dn_line, fname,
+                                      "sipSPISendRefer Failed");
+                }
+                break;
+
+            case RESP_OF_INVITE:
+                if (ccb->state == SIP_STATE_SENT_INVITE) {
+                    // Since we need to start a new Dialog we should clear
+                    // any previous record route info
+                    if (ccb->record_route_info) {
+                        sippmh_free_record_route(ccb->record_route_info);
+                        ccb->record_route_info = NULL;
+                    }
+
+                    (void) sipSPISendInviteMidCall(ccb, TRUE /* does expire */);
+                } else {
+                    (void) sipSPISendInviteMidCall(ccb, FALSE /* doesn't expire */);
+                }
+                break;
+            case RESP_OF_NOTIFY:
+                // Delete the previous transaction block
+                clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+                if (sipSPISendNotify(ccb, ccb->xfer_status) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                      ccb->index, ccb->dn_line, fname,
+                                      "sipSPISendNotify Failed");
+                }
+                break;
+            default:
+                break;
+            }
+        } else {
+            if ((ccb->redirect_info != NULL)
+                && (method == sipMethodInvite)
+                && (ccb->state == SIP_STATE_SENT_INVITE)) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"401/407 response missing "
+                                  "Authenticate, redirecting to next add.\n", fname);
+                sip_redirect(ccb, response, status_code);
+            } else {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"401/407 response missing "
+                "Authenticate\n", fname);
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX "Clearing call\n", fname);
+                // Delete the previous transaction block
+                clean_method_request_trx(ccb, method, TRUE);
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            }
+        }
+
+        free_sip_message(response);
+        return;
+
+    case SIP_CLI_ERR_NOT_ALLOWED:
+    case SIP_SERV_ERR_NOT_IMPLEM:
+        if (RESP_OF_REFER == resp_type) {
+            /* Error occurred on the target end so need to inform transferror */
+            /*
+             * Since REFER method is not implemented at far side we got
+             * this error, at this point we need to switch to BYE/ALSO
+             * method.  It is assumed that BYE/ALSO will be successful;
+             * otherwise, any way this call will be disconnected without
+             * any notification to the enduser which is normal if you
+             * use BYE/ALSO anyway.  So fill up the cause as CC_CAUSE_OK
+             * and mimic as if transfer is done through BYE/ALSO and
+             * have GSM to disconnect and post us a disconnect local with
+             * also string
+             */
+            cc_feature_data_t data;
+
+            // Delete the previous transaction block
+            clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+
+            ccb->referto = strlib_update(ccb->referto, ccb->sip_referTo);
+            data.notify.cause = CC_CAUSE_OK;
+            data.notify.cause_code = status_code;
+            data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+            data.notify.method = CC_XFER_METHOD_BYE;
+            data.notify.blind_xferror_gsm_id = 0;
+            sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_NOTIFY,
+                           (void *) &data);
+        }
+        if (method == sipMethodInvite) {
+            sipSPISendFailureResponseAck(ccb, response, FALSE, 0);
+            if ((ccb->redirect_info != NULL) &&
+                (ccb->state == SIP_STATE_SENT_INVITE)) {
+                sip_redirect(ccb, response, status_code);
+            } else {
+                handle_error_for_state(ccb, status_code);
+            }
+        }
+
+        if (method == sipMethodNotify) {
+            clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+        }
+        free_sip_message(response);
+        break;
+
+    case SIP_CLI_ERR_REQ_PENDING:
+        if (method == sipMethodInvite) {
+            CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d Glare detected!\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                                                         ccb->index);
+            sipSPISendFailureResponseAck(ccb, response, FALSE, 0);
+            // The other side has detected a glare condition -
+            // Need to inform GSM and let it take appropriate action
+            handle_error_for_state(ccb, status_code);
+            /*
+             * The glare condition handling is now handled by GSM so do not need
+             * to set the glare timers in the stack...
+             if (ccb->flags & INCOMING) {
+             // We did not initiate this call, so set timer between 0 and 2000ms
+             sip_platform_glare_avoidance_timer_start(cpr_rand()%2000,
+             ccb->index);
+             } else {
+             // We initiated this call, so set the timer between 2100 and 4000ms
+             sip_platform_glare_avoidance_timer_start(cpr_rand()%2000+2100,
+             ccb->index);
+             }
+             */
+        }
+
+        if (method == sipMethodNotify) {
+            clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+        }
+
+        free_sip_message(response);
+        break;
+
+    default:
+        if (method == sipMethodInvite) {
+            sipSPISendFailureResponseAck(ccb, response, FALSE, 0);
+
+            if ((ccb->redirect_info != NULL)
+                && (ccb->state == SIP_STATE_SENT_INVITE)) {
+                sip_redirect(ccb, response, status_code);
+            } else {
+                handle_error_for_state(ccb, status_code);
+            }
+
+        } else if (method == sipMethodRefer) {
+            ccsipCCB_t *other_ccb;
+
+            /*
+             * The Transfer has failed.  If the call being transferred is in the
+             * "Ringing" state, send a Cancel to the Proxy to tear down the call
+             * and free up the CCB.
+             */
+            // Delete the previous transaction block
+            clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+
+            other_ccb = sip_sm_get_ccb_by_target_call_id(ccb->con_call_id);
+            if ((other_ccb != NULL) &&
+                (other_ccb->state == SIP_STATE_SENT_INVITE)) {
+                sipSPISendCancel(other_ccb);
+                sip_cc_release(other_ccb->gsm_id, other_ccb->dn_line,
+                               CC_CAUSE_NORMAL, NULL);
+                sip_sm_change_state(other_ccb, SIP_STATE_RELEASE);
+            }
+            sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, CC_FEATURE_XFER,
+                               NULL, CC_CAUSE_ERROR);
+
+        } else if (method == sipMethodNotify) {
+            clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+        } else {
+            handle_error_for_state(ccb, status_code);
+        }
+        free_sip_message(response);
+    }
+
+}
+
+
+
+/*
+ *
+ ***** SIP_STATE_SENT_INVITE_CONNECTED
+ *
+ */
+void
+ccsip_handle_sentinviteconnected_ev_cc_connected_ack (ccsipCCB_t *ccb,
+                                                      sipSMEvent_t *event)
+{
+    const char *fname = "ccsip_handle_sentinviteconnected_ev_cc_connected_ack";
+
+    /*
+     * Do not support sending out any body in the ACK.
+     * Send ACK.
+     */
+    if (sipSPISendAck(ccb, NULL) == FALSE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipSPISendAck");
+    }
+    /*
+     * Reset the number of retires we have for authentication
+     * back to 0 because at this point, if the proxy required
+     * authentication, our ACK with our credentials must have
+     * succeeded
+     */
+//    ccb->authen.retries_401_407 = 0;
+
+    sip_sm_change_state(ccb, SIP_STATE_ACTIVE);
+}
+
+
+/*
+ *
+ ***** SIP_STATE_RECV_INVITE
+ ***** SIP_STATE_RECV_INVITE_PROCEEDING
+ ***** SIP_STATE_RECV_INVITE_ALERTING
+ ***** SIP_STATE_RECV_INVITE_CONNECTED
+ *
+ */
+void
+ccsip_handle_recvinvite_ev_cc_setup_ack (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    sip_sm_change_state(ccb, SIP_STATE_RECV_INVITE);
+    ccb->dn_line = event->u.cc_msg->msg.setup_ack.line;
+}
+
+
+void
+ccsip_handle_recvinvite_ev_cc_proceeding (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "recvinvite_ev_cc_proceeding";
+
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY),
+                      ccb->index, ccb->dn_line, fname,
+                      sip_util_state2string(ccb->state),
+                      sip_util_event2string(event->type));
+
+    sip_sm_change_state(ccb, SIP_STATE_RECV_INVITE_PROCEEDING);
+}
+
+
+void
+ccsip_handle_recvinvite_ev_cc_alerting (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    sipSPISendInviteResponse180(ccb);
+    sip_sm_change_state(ccb, SIP_STATE_RECV_INVITE_ALERTING);
+}
+
+
+void
+ccsip_handle_recvinvite_ev_cc_connected (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "recvinvite_ev_cc_connected";
+    int timer_h, timer_t1 = 500;
+
+    (void) sip_platform_localexpires_timer_stop(ccb->index);
+
+    CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"SIPSM %d: connected\n",
+                                         DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname), ccb->index);
+
+    /* Send 200 OK */
+    ccsip_save_local_msg_body(ccb, &event->u.cc_msg->msg.connected.msg_body);
+    sipSPISendInviteResponse200(ccb);
+
+    // Start the INVITE Expires timer so that if we don't get an ACK we
+    // can release the call. Note that this timer is being reused.
+    config_get_value(CFGID_TIMER_T1, &timer_t1, sizeof(timer_t1));
+    timer_h = 64 * timer_t1;
+
+    if (sip_platform_expires_timer_start(timer_h, ccb->index, NULL, 0) != SIP_OK) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED), fname,
+                          "sip_platform_expires_timer_start(ACK Timer)");
+    }
+
+    sip_sm_change_state(ccb, SIP_STATE_RECV_INVITE_CONNECTED);
+}
+
+// The handler below is called when we sent a 200OK for a received INVITE
+// but never received an ACK
+void
+ccsip_handle_recvinvite_ev_expires_timer (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "recvinvite_ev_expires_timer";
+
+    CCSIP_DEBUG_STATE(DEB_F_PREFIX"Sent 200OK but received no ACK\n",
+        DEB_F_PREFIX_ARGS(SIP_ACK, fname));
+
+    // Stop the retry timer, if any
+    sip_platform_msg_timer_stop(ccb->index);
+
+    // Send a BYE to the other side and change state to release
+    // and send release complete to GSM
+    sipSPISendBye(ccb, NULL, NULL);
+    sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+    sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+}
+
+void
+ccsip_handle_recvinvite_ev_sip_ack (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "recvinvite_ev_sip_ack";
+    sipMessage_t   *request;
+    boolean         no_media = FALSE;
+    uint16_t        request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    sipsdp_status_t sdp_status;
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    /* Request check and store */
+    if (sip_sm_request_check_and_store(ccb, request, sipMethodAck, FALSE,
+                                       &request_check_reason_code,
+                                       request_check_reason_phrase, FALSE) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                          ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+        free_sip_message(request);
+        return;
+    }
+    // Stop the expires timer
+    (void) sip_platform_expires_timer_stop(ccb->index);
+
+    ccb->authen.cred_type = 0;
+
+    /*
+     * Extract SDP
+     */
+    sdp_status = sip_util_extract_sdp(ccb, request);
+
+    switch (sdp_status) {
+    case SIP_SDP_SUCCESS:
+    case SIP_SDP_SESSION_AUDIT:
+        if (ccb->oa_state != OA_OFFER_SENT) {
+            /*
+             * Received an offer SDP in an ACK.
+             */
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                              ccb->index, ccb->dn_line, fname,
+                              "Received OFFER SDP in ACK, releasing call");
+            sipSPISendBye(ccb, NULL, NULL);
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+            clean_method_request_trx(ccb, sipMethodAck, FALSE);
+            return;
+        } else {
+            ccb->oa_state = OA_IDLE;
+            CCSIP_DEBUG_STATE(DEB_F_PREFIX"Using the SDP in INVITE\n",
+                DEB_F_PREFIX_ARGS(SIP_ACK, fname));
+        }
+        break;
+
+    case SIP_SDP_DNS_FAIL:
+    case SIP_SDP_ERROR:
+        sipSPISendBye(ccb, NULL, NULL);
+        sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+        clean_method_request_trx(ccb, sipMethodAck, FALSE);
+        return;
+
+    case SIP_SDP_NO_MEDIA:
+    case SIP_SDP_NOT_PRESENT:
+    default:
+        no_media = TRUE;
+        if (ccb->oa_state == OA_OFFER_SENT) {
+            sipSPISendBye(ccb, NULL, NULL);
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+            clean_method_request_trx(ccb, sipMethodAck, FALSE);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            return;
+        } else {
+            CCSIP_DEBUG_STATE(DEB_F_PREFIX"Using the SDP in INVITE\n",
+                DEB_F_PREFIX_ARGS(SIP_ACK, fname));
+        }
+    }
+
+    /*
+     * Update connected party info from RPID and Call-Info header.
+     * Received ACK from the remote, media manipulation may occur. Defer
+     * call information update.
+     */
+    ccsip_update_callinfo(ccb, request, TRUE, TRUE, TRUE);
+
+    if (no_media) {
+        sip_cc_connected_ack(ccb->gsm_id, ccb->dn_line, NULL);
+    } else {
+        sip_cc_connected_ack(ccb->gsm_id, ccb->dn_line, request);
+    }
+
+    // Add code to send BYE the transferee
+    if (ccb->wastransferred) {
+        ccsipCCB_t *refererccb = NULL;
+        cc_feature_data_t data;
+
+        refererccb = sip_sm_get_ccb_by_callid(ccb->sipxfercallid);
+        if (NULL != refererccb) {
+            data.notify.cause = CC_CAUSE_OK;
+            data.notify.cause_code = 200;
+            data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+            data.notify.method = CC_XFER_METHOD_REFER;
+            data.notify.blind_xferror_gsm_id = 0;
+            sip_cc_feature(refererccb->gsm_id, refererccb->dn_line,
+                           CC_FEATURE_NOTIFY, (void *) &data);
+        }
+        strlib_free(ccb->sipxfercallid);
+        ccb->sipxfercallid = strlib_empty();
+    }
+
+    sip_sm_change_state(ccb, SIP_STATE_ACTIVE);
+    clean_method_request_trx(ccb, sipMethodAck, FALSE);
+    clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+}
+
+// Handle a 2xx received in this state. Could be a 202 for a REFER we sent
+// out while in this state
+void
+ccsip_handle_recvinvite_ev_sip_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "recvinvite_ev_sip_2xx";
+    sipMessage_t   *response;
+    sipMethod_t     method = sipMethodInvalid;
+    int             response_code = 0;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+
+    /* Check if this is a REFER response */
+    if (sipGetResponseCode(response, &response_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipGetResponseCode");
+        free_sip_message(response);
+        return;
+    }
+    if (sipGetResponseMethod(response, &method) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipGetResponseMethod");
+        return;
+    }
+    if (response_code == SIP_ACCEPTED && method == sipMethodRefer) {
+        ccsip_handle_accept_2xx(ccb, event);
+        return;
+    }
+    free_sip_message(response);
+    clean_method_request_trx(ccb, method, TRUE);
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED), ccb->index,
+                      ccb->dn_line, fname, sip_util_state2string(ccb->state));
+}
+
+void
+ccsip_handle_disconnect_local (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "disconnect_local";
+    char       *alsoString = NULL;
+
+
+    /*
+     * If we arrived here from the SENT_INVITE_CONNECTED state,
+     * send an ACK for the 200 we received.
+     */
+    if (ccb->state == SIP_STATE_SENT_INVITE_CONNECTED) {
+        if (sipSPISendAck(ccb, NULL) == FALSE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPISendAck");
+        }
+    }
+    // If we have sent an OK but not yet received the ACK, we should wait
+    // until it arrives before sending the BYE
+    if (ccb->state == SIP_STATE_RECV_INVITE_CONNECTED) {
+        // Stop the expires timer to receive ACK
+        (void) sip_platform_expires_timer_stop(ccb->index);
+        ccb->wait_for_ack = TRUE;
+        ccb->send_delayed_bye = TRUE;
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+        return;
+    }
+
+    if (ccb->referto[0]) {
+        alsoString = (char *) cpr_malloc(MAX_SIP_URL_LENGTH);
+        if (alsoString == NULL) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "malloc(also string)");
+        } else {
+            sstrncpy(alsoString, ccb->referto, MAX_SIP_URL_LENGTH);
+        }
+    }
+
+    /* Send BYE message */
+    ccb->authen.cred_type = 0;
+
+    sipSPISendBye(ccb, alsoString, NULL);
+    if (ccb->state == SIP_STATE_SENT_MIDCALL_INVITE) {
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_RESP_TIMEOUT, NULL);
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "MIDCALL INVITE TMR EXPIRED");
+    } else {
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+    }
+}
+
+
+void
+ccsip_handle_disconnect_media_change (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "ccsip_handle_disconnect_media_change";
+    int             code;
+    char           *phrase;
+    char           *alsoString = NULL;
+    cc_causes_t     cause = CC_CAUSE_NORMAL;
+
+    cause = event->u.cc_msg->msg.release.cause;
+    if (cause == CC_CAUSE_NO_MEDIA || cause == CC_CAUSE_PAYLOAD_MISMATCH) {
+        /*
+         * Send Invite Response with correct cause message
+         */
+        code = ccsip_cc_to_sip_cause(cause, &phrase);
+        if (ccb->state == SIP_STATE_RECV_MIDCALL_INVITE_CCFEATUREACK_PENDING) {
+            sipSPISendInviteResponse(ccb, (uint16_t) code, phrase, 0, NULL,
+                                     FALSE /* no SDP */, TRUE /* reTx */);
+            ccb->wait_for_ack = TRUE;
+            ccb->send_delayed_bye = TRUE;
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+            return;
+        } else {
+            (void) sipSPISendUpdateResponse(ccb, FALSE, cause, FALSE);
+        }
+    }
+
+    if (ccb->referto[0]) {
+        alsoString = (char *) cpr_malloc(MAX_SIP_URL_LENGTH);
+        if (alsoString == NULL) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname, "malloc(also string)");
+        } else {
+            sstrncpy(alsoString, ccb->referto, MAX_SIP_URL_LENGTH);
+        }
+    }
+
+    /* Send BYE message */
+    ccb->authen.cred_type = 0;
+    sipSPISendBye(ccb, alsoString, NULL);
+    sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+    sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+}
+
+void
+ccsip_handle_disconnect_local_early (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "disconnect_local_early";
+
+    /*
+     * Note that event.type should always be checked prior to dereferencing. Failure
+     * to check the event type can cause non-deterministic behavior when dereferencing
+     * the event structure.
+     */
+
+    /*
+     * Check for special case early attended transfer release.
+     */
+    if (event->type == E_CC_RELEASE) {
+        if (event->u.cc_msg->msg.release.cause == CC_CAUSE_NO_USER_RESP) {
+            ccb->early_transfer = TRUE;
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                              ccb->index, ccb->dn_line, fname,
+                              "No action, Early Attended Transfer");
+            return;
+        }
+    }
+
+    /* Send CANCEL message only if we have recd at least a 1xx message in
+     * response to our INVITE. If not, we will mark the CCB and send it only
+     * when a 1xx is received - or if not - not at all
+     */
+    if (ccb->flags & RECD_1xx) {
+        sipSPISendCancel(ccb);
+    } else {
+        // Defer the CANCEL - Note we'll move to the RELEASE state even if we
+        // defer the CANCEL
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Received CC disconnect without 1xx from far side\n",
+            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                          ccb->index, ccb->dn_line, fname,
+                          "Stopping retx timer");
+        sip_platform_msg_timer_stop(ccb->index);
+        ccb->flags |= SEND_CANCEL;
+    }
+
+    if (ccb->blindtransferred == TRUE) {
+        cc_feature_data_t data;
+
+        data.notify.cause = CC_CAUSE_OK;
+        data.notify.cause_code = SIP_CLI_ERR_REQ_CANCEL;
+        data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+        data.notify.method = CC_XFER_METHOD_REFER;
+        data.notify.blind_xferror_gsm_id = sip_sm_get_blind_xfereror_ccb_by_gsm_id(ccb->gsm_id);
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_NOTIFY, (void *)&data);
+    } else if (ccb->wastransferred) {
+        /* The call was a transfer and we are ending it before
+         * we connect. GSM has already cleaned up the transfer
+         * data blocks for this when it received the END_CALL
+         * message from UI. We still need to send the NOTIFY
+         * message to the transferor, so we will send it from
+         * here.
+         */
+        ccsipCCB_t *referccb = NULL;
+
+        referccb = sip_sm_get_target_call_by_gsm_id(ccb->gsm_id);
+        if (referccb != NULL) {
+            ccb->flags |= FINAL_NOTIFY;
+            (void) sipSPISendNotify(referccb, SIP_CLI_ERR_REQ_CANCEL);
+            ccb->xfer_status = SIP_CLI_ERR_REQ_CANCEL;
+        }
+    }
+
+    if (event->type == E_SIP_INV_EXPIRES_TIMER) {
+        sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+    } else {
+        sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+    }
+
+    sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+}
+
+void
+ccsip_handle_localexpires_timer (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    /* Send 486 error */
+    sipSPISendInviteResponse(ccb, SIP_CLI_ERR_BUSY_HERE,
+                             SIP_CLI_ERR_BUSY_HERE_PHRASE,
+                             0, NULL, FALSE, /* no SDP */ TRUE /* reTx */);
+
+    /* tell GSM to cleanup */
+    sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL, NULL);
+
+    sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+}
+
+static void
+ccsip_cc_to_warning (cc_causes_t cause, char **warning_phrase,
+                     uint16_t *warning_code)
+{
+    switch (cause) {
+
+    case CC_CAUSE_PAYLOAD_MISMATCH:
+        *warning_phrase = SIP_WARN_INCOMPAT_MEDIA_FORMAT_PHRASE;
+        *warning_code = SIP_WARN_INCOMPAT_MEDIA_FORMAT;
+        break;
+
+    case CC_CAUSE_NO_MEDIA:
+        *warning_phrase = SIP_WARN_MEDIA_TYPE_UNAVAIL_PHRASE;
+        *warning_code = SIP_WARN_MEDIA_TYPE_UNAVAIL;
+        break;
+
+    default:
+        *warning_phrase = NULL;
+        *warning_code = 0;
+    }
+
+}
+
+
+int
+ccsip_cc_to_sip_cause (cc_causes_t cause, char **phrase)
+{
+    switch (cause) {
+
+    case CC_CAUSE_OK:
+        *phrase = SIP_SUCCESS_SETUP_PHRASE;
+        return SIP_SUCCESS_SETUP;
+
+    case CC_CAUSE_ERROR:
+        *phrase = SIP_CLI_ERR_BAD_REQ_PHRASE;
+        return SIP_CLI_ERR_BAD_REQ;
+
+    case CC_CAUSE_UNASSIGNED_NUM:
+        *phrase = SIP_CLI_ERR_NOT_FOUND_PHRASE;
+        return SIP_CLI_ERR_NOT_FOUND;
+
+    case CC_CAUSE_NO_RESOURCE:
+        *phrase = SIP_SERV_ERR_INTERNAL_PHRASE;
+        return SIP_SERV_ERR_INTERNAL;
+
+    case CC_CAUSE_BUSY:
+        *phrase = SIP_CLI_ERR_BUSY_HERE_PHRASE;
+        return SIP_CLI_ERR_BUSY_HERE;
+
+    case CC_CAUSE_ANONYMOUS:
+        *phrase = SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED_PHRASE;
+        return SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED;
+
+    case CC_CAUSE_INVALID_NUMBER:
+        *phrase = SIP_CLI_ERR_ADDRESS_PHRASE;
+        return SIP_CLI_ERR_ADDRESS;
+
+    case CC_CAUSE_PAYLOAD_MISMATCH:
+        *phrase = SIP_CLI_ERR_NOT_ACCEPT_HERE_PHRASE;
+        return SIP_CLI_ERR_NOT_ACCEPT_HERE;
+
+    case CC_CAUSE_NO_REPLACE_CALL:
+        *phrase = SIP_CLI_ERR_CALLEG_PHRASE;
+        return SIP_CLI_ERR_CALLEG;
+
+    case CC_CAUSE_REQUEST_PENDING:
+        *phrase = SIP_CLI_ERR_REQ_PENDING_PHRASE;
+        return SIP_CLI_ERR_REQ_PENDING;
+
+    default:
+        *phrase = SIP_CLI_ERR_BAD_REQ_PHRASE;
+        return SIP_CLI_ERR_BAD_REQ;
+    }
+}
+
+void
+ccsip_handle_disconnect_local_unanswered (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    int             code;
+    char           *phrase;
+    uint16_t        warning_code = 0;
+    char           *warning_phrase = NULL;
+
+    ccb->dn_line = event->u.cc_msg->msg.release.line;
+    code = ccsip_cc_to_sip_cause(event->u.cc_msg->msg.release.cause, &phrase);
+
+    ccsip_cc_to_warning(event->u.cc_msg->msg.release.cause, &warning_phrase, &warning_code);
+
+    /* Send Invite Response with correct cause message */
+    sipSPISendInviteResponse(ccb, (uint16_t)code, phrase, warning_code,
+                             warning_phrase,
+                             FALSE /*no SDP */ , TRUE /*reTx */ );
+
+    sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+    ccb->wait_for_ack = TRUE;
+    sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+}
+
+
+void
+ccsip_handle_disconnect_remote (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "disconnect_remote";
+    sipMessage_t   *request;
+    sipMessage_t   *store_invite_req;
+    const char     *alsoString = NULL, *reasonHdr=NULL;
+    uint16_t        request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    sipMethod_t     method = sipMethodInvalid;
+    cc_causes_t      cause = CC_CAUSE_NORMAL;
+
+    memset(request_check_reason_phrase, 0, SIP_WARNING_LENGTH);
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+    store_invite_req = ccb->last_request;
+
+    sipGetRequestMethod(request, &method);
+
+    /* Request check and store */
+    if (sip_sm_request_check_and_store(ccb, request, method, TRUE,
+                                       &request_check_reason_code,
+                                       request_check_reason_phrase, TRUE) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       request_check_reason_code,
+                                       request_check_reason_phrase, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+    // Stop the expires timer
+    (void) sip_platform_expires_timer_stop(ccb->index);
+
+    //Check if this BYE is for the original call (between
+    //transferor and transferee) in attended transfer scenario.
+    if (ccb->con_call_id != CC_NO_CALL_ID) {
+        //Transferee has received a BYE
+        //from the transferor. Two scenarios are possible:
+        //1. BYE is received after receiving the 2xx/4xx/5xx/6xx for the
+        //        consultative call. Therefore, setting wasTransferred flag
+        //        to FALSE has no impact.
+        //2. BYE is received before receiving the 2xx/4xx/5xx/6xx for the
+        //        consultative call.
+        //        Therefore, setting wastransferred flag
+        //        to FALSE, results in not sending the final NOTIFY
+        //        after receiving 2xx/4xx/5xx/6xx from the target.
+        ccsipCCB_t *other_ccb;
+
+        other_ccb = sip_sm_get_ccb_by_target_call_id(ccb->con_call_id);
+        if (other_ccb) {
+            other_ccb->wastransferred = FALSE;
+            strlib_free(other_ccb->sipxfercallid);
+            other_ccb->sipxfercallid = strlib_empty();
+        }
+    }
+
+
+    /*
+     * Detect whether this is a Call Transfer by checking
+     * for the presence of the "Also:" header
+     */
+    alsoString = sippmh_get_header_val(request, SIP_HEADER_ALSO, NULL);
+    if (alsoString) {
+        cc_feature_data_t data;
+
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d Far end requested Call Transfer, "
+                          "destination=<%s>\n",
+                                                 DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                                                 ccb->index, alsoString);
+        sstrncpy(data.xfer.dialstring, alsoString, strlen(alsoString) + 1);
+        data.xfer.cause  = CC_CAUSE_XFER_REMOTE;
+        data.xfer.method = CC_XFER_METHOD_BYE;
+        data.xfer.target_call_id = CC_NO_CALL_ID;
+        ccb->referto = strlib_update(ccb->referto, alsoString);
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_XFER, (void *)&data);
+        (void) sipSPISendByeOrCancelResponse(ccb, request, sipMethodBye);
+        free_sip_message(store_invite_req);
+        return;
+    } else {
+        /* Send BYE response message */
+        if (event->type == E_SIP_BYE) {
+            /*
+             * We want to wait for release_complete from GSM before sending
+             * the 200 OK out, in order to collect call_stats
+             */
+            ccb->flags |= RECD_BYE;
+        } else {
+            (void) sipSPISendByeOrCancelResponse(ccb, request, sipMethodCancel);
+            // if the reason header contains a reason code of 200
+            // we should pass this forward to gsm
+            reasonHdr = sippmh_get_header_val(request, SIP_HEADER_REASON, NULL);
+            if ( reasonHdr && strcasestr(reasonHdr, "cause=200;") ) {
+               cause = CC_SIP_CAUSE_ANSWERED_ELSEWHERE;
+            }
+        }
+
+        if (SIP_SM_CALL_SETUP_RESPONDING(ccb) ||
+            ccb->state == SIP_STATE_RECV_INVITE ||
+            ccb->state == SIP_STATE_RECV_INVITE_CONNECTED) {
+            /*
+             * Store invite request into ccb before sending 487. This is a
+             * temporary solution and should be removed in Moonpie
+             */
+            sipGetRequestMethod(store_invite_req, &method);
+            if (sip_sm_request_check_and_store(ccb, store_invite_req, method,
+                                               TRUE, &request_check_reason_code,
+                                               request_check_reason_phrase, FALSE) < 0) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                                  ccb->dn_line, fname,
+                                  get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+                free_sip_message(store_invite_req);
+                return;
+            }
+            sipSPISendInviteResponse(ccb, 487, SIP_CLI_ERR_REQ_CANCEL_PHRASE, 0,
+                                     NULL, FALSE, /* no SDP */ TRUE /* reTx */);
+            ccb->wait_for_ack = TRUE;
+        } else {
+            free_sip_message(store_invite_req);
+        }
+
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        sip_cc_release(ccb->gsm_id, ccb->dn_line, cause, NULL);
+    }
+}
+
+/* E_SIP_REFER                    */
+void
+ccsip_handle_refer_sip_message (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "refer_sip_message";
+    sipMessage_t   *request;
+    const char     *contact = NULL;
+    uint16_t        request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    sipMethod_t     method = sipMethodInvalid;
+    cc_feature_data_t data;
+    char           *referToString[MAX_REFER_TO_HEADERS] = { NULL };
+    char           *referByString;
+    sipReferTo_t   *referto = NULL;
+    sipReplaces_t  *replaces_t = NULL;
+    int             noOfReferTo = 0;
+    int             noOfReferBy = 0;
+    char            tempreferto[MAX_SIP_URL_LENGTH];
+    int             rpid_flag = RPID_DISABLED;
+    char            tempreferby[MAX_SIP_URL_LENGTH];
+    char           *semi_token = NULL;
+    int             rcode;
+    sipContact_t   *contact_info = NULL;
+
+    memset(tempreferto, 0, MAX_SIP_URL_LENGTH);
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    /* transfer method if attended or blind */
+    ccb->featuretype = CC_FEATURE_BLIND_XFER;  // Default is Blind
+
+    sipGetRequestMethod(request, &method);
+
+    // Check if we are already processing a previously received REFER
+    if (get_method_request_trx_index(ccb, method, FALSE) > -1) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received REFER while processing an old one!\n", fname);
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_REQ_PENDING,
+                                       SIP_CLI_ERR_REQ_PENDING_PHRASE,
+                                       0, NULL, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+
+    /* Request check and store */
+    rcode = sip_sm_request_check_and_store(ccb, request, method, TRUE,
+                                           &request_check_reason_code,
+                                           request_check_reason_phrase, FALSE);
+    if (rcode < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+
+        if (rcode == -2) { // trx malloc error
+            (void) sipSPISendErrorResponse(request, SIP_SERV_ERR_INTERNAL,
+                                           SIP_SERV_ERR_INTERNAL_PHRASE,
+                                           request_check_reason_code,
+                                           request_check_reason_phrase, NULL);
+        } else {
+            (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                           SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                           request_check_reason_code,
+                                           request_check_reason_phrase, NULL);
+        }
+        free_sip_message(request);
+        return;
+    }
+    contact = sippmh_get_cached_header_val(request, CONTACT);
+    if (contact) {
+        if (sipSPICheckContact(contact) < 0) { // If contact is invalid
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "sipSPICheckContact()");
+            (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                           SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                           SIP_WARN_MISC,
+                                           SIP_CLI_ERR_BAD_REQ_CONTACT_FIELD,
+                                           ccb);
+            return;
+        }
+        contact_info = sippmh_parse_contact(contact);
+        if (contact_info && contact_info->num_locations > 1) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received REFER with multiple contacts!\n", fname);
+            (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_AMBIGUOUS,
+                                           SIP_CLI_ERR_AMBIGUOUS_PHRASE,
+                                           SIP_WARN_MISC,
+                                           SIP_WARN_REFER_AMBIGUOUS_PHRASE,
+                                           ccb);
+            sippmh_free_contact(contact_info);
+            return;
+        }
+        if (contact_info) {
+            sippmh_free_contact(contact_info);
+        }
+    } else { // If No contact header
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       SIP_CLI_ERR_BAD_REQ_CONTACT_FIELD,
+                                       ccb);
+        return;
+    }
+
+    /*
+     * Update connected party info from RPID and Call-Info header.
+     * Handing REFER, do not defer call information update.
+     */
+    ccsip_update_callinfo(ccb, request, TRUE, TRUE, FALSE);
+
+    // Store Refer Information such as Call_id and to-tag
+    // If replace is present then it must be attended transfer
+    noOfReferTo = sippmh_get_num_particular_headers(request, SIP_HEADER_REFER_TO,
+                                                    SIP_C_HEADER_REFER_TO,
+                                                    referToString, MAX_REFER_TO_HEADERS);
+
+    if (noOfReferTo == 1) {
+        referto = sippmh_parse_refer_to(referToString[0]);
+    }
+
+    if ((noOfReferTo == 0) || (noOfReferTo > 1) || (referto == NULL)) {
+
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                          ccb->dn_line, fname,
+                          "Incorrect number of Refer-To headers and/or value.\n");
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE_REFER_TO,
+                                       ccb);
+
+        if (referto) {
+            cpr_free(referto);
+        }
+        return;
+    }
+
+    sipSPIGenerateTargetUrl(referto->targetUrl, tempreferto);
+    ccb->sip_referTo = strlib_update(ccb->sip_referTo, tempreferto);
+    if (referto->sip_replaces_hdr != NULL) {
+        char tempreplace[MAX_SIP_URL_LENGTH];
+
+        memset(tempreplace, 0, MAX_SIP_URL_LENGTH);
+        sstrncpy(tempreplace, "Replaces=", sizeof(tempreplace));
+        sstrncat(tempreplace, referto->sip_replaces_hdr, (sizeof(tempreplace) - sizeof("Replaces=")));
+        replaces_t = sippmh_parse_replaces(tempreplace, FALSE);
+        if (NULL != replaces_t) {
+            ccb->sipxfercallid = strlib_update(ccb->sipxfercallid, replaces_t->callid);
+            if (ccb->sipxfercallid) {
+                ccb->sipxfercallid = strlib_append(ccb->sipxfercallid, ";to-tag=");
+                if (ccb->sipxfercallid) {
+                    ccb->sipxfercallid = strlib_append(ccb->sipxfercallid, replaces_t->toTag);
+                }
+                if (ccb->sipxfercallid) {
+                    ccb->sipxfercallid = strlib_append(ccb->sipxfercallid, ";from-tag=");
+                }
+                if (ccb->sipxfercallid) {
+                    ccb->sipxfercallid = strlib_append(ccb->sipxfercallid, replaces_t->fromTag);
+                }
+            }
+            ccb->featuretype = CC_FEATURE_XFER;
+            sippmh_free_replaces(replaces_t);
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                              ccb->dn_line, fname,
+                              "replaces header in referto missing callid or to/from tags");
+            (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                           SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                           SIP_WARN_MISC,
+                                           SIP_CLI_ERR_BAD_REQ_PHRASE_REFER_TO,
+                                           ccb);
+            sippmh_free_refer_to(referto);
+            return;
+        }
+    }
+#ifdef SIP_ACC_CONT
+    if (referto->sip_acc_cont != NULL) {
+        // attach the new string to the ccb...
+        // Note: This doesn't currently generate any header, but the hook
+        // is here if we need it later.
+        if (ccb->refer_acc_cont != NULL) {
+            cpr_free(ccb->refer_acc_cont);
+            ccb->refer_acc_cont = NULL;
+        }
+        ccb->refer_acc_cont = cpr_strdup(referto->sip_acc_cont);
+    }
+#endif
+
+    if (referto->sip_proxy_auth != NULL) {
+        if (ccb->refer_proxy_auth != NULL) {
+            // if there was already one of these, toss it out
+            cpr_free(ccb->refer_proxy_auth);
+            ccb->refer_proxy_auth = NULL;
+        }
+        // attach the new string to the ccb...
+        ccb->refer_proxy_auth = cpr_strdup(referto->sip_proxy_auth);
+    }
+
+    noOfReferBy = sippmh_get_num_particular_headers(request,
+                                                    SIP_HEADER_REFERRED_BY,
+                                                    SIP_C_HEADER_REFERRED_BY,
+                                                    &referByString,
+                                                    MAX_REFER_BY_HEADERS);
+
+    if (noOfReferBy == 0) {
+
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          "Missing referred by header\n");
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE_REFER_BY,
+                                       ccb);
+        sippmh_free_refer_to(referto);
+        return;
+    }
+
+    if (noOfReferTo > 1 || noOfReferBy > 1) {
+
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          "Multiple referto or refer by headers found in message");
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE_REFER,
+                                       ccb);
+        sippmh_free_refer_to(referto);
+        return;
+    }
+
+    config_get_value(CFGID_REMOTE_PARTY_ID, &rpid_flag, sizeof(rpid_flag));
+
+    if ((rpid_flag == RPID_ENABLED) && (ccb->best_rpid)
+        && (cpr_strcasecmp(ccb->best_rpid->privacy, PRIVACY_OFF) != 0)) {
+        snprintf(tempreferby, MAX_SIP_URL_LENGTH, "\"%s\" <sip:%s@%s>",
+                 SIP_HEADER_ANONYMOUS_STR,
+                 ccb->best_rpid->loc->genUrl->u.sipUrl->user,
+                 ccb->best_rpid->loc->genUrl->u.sipUrl->host);
+        ccb->sip_referredBy = strlib_update(ccb->sip_referredBy, tempreferby);
+    } else {
+        ccb->sip_referredBy = strlib_update(ccb->sip_referredBy, referByString);
+    }
+
+    sstrncpy(data.xfer.dialstring, referToString[0], sizeof(data.xfer.dialstring));
+    semi_token = strchr(data.xfer.dialstring, '?');
+    if (semi_token) {
+        *semi_token++ = '>';
+        *semi_token = 0;
+    }
+    if (data.xfer.dialstring[0] != '<') {
+        semi_token = strchr(data.xfer.dialstring, '>');
+        if (semi_token) {
+            *semi_token = 0;
+        }
+    }
+
+    /* Convert the escapeed userino part, if any to unescaped format */
+    if (unescape_UserInfo(data.xfer.dialstring, tempreferto, MAX_SIP_URL_LENGTH)) {
+        memset(data.xfer.dialstring, 0, CC_MAX_DIALSTRING_LEN);
+        sstrncpy(data.xfer.dialstring, tempreferto, CC_MAX_DIALSTRING_LEN);
+    }
+
+    data.xfer.method = CC_XFER_METHOD_REFER;
+    data.xfer.target_call_id = CC_NO_CALL_ID;
+    data.xfer.cause = CC_CAUSE_XFER_REMOTE;
+    if (ccb->featuretype == CC_FEATURE_BLIND_XFER) {
+        sstrncpy(data.xfer.referred_by, ccb->sip_referredBy,
+                 MAX_SIP_URL_LENGTH);
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_BLIND_XFER,
+                       (void *) &data);
+    } else {
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_XFER,
+                       (void *) &data);
+    }
+    sippmh_free_refer_to(referto);
+}
+
+void
+ccsip_handle_process_in_call_options_request (ccsipCCB_t *ccb,
+                                              sipSMEvent_t *event)
+{
+    const char     *fname = "ccsip_handle_process_in_call_options_request";
+    uint16_t        request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    sipMessage_t   *request;
+    sipMethod_t     method = sipMethodInvalid;
+
+    CCSIP_DEBUG_STATE(DEB_F_PREFIX"Processing within-dialog OPTIONS request\n",
+        DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    ccb->featuretype = CC_FEATURE_NONE;
+
+    /* Request check and store */
+    sipGetRequestMethod(request, &method);
+    if (sip_sm_request_check_and_store(ccb, request, method, TRUE,
+                                       &request_check_reason_code,
+                                       request_check_reason_phrase, FALSE) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       request_check_reason_code,
+                                       request_check_reason_phrase, NULL);
+        free_sip_message(request);
+        return;
+    }
+    // fixed CSCsi68191: The ccb->last_request and event->u.pSipMessage pointed to same memory address.
+    // Set last_request to NULL to avoid double deletion of memory, which is freed by
+    // gsm event of OPTION ACK later.
+    ccb->last_request = NULL;
+    sip_cc_options(ccb->gsm_id, ccb->dn_line, event->u.pSipMessage);
+}
+
+void
+ccsip_handle_ev_cc_answer_options_request (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "ccsip_handle_ev_cc_answer_options_request";
+    cc_options_sdp_ack_t *options_sdp_ack;
+
+    options_sdp_ack = (cc_options_sdp_ack_t *) event->u.cc_msg;
+
+    if (!ccb) {
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Processing OPTIONS (out of dialog) "
+                          "request(GSM has responded)\n", DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+        (void) sipSPIsendNonActiveOptionResponse((sipMessage_t *) options_sdp_ack->pMessage,
+                                                 &options_sdp_ack->msg_body);
+
+        //since there is no call and therefore no ccb, we must free
+        //the SIP OPTIONS message here.
+        free_sip_message((sipMessage_t *)options_sdp_ack->pMessage);
+        options_sdp_ack->pMessage = NULL;
+    } else {
+        //Since there is a ccb, the SIP OPTIONS message will be freed
+        //in sip_sm_call_cleanup() after the call is done.
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Processing OPTIONS (in dialog) "
+                          "request(GSM has responded)\n", DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+
+        // Must save sdp from gsm to ccb before building the response
+        ccsip_save_local_msg_body(ccb, &options_sdp_ack->msg_body);
+        (void) sipSPISendOptionResponse(ccb, event->u.pSipMessage);
+    }
+}
+
+void
+ccsip_handle_ev_cc_answer_audit_request (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    cc_audit_sdp_ack_t *audit_ack;
+
+
+    audit_ack = (cc_audit_sdp_ack_t *) event->u.cc_msg;
+    /*
+     * Use the new sdp generated by the gsm and free the old copy.
+     */
+    ccsip_save_local_msg_body(ccb, &audit_ack->msg_body);
+    sipSPISendInviteResponse200(ccb);
+}
+
+int
+sip_sm_process_event (sipSMEvent_t *pEvent)
+{
+    const char     *fname = "sip_sm_process_event";
+    ccsipCCB_t     *ccb;
+    sipSMAction_t   event_handler = H_INVALID_EVENT;
+
+    ccb = pEvent->ccb;
+    if (!ccb) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"ccb is null. Unable to process event "
+                          "<%d>\n", fname, pEvent->type);
+        return (-1);
+    }
+
+    /* Unbind UDP ICMP response handler */
+    UNBIND_UDP_ICMP_HANDLER(ccb->udpId);
+
+    /*
+     * ccb->index is an unsigned type and TEL_CCB_START is zero,
+     * so just check the end condition
+     */
+    if ((int) (ccb->index) > TEL_CCB_END) {
+        // We got an illegal line for our SIP SM
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"illegal line number: %d\n",
+                          fname, ccb->index);
+        return (-1);
+    }
+
+    if (!sip_config_check_line(ccb->dn_line)) {
+        // We got an illegal DN for our SIP SM
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"illegal directory number: %d\n",
+                          fname, ccb->dn_line);
+        return (-1);
+    }
+
+    if ((event_handler = get_handler_index(ccb->state, pEvent->type))
+            != H_INVALID_EVENT) {
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Processing SM event: %d: --0x%08lx--%21s: %s <- %s\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->dn_line, ccb->gsm_id, fname),
+                                                 ccb->index, EVENT_ACTION_SM(event_handler),
+                          "", sip_util_state2string(ccb->state),
+                          sip_util_event2string(pEvent->type));
+
+        EVENT_ACTION_SM(event_handler) (ccb, pEvent);
+    } else {
+        /* Invalid State/Event pair */
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"illegal state/event pair: "
+                          "(%d <-- %d)\n", fname, ccb->state, pEvent->type);
+        return (-1);
+    }
+
+    return (0);
+}
+
+
+/**
+ *
+ * Extract the k factor stats sent by GSM and store a ptr on ccb
+ *
+ * @param pCCMsg - msg from GSM
+ * @param ccb - ccb for this call
+ *
+ * @return  nothing
+ */
+void
+sip_extract_kfactor_stats (cc_msg_t *pCCMsg, ccsipCCB_t *ccb)
+{
+    if (pCCMsg->msg.release.msg_id == CC_MSG_RELEASE) {
+        ccb->kfactor_ptr = &pCCMsg->msg.release.kfactor;
+    } else if (pCCMsg->msg.release_complete.msg_id == CC_MSG_RELEASE_COMPLETE) {
+        ccb->kfactor_ptr = &pCCMsg->msg.release_complete.kfactor;
+    } else if ((pCCMsg->msg.feature.msg_id == CC_MSG_FEATURE) ||
+               (pCCMsg->msg.feature.msg_id == CC_MSG_FEATURE_ACK)) {
+        if (pCCMsg->msg.feature.feature_id == CC_FEATURE_HOLD) {
+            ccb->kfactor_ptr = &pCCMsg->msg.feature.data.hold.kfactor;
+        } else if (pCCMsg->msg.feature.feature_id == CC_FEATURE_MEDIA) {
+            ccb->kfactor_ptr = &pCCMsg->msg.feature.data.resume.kfactor;
+        } else {
+            ccb->kfactor_ptr = NULL;
+        }
+    } else {
+        ccb->kfactor_ptr = NULL;
+    }
+}
+int
+sip_sm_process_cc_event (cprBuffer_t buf)
+{
+    const char     *fname = "sip_sm_process_cc_event";
+    sipSMEvent_t    sip_sm_event;
+    line_t          idx = 0;
+    sipSMAction_t   event_handler = H_INVALID_EVENT;
+    cc_msg_t       *pCCMsg = (cc_msg_t *) buf;
+
+    memset(&sip_sm_event, 0, sizeof(sipSMEvent_t));
+
+    sip_sm_event.u.cc_msg = pCCMsg;
+    sip_sm_event.type = sip_util_ccevent2sipccevent(pCCMsg->msg.setup.msg_id);
+    if (pCCMsg->msg.setup.msg_id == CC_MSG_SETUP) {
+        sip_sm_event.ccb = sip_sm_get_ccb_next_available(&idx);
+        if (!sip_sm_event.ccb) {
+            sip_cc_release(pCCMsg->msg.setup.call_id, pCCMsg->msg.setup.line,
+                           CC_CAUSE_ERROR, NULL);
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"No free lines available\n",
+                DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+            cc_free_msg_data(sip_sm_event.u.cc_msg);
+            cpr_free(pCCMsg);
+            return SIP_OK;
+        }
+    } else if (pCCMsg->msg.setup.msg_id == CC_MSG_OPTIONS_ACK) {
+        /*
+         * Process the out of dialogue and in dialogue option ack
+         * (local sdp built by the gsm)
+         */
+        sip_sm_event.ccb = sip_sm_get_ccb_by_gsm_id(pCCMsg->msg.setup.call_id);
+        ccsip_handle_ev_cc_answer_options_request(sip_sm_event.ccb, &sip_sm_event);
+        cc_free_msg_data(sip_sm_event.u.cc_msg);
+        cpr_free(pCCMsg);
+        return (SIP_OK);
+    } else if (ccsip_handle_cc_select_event(&sip_sm_event)) {
+        /*
+         * The select feature has been handled.
+         */
+        cc_free_msg_data(sip_sm_event.u.cc_msg);
+        cpr_free(pCCMsg);
+        return SIP_OK;
+    } else if (ccsip_handle_cc_b2bjoin_event(&sip_sm_event)) {
+        /*
+         * The b2bjoin feature has been handled.
+         */
+        cc_free_msg_data(sip_sm_event.u.cc_msg);
+        cpr_free(pCCMsg);
+        return SIP_OK;
+    } else if (ccsip_handle_cc_hook_event(&sip_sm_event) == TRUE) {
+        /*
+         * The hook event has been handled.
+         */
+        cc_free_msg_data(sip_sm_event.u.cc_msg);
+        cpr_free(pCCMsg);
+        return SIP_OK;
+    } else {
+        sip_sm_event.ccb = sip_sm_get_ccb_by_gsm_id(pCCMsg->msg.setup.call_id);
+        if (!sip_sm_event.ccb) {
+            // If there is no associated CCB, this could be an
+            // Out of Dialog feature request
+            if (pCCMsg->msg.setup.msg_id == CC_MSG_FEATURE) {
+                // Call function to process an OOD Feature
+                // OOD features are handled by the sub/not API
+                // If unsuccessful processing
+                sip_cc_release(pCCMsg->msg.setup.call_id, pCCMsg->msg.setup.line,
+                               CC_CAUSE_ERROR, NULL);
+            } else if (pCCMsg->msg.setup.msg_id == CC_MSG_RELEASE) {
+                sip_cc_release_complete(pCCMsg->msg.setup.call_id,
+                                        pCCMsg->msg.setup.line, CC_CAUSE_ERROR);
+            } else {
+                sip_cc_release(pCCMsg->msg.setup.call_id, pCCMsg->msg.setup.line,
+                               CC_CAUSE_ERROR, NULL);
+            }
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"No ccb with matching gsm_id = <%d>\n",
+                DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname),
+                             pCCMsg->msg.setup.call_id);
+            cc_free_msg_data(sip_sm_event.u.cc_msg);
+            cpr_free(pCCMsg);
+            return SIP_OK;
+        }
+    }
+
+    if (pCCMsg->msg.setup.msg_id == CC_MSG_AUDIT_ACK) {
+        /*
+         * Process the in dialogue audit ack
+         * (local sdp built by the gsm)
+         */
+        ccsip_handle_ev_cc_answer_audit_request(sip_sm_event.ccb, &sip_sm_event);
+        cc_free_msg_data(sip_sm_event.u.cc_msg);
+        cpr_free(pCCMsg);
+        return (SIP_OK);
+    }
+
+    /* CSCds88133: ReTx timers cancelled prematurely */
+    if (sip_sm_event.ccb->state == SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING) {
+        /* RAC FIXME - Don't like this... */
+        line_t line_index = (line_t) -1;
+
+        sip_sm_event.ccb = sip_sm_get_ccb_next_available(&line_index);
+        if (!sip_sm_event.ccb) {
+            if (pCCMsg->msg.setup.msg_id == CC_MSG_RELEASE) {
+                sip_cc_release_complete(pCCMsg->msg.setup.call_id,
+                                        pCCMsg->msg.setup.line, CC_CAUSE_ERROR);
+            } else {
+                sip_cc_release(pCCMsg->msg.setup.call_id, pCCMsg->msg.setup.line,
+                               CC_CAUSE_ERROR, NULL);
+            }
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_sm_get_ccb_next_available()"
+                              " returned null.\n", fname);
+            cc_free_msg_data(sip_sm_event.u.cc_msg);
+            cpr_free(pCCMsg);
+            return SIP_OK;
+        }
+        /* Set this ccb to always be on the first DN. */
+        sip_sm_event.ccb->dn_line = 1;
+    }
+
+    sip_extract_kfactor_stats(pCCMsg, sip_sm_event.ccb);
+    event_handler = get_handler_index(sip_sm_event.ccb->state, sip_sm_event.type);
+    if (event_handler != H_INVALID_EVENT) {
+
+        DEF_DEBUG(DEB_L_C_F_PREFIX"Processing CC event: %-6d: SM: %s <- %s\n",
+                        DEB_L_C_F_PREFIX_ARGS(SIP_EVT, sip_sm_event.ccb->dn_line,
+                        sip_sm_event.ccb->gsm_id, fname),
+                        (long)buf,
+                        sip_util_state2string(sip_sm_event.ccb->state),
+                        sip_util_event2string(sip_sm_event.type));
+
+        EVENT_ACTION_SM(event_handler)(sip_sm_event.ccb, &sip_sm_event);
+    } else {
+        /* Invalid State/Event pair */
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"illegal state/event pair: (%d <-- %d)\n",
+                          fname, sip_sm_event.ccb->state, sip_sm_event.type);
+        cc_free_msg_data(sip_sm_event.u.cc_msg);
+        cpr_free(pCCMsg);
+        return SIP_ERROR;
+    }
+    sip_sm_event.ccb->kfactor_ptr = NULL;
+    cc_free_msg_data(sip_sm_event.u.cc_msg);
+    cpr_free(pCCMsg);
+    return SIP_OK;
+}
+
+void
+ccsip_handle_send_blind_notify (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "send_blind_notify";
+    cc_features_t   feature_type;
+
+    feature_type = event->u.cc_msg->msg.feature.feature_id;
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY),
+                      ccb->index, ccb->dn_line, fname,
+                      sip_util_state2string(ccb->state),
+                      cc_feature_name(feature_type));
+
+    if (CC_FEATURE_NOTIFY == feature_type) {
+        if (event->u.cc_msg->msg.feature.data.notify.final == TRUE) {
+            ccb->flags |= FINAL_NOTIFY;
+        }
+        (void) sipSPISendNotify(ccb, event->u.cc_msg->msg.feature.data.notify.cause_code);
+        ccb->xfer_status = event->u.cc_msg->msg.feature.data.notify.cause_code;
+    } else {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FEATURE_UNSUPPORTED),
+                          ccb->index, ccb->dn_line, fname);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type, NULL,
+                           CC_CAUSE_ERROR);
+    }
+}
+
+void
+sip_sm_call_cleanup (ccsipCCB_t *ccb)
+{
+    const char     *fname = "sip_sm_call_cleanup";
+    int             i = 0;
+
+    if (ccb == NULL) {
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Null CCB passed into function.\n",
+            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+        return;
+    }
+
+    /* first time the phone boots, this function gets called, but the
+     * CCBs haven't been initialized yet. This is the only time the
+     * dn_line can be zero. Otherwise, zero is invalid. On the first
+     * bootup, we don't need to do anything in this function.
+     */
+    if (ccb->dn_line == 0) {
+        return;
+    }
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                      ccb->index, ccb->dn_line, fname, "Cleaning up the call");
+
+    /*
+     * If there are any shared pointers because of duping
+     * make sure we have taken care of those
+     */
+    if (ccb->dup_flags & DUP_CCB) {
+        free_duped(ccb);
+    }
+
+    /* Cancel any outstanding timers */
+    (void) sip_platform_localexpires_timer_stop(ccb->index);
+
+    /*
+     * ccb->index is an unsigned type and TEL_CCB_START is zero,
+     * so just check the end condition
+     */
+    if ((int) (ccb->index) <= TEL_CCB_END) {
+        (void) sip_platform_supervision_disconnect_timer_stop(ccb->index);
+        submanager_update_ccb_addr(ccb);
+    }
+
+    /* Unbind UDP ICMP Unreachable handler just incase */
+    UNBIND_UDP_ICMP_HANDLER(ccb->udpId);
+
+    /* Free DNS Srv handle if present */
+    if (ccb->SRVhandle != NULL) {
+        dnsFreeSrvHandle(ccb->SRVhandle);
+        ccb->SRVhandle = NULL;
+    }
+
+    /* Free Ooutbound proxy DNS Srv handle if present */
+    if (ccb->ObpSRVhandle != NULL) {
+        dnsFreeSrvHandle(ccb->ObpSRVhandle);
+        ccb->ObpSRVhandle = NULL;
+    }
+
+    /*
+     * Store call history info
+     */
+    if ((int) (ccb->index) <= TEL_CCB_END) {
+        (void) sip_platform_expires_timer_stop(ccb->index);
+        (void) sip_platform_register_expires_timer_stop(ccb->index);
+    // bugid: CSCsz34666
+        memcpy(gCallHistory[ccb->index].last_call_id, ccb->sipCallID,
+            MAX_SIP_CALL_ID);
+    }
+
+    /* Free message bodies were saved during the cause of the call */
+    cc_free_msg_body_parts(&ccb->local_msg_body);
+
+    if (ccb->contact_info) {
+        sippmh_free_contact(ccb->contact_info);
+        ccb->contact_info = NULL;
+    }
+    if (ccb->record_route_info) {
+        sippmh_free_record_route(ccb->record_route_info);
+        ccb->record_route_info = NULL;
+    }
+
+    /* Free last recorded request */
+    if (ccb->last_request) {
+        free_sip_message(ccb->last_request);
+        ccb->last_request = NULL;
+    }
+
+    /* Free CC-Diversion headers */
+    for (i = 0; i < MAX_DIVERSION_HEADERS; i++) {
+        if (ccb->diversion[i]) {
+            cpr_free(ccb->diversion[i]);
+            ccb->diversion[i] = NULL;
+        }
+    }
+
+    sippmh_free_diversion_info(ccb->div_info);
+    ccb->div_info = NULL;
+    ccb->call_type = CC_CALL_NONE;
+
+    /* Free Redirection structure */
+    if (ccb->redirect_info) {
+        sippmh_free_contact(ccb->redirect_info->sipContact);
+        cpr_free(ccb->redirect_info);
+        ccb->redirect_info = NULL;
+    }
+
+    ccb->best_rpid = NULL;
+    sippmh_free_remote_party_id_info(ccb->rpid_info);
+    ccb->rpid_info = NULL;
+
+//    ccb->authen.retries_401_407 = 0;
+    ccb->authen.cnonce[0] = '\0';
+    if (ccb->authen.authorization != NULL) {
+        cpr_free(ccb->authen.authorization);
+        ccb->authen.authorization = NULL;
+    }
+    if (ccb->refer_proxy_auth != NULL) {
+        cpr_free(ccb->refer_proxy_auth);
+        ccb->refer_proxy_auth = NULL;
+    }
+#ifdef SIP_ACC_CONT
+    if (ccb->refer_acc_cont != NULL) {
+        cpr_free(ccb->refer_acc_cont);
+        ccb->refer_acc_cont = NULL;
+    }
+#endif
+
+    if (ccb->authen.sip_authen != NULL) {
+        sippmh_free_authen(ccb->authen.sip_authen);
+        ccb->authen.sip_authen = NULL;
+    }
+
+    ccb->hold_initiated = FALSE;
+    ccb->wastransferred = FALSE;
+    ccb->blindtransferred = FALSE;
+    ccb->gsm_id = CC_NO_CALL_ID;
+    ccb->con_call_id = CC_NO_CALL_ID;
+    ccb->blind_xfer_call_id = CC_NO_CALL_ID;
+    ccb->xfer_status = 0;
+
+    if (ccb->old_session_id != NULL) {
+        cpr_free(ccb->old_session_id);
+    }
+    if (ccb->old_version_id != NULL) {
+        cpr_free(ccb->old_version_id);
+    }
+    ccb->old_session_id = NULL;
+    ccb->old_version_id = NULL;
+
+    ccb->displayCalledNumber = TRUE;
+    ccb->displayCallingNumber = TRUE;
+
+    /*free all the dynamically allocated strings */
+
+    strlib_free(ccb->calledDisplayedName);
+    strlib_free(ccb->callingNumber);
+       strlib_free(ccb->altCallingNumber);
+    strlib_free(ccb->callingDisplayName);
+    strlib_free(ccb->calledNumber);
+    strlib_free(ccb->ReqURIOriginal);
+    strlib_free(ccb->sip_from);
+    strlib_free(ccb->sip_to);
+    strlib_free(ccb->sip_to_tag);
+    strlib_free(ccb->sip_from_tag);
+    strlib_free(ccb->sip_contact);
+    strlib_free(ccb->sip_reqby);
+    strlib_free(ccb->sip_require);
+    strlib_free(ccb->sip_unsupported);
+    strlib_free(ccb->referto);
+    strlib_free(ccb->sip_referTo);
+    strlib_free(ccb->sip_referredBy);
+    strlib_free(ccb->sipxfercallid);
+    strlib_free(ccb->sip_remote_party_id);
+
+    if (ccb->in_call_info) {
+        ccsip_free_call_info_header(ccb->in_call_info);
+        ccb->in_call_info = NULL;
+    }
+    if (ccb->out_call_info) {
+        ccsip_free_call_info_header(ccb->out_call_info);
+        ccb->out_call_info = NULL;
+    }
+    if (ccb->join_info) {
+        sippmh_free_join_info(ccb->join_info);
+        ccb->join_info = NULL;
+    }
+    if (ccb->feature_data) {
+        cpr_free(ccb->feature_data);
+        ccb->feature_data = NULL;
+    }
+
+    for (i = 0; i < MAX_REQ_OUTSTANDING; i++) {
+        ccb->sent_request[i].cseq_number = CCSIP_START_CSEQ;
+        ccb->sent_request[i].cseq_method = sipMethodInvalid;
+        strlib_free(ccb->sent_request[i].u.sip_via_branch);
+        strlib_free(ccb->sent_request[i].sip_via_sentby);
+        ccb->recv_request[i].cseq_number = CCSIP_START_CSEQ;
+        ccb->recv_request[i].cseq_method = sipMethodInvalid;
+        strlib_free(ccb->recv_request[i].u.sip_via_header);
+        strlib_free(ccb->recv_request[i].sip_via_sentby);
+    }
+
+    if (ccb->index >= REG_CCB_START) {
+        /*
+         * Necessary to cast the SIP_REG_STATE_IDLE to properly initialize
+         * registration CCBs which use the same ccb->state field.
+         */
+        (void) sip_sm_ccb_init(ccb, ccb->index, ccb->dn_line,
+                               SIP_REG_STATE_IDLE);
+        return;
+    }
+
+    if (sip_platform_msg_timer_outstanding_get(ccb->index)) {
+        sip_sm_change_state(ccb, SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING);
+        (void) sip_sm_ccb_init(ccb, ccb->index, ccb->dn_line,
+                               SIP_REG_STATE_IDLE /*_MSG_TIMER_OUTSTANDING */);
+    } else {
+        sip_sm_change_state(ccb, SIP_STATE_IDLE);
+        (void) sip_sm_ccb_init(ccb, ccb->index, 1, SIP_REG_STATE_IDLE);
+    }
+
+}
+
+
+int
+sip_sm_ccb_init (ccsipCCB_t *ccb, line_t ccb_index, int DN,
+                 sipRegSMStateType_t initial_state)
+{
+    int nat_enable = 0;
+    uint8_t i;
+
+    /*
+     * TEL_CCB_START equates to zero and line_t is an unsigned type,
+     * so just check the end condition
+     */
+    if ((int) ccb_index <= TEL_CCB_END) {
+        memset(ccb, 0, sizeof(ccsipCCB_t));
+    }
+
+    ccb->dup_flags = DUP_NO_FLAGS;
+    ccb->mother_ccb = NULL;
+    sip_reg_sm_change_state(ccb, initial_state); /* print the debug msg also */
+    ccb->index = ccb_index;
+    ccb->hold_initiated = FALSE;
+    ccb->wastransferred = FALSE;
+    ccb->blindtransferred = FALSE;
+    ccb->callingNumber = strlib_empty();
+    ccb->calledNumberFirstDigitDialed = FALSE;
+    ccb->calledNumber = strlib_empty();
+    ccb->altCallingNumber = strlib_empty();
+    ccb->calledDisplayedName = strlib_empty();
+    ccb->callingDisplayName = strlib_empty();
+    ccb->displayCalledNumber = TRUE;
+    ccb->displayCallingNumber = TRUE;
+    ccb->calledNumberLen = 0;
+    ccb->ReqURIOriginal = strlib_empty();
+    ccb->sip_to_tag = strlib_empty();
+    ccb->sip_from_tag = strlib_empty();
+    ccb->sip_contact = strlib_empty();
+    ccb->sip_reqby = strlib_empty();
+    ccb->sip_require = strlib_empty();
+    ccb->sip_unsupported = strlib_empty();
+    ccb->sip_remote_party_id = strlib_empty();
+    ccb->flags = 0;
+    ccb->avt.payload_type = RTP_NONE;
+    ccb->alert_info = ALERTING_NONE;
+    ccb->alerting_ring = VCM_INSIDE_RING;
+    ccb->wait_for_ack = FALSE;
+    ccb->send_delayed_bye = FALSE;
+    ccb->retx_flag = FALSE;
+    ccb->early_transfer = FALSE;
+    ccb->redirect_info = NULL;
+    ccb->proxySelection = SIP_PROXY_DEFAULT;
+    ccb->outBoundProxyAddr = ip_addr_invalid;
+    ccb->outBoundProxyPort = 0;
+    ccb->oa_state = OA_IDLE;
+    ccb->call_type = CC_CALL_NONE;
+    ccb->cc_cfg_table_entry = NULL;
+    ccb->first_pass_3xx = TRUE;
+
+    /* Sip msg destination is set to proxy by default */
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        sip_config_get_net_device_ipaddr(&(ccb->src_addr));
+    } else {
+        sip_config_get_nat_ipaddr(&(ccb->src_addr));
+    }
+
+    config_get_value(CFGID_VOIP_CONTROL_PORT, &ccb->local_port,
+                     sizeof(ccb->local_port));
+
+    if ((int) ccb_index <= TEL_CCB_END) {
+        ccb->type = SIP_CALL_CCB;
+        ccb->dn_line = (line_t) DN;
+        sipTransportGetServerIPAddr(&(ccb->dest_sip_addr), ccb->dn_line);
+        ccb->dest_sip_port = sipTransportGetPrimServerPort(ccb->dn_line);
+        ccb->sipCallID[0] = '\0';
+    } else if ((ccb_index >= REG_CCB_START) && (ccb_index <= REG_CCB_END)) {
+        ccb->type = SIP_REG_CCB;
+        ccb->dn_line = ccb_index - MAX_TEL_LINES + 1;
+        sip_regmgr_set_cc_info(ccb_index, ccb->dn_line, &ccb->cc_type,
+                               (void *)&ccb->cc_cfg_table_entry);
+        if (ccb->cc_type == CC_CCM) {
+            /*
+             * regmgr - If type of call control is ccm set the
+             * address here as well.
+             */
+            sipTransportGetServerIPAddr(&(ccb->dest_sip_addr), ccb->dn_line);
+            ccb->dest_sip_port = sipTransportGetPrimServerPort(ccb->dn_line);
+        } else {
+            /*
+             * regmgr - Assume it is CSPS for now.
+             */
+            ccb->dest_sip_addr = ip_addr_invalid;
+            ccb->dest_sip_port = sipTransportGetPrimServerPort(ccb->dn_line);
+        }
+    } else if (ccb_index == REG_BACKUP_CCB) {
+        ccb->type = SIP_REG_CCB;
+        ccb->dn_line = REG_BACKUP_DN;
+        sip_regmgr_set_cc_info(ccb_index, ccb->dn_line, &ccb->cc_type,
+                               &ccb->cc_cfg_table_entry);
+        if (ccb->cc_type != CC_CCM) {
+            /*
+             * regmgr - Assume it is CSPS for now.
+             */
+            sipTransportGetBkupServerAddress(&ccb->dest_sip_addr, ccb->dn_line, ccb->reg.proxy);
+            ccb->dest_sip_port = sipTransportGetBkupServerPort(ccb->dn_line);
+        }
+    } else if ((ccb_index >= REG_FALLBACK_CCB_START) &&
+               (ccb_index <= REG_FALLBACK_CCB_END)) {
+        /*
+         * regmgr - There can be upto 3 fallback ccb's that get created
+         * dynamically. If the ccb index indicates that then set the
+         * type to be SIP_REG_CCB so that the register state
+         * machine gets the event.
+         */
+        ccb->type = SIP_REG_CCB;
+        ccb->dn_line = REG_BACKUP_DN;
+    }
+    /*
+     * Get the listen port that is configured from the
+     * Transport Interface instead of the direct config
+     * read from config.
+     */
+    ccb->local_port = sipTransportGetListenPort(ccb->dn_line, ccb);
+    /* Init SDP info */
+    memset(&ccb->local_msg_body, 0, sizeof(cc_msgbody_info_t));
+    //ccb->dest_port            = 0;
+    //ccb->dest_addr            = 0;
+    ccb->old_session_id = NULL;
+    ccb->old_version_id = NULL;
+
+    /* Into To:, From:, and Request-URI: fields */
+    ccb->ReqURI[0] = '\0';
+    ccb->sip_from  = strlib_empty();   /* There is no pre-set From: field */
+    ccb->sip_to    = strlib_empty();   /* There is no pre-set To: field */
+
+    // The following is the refer stuff
+
+    ccb->sip_referTo = strlib_empty();
+    ccb->sip_referredBy = strlib_empty();
+    ccb->referto = strlib_empty();
+    ccb->sipxfercallid = strlib_empty();
+    ccb->featuretype = CC_FEATURE_NONE;
+    ccb->join_info = NULL;
+    ccb->feature_data = NULL;
+
+    for (i = 0; i < MAX_REQ_OUTSTANDING; i++) {
+        ccb->sent_request[i].cseq_number = CCSIP_START_CSEQ;
+        ccb->sent_request[i].cseq_method = sipMethodInvalid;
+        ccb->sent_request[i].u.sip_via_branch = strlib_empty();
+        ccb->sent_request[i].sip_via_sentby = strlib_empty();
+        ccb->recv_request[i].cseq_number = CCSIP_START_CSEQ;
+        ccb->recv_request[i].cseq_method = sipMethodInvalid;
+        ccb->recv_request[i].u.sip_via_header = strlib_empty();
+        ccb->recv_request[i].sip_via_sentby = strlib_empty();
+    }
+
+    ccb->last_recv_request_cseq        = 0;
+    ccb->last_recv_request_cseq_method = sipMethodInvalid;
+
+    if (ccb_index < REG_CCB_START) {
+        // If this is a tel CCB, restart the counter
+        ccb->last_used_cseq = CCSIP_START_CSEQ;
+    } else {
+        // If this is a reg CCB, continue from where we left off,
+        // and only initialize it the first time
+        if (ccb->last_used_cseq == 0) {
+            ccb->last_used_cseq = CCSIP_START_CSEQ;
+        }
+    }
+
+    ccb->last_request = NULL;
+
+    ccb->xfr_inprogress = SIP_SM_NO_XFR;
+
+
+    ccb->gsm_id = CC_NO_CALL_ID;
+    ccb->con_call_id = CC_NO_CALL_ID;
+    ccb->blind_xfer_call_id = CC_NO_CALL_ID;
+    ccb->xfer_status = 0;
+
+    /* AVT info */
+    config_get_value(CFGID_DTMF_AVT_PAYLOAD, &ccb->avt.payload_type,
+                     sizeof(ccb->avt.payload_type));
+
+    /* SIP REGISTER info */
+    ccb->reg.registered = 0;
+    ccb->reg.tmr_expire = 0;
+    ccb->reg.act_time = 0;
+    ccb->reg.rereg_pending = 0;
+
+    /* SIP authentication info */
+    ccb->authen.retries_401_407 = 0;
+    ccb->authen.cred_type = 0;
+    ccb->authen.authorization = NULL;
+    ccb->authen.status_code = 0;
+    ccb->authen.nc_count = 0;
+    ccb->authen.new_flag = FALSE;
+    ccb->in_call_info = NULL;
+    ccb->out_call_info = NULL;
+    ccb->udpId = NULL;
+    ccb->callref = 0;
+
+    return (0);
+}
+
+
+
+const char *
+sip_util_state2string (sipSMStateType_t state)
+{
+    switch (state) {
+    case SIP_STATE_NONE:
+        return ("SIP_STATE_NONE");
+
+    case SIP_STATE_IDLE:
+        return ("SIP_STATE_IDLE");
+
+    case SIP_STATE_SENT_INVITE:
+        return ("SIP_STATE_SENT_INVITE");
+
+    case SIP_STATE_SENT_INVITE_CONNECTED:
+        return ("SIP_STATE_SENT_INVITE_CONNECTED");
+
+    case SIP_STATE_RECV_INVITE:
+        return ("SIP_STATE_RECV_INVITE");
+
+    case SIP_STATE_RECV_INVITE_PROCEEDING:
+        return ("SIP_STATE_RECV_INVITE_PROCEEDING");
+
+    case SIP_STATE_RECV_INVITE_ALERTING:
+        return ("SIP_STATE_RECV_INVITE_ALERTING");
+
+    case SIP_STATE_RECV_INVITE_CONNECTED:
+        return ("SIP_STATE_RECV_INVITE_CONNECTED");
+
+    case SIP_STATE_ACTIVE:
+        return ("SIP_STATE_ACTIVE");
+
+    case SIP_STATE_RECV_MIDCALL_INVITE_CCFEATUREACK_PENDING:
+        return ("SIP_STATE_RECV_MIDCALL_INVITE_CCFEATUREACK_PENDING");
+
+    case SIP_STATE_RECV_MIDCALL_INVITE_SIPACK_PENDING:
+        return ("SIP_STATE_RECV_MIDCALL_INVITE_SIPACK_PENDING");
+
+    case SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING:
+        return ("SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING");
+
+    case SIP_STATE_RELEASE:
+        return ("SIP_STATE_RELEASE");
+
+    case SIP_STATE_BLIND_XFER_PENDING:
+        return ("SIP_STATE_BLIND_XFER_PENDING");
+
+    case SIP_STATE_SENT_OOD_REFER:
+        return ("SIP_STATE_SENT_OOD_REFER");
+
+    case SIP_STATE_RECV_UPDATEMEDIA_CCFEATUREACK_PENDING:
+        return ("SIP_STATE_RECV_UPDATEMEDIA_CCFEATUREACK_PENDING");
+
+    case SIP_STATE_SENT_MIDCALL_INVITE:
+        return ("SIP_STATE_SENT_MIDCALL_INVITE");
+
+    default:
+        return ("UNKNOWN STATE");
+    }
+}
+
+
+const char *
+sip_util_event2string (sipSMEventType_t event)
+{
+    switch (event) {
+    case E_SIP_INVITE:
+        return ("E_SIP_INVITE");
+    case E_SIP_ACK:
+        return ("E_SIP_ACK");
+    case E_SIP_BYE:
+        return ("E_SIP_BYE");
+    case E_SIP_CANCEL:
+        return ("E_SIP_CANCEL");
+    case E_SIP_1xx:
+        return ("E_SIP_1xx");
+    case E_SIP_2xx:
+        return ("E_SIP_2xx");
+    case E_SIP_3xx:
+        return ("E_SIP_3xx");
+    case E_SIP_FAILURE_RESPONSE:
+        return ("E_SIP_FAILURE_RESPONSE");
+    case E_SIP_REFER:
+        return ("E_SIP_REFER");
+    case E_CC_SETUP:
+        return ("E_CC_SETUP");
+    case E_CC_SETUP_ACK:
+        return ("E_CC_SETUP_ACK");
+    case E_CC_PROCEEDING:
+        return ("E_CC_PROCEEDING");
+    case E_CC_ALERTING:
+        return ("E_CC_ALERTING");
+    case E_CC_CONNECTED:
+        return ("E_CC_CONNECTED");
+    case E_CC_CONNECTED_ACK:
+        return ("E_CC_CONNECTED_ACK");
+    case E_CC_RELEASE:
+        return ("E_CC_RELEASE");
+    case E_CC_RELEASE_COMPLETE:
+        return ("E_CC_RELEASE_COMPLETE");
+    case E_CC_FEATURE:
+        return ("E_CC_FEATURE");
+    case E_CC_FEATURE_ACK:
+        return ("E_CC_FEATURE_ACK");
+    case E_CC_CAPABILITIES:
+        return ("E_CC_CAPABILITIES");
+    case E_CC_CAPABILITIES_ACK:
+        return ("E_CC_CAPABILITIES_ACK");
+    case E_CC_SUBSCRIBE:
+        return ("E_CC_SUBSCRIBE");
+    case E_CC_INFO:
+        return ("E_CC_INFO");
+    case E_SIP_INV_EXPIRES_TIMER:
+        return ("E_SIP_INV_EXPIRES_TIMER");
+    case E_SIP_INV_LOCALEXPIRES_TIMER:
+        return ("E_SIP_INV_LOCALEXPIRES_TIMER");
+    case E_SIP_SUPERVISION_DISCONNECT_TIMER:
+        return ("E_SIP_SUPERVISION_DISCONNECT_TIMER");
+    case E_SIP_TIMER:
+        return ("E_SIP_TIMER");
+    case E_SIP_OPTIONS:
+        return ("E_SIP_OPTIONS");
+    case E_SIP_UPDATE:
+        return ("E_SIP_UPDATE");
+    case E_SIP_UPDATE_RESPONSE:
+        return ("E_SIP_UPDATE_RESPONSE");
+    case E_SIP_GLARE_AVOIDANCE_TIMER:
+        return ("E_SIP_GLARE_AVOIDANCE_TIMER");
+    case E_SIP_ICMP_UNREACHABLE:
+        return "E_SIP_ICMP_UNREACHABLE";
+    default:
+        return ("UNKNOWN EVENT");
+    }
+}
+
+
+sipSMEventType_t
+sip_util_ccevent2sipccevent (cc_msgs_t cc_msg_id)
+{
+    switch (cc_msg_id) {
+    case CC_MSG_SETUP:
+        return (E_CC_SETUP);
+    case CC_MSG_SETUP_ACK:
+        return (E_CC_SETUP_ACK);
+    case CC_MSG_PROCEEDING:
+        return (E_CC_PROCEEDING);
+    case CC_MSG_ALERTING:
+        return (E_CC_ALERTING);
+    case CC_MSG_CONNECTED:
+        return (E_CC_CONNECTED);
+    case CC_MSG_CONNECTED_ACK:
+        return (E_CC_CONNECTED_ACK);
+    case CC_MSG_RELEASE:
+        return (E_CC_RELEASE);
+    case CC_MSG_RELEASE_COMPLETE:
+        return (E_CC_RELEASE_COMPLETE);
+    case CC_MSG_FEATURE:
+        return (E_CC_FEATURE);
+    case CC_MSG_FEATURE_ACK:
+        return (E_CC_FEATURE_ACK);
+    case CC_MSG_INFO:
+        return (E_CC_INFO);
+    default:
+        return ((sipSMEventType_t) (-1));
+    }
+}
+
+
+void
+sip_create_new_sip_call_id (char *sipCallID, uint8_t *mac_address, char *pSrcAddrStr)
+{
+    static uint16_t count = 1;
+
+    count++;
+
+    if (sipCallID == NULL) {
+        return;
+    }
+    snprintf(sipCallID, MAX_SIP_CALL_ID, "%.4x%.4x-%.4x%.4x-%.8x-%.8x@%s",
+             mac_address[0] * 256 + mac_address[1],
+             mac_address[2] * 256 + mac_address[3],
+             mac_address[4] * 256 + mac_address[5],
+             count, (unsigned int)cpr_rand(), (unsigned int)cpr_rand(), pSrcAddrStr);
+}
+
+void
+sip_util_get_new_call_id (ccsipCCB_t *ccb)
+{
+    const char     *fname = "sip_util_get_new_call_id";
+    uint8_t         mac_address[MAC_ADDRESS_LENGTH];
+    char            pSrcAddrStr[MAX_IPADDR_STR_LEN];
+    char           *temp_call_id;
+
+    memset(pSrcAddrStr, 0, MAX_IPADDR_STR_LEN);
+
+    /* Args Check */
+    if (!ccb) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args Check: ccb is null\n", fname);
+        return;
+    }
+
+    /* use same call id for registration */
+    if ((ccb->type == SIP_REG_CCB) && (ccb->sipCallID[0] != 0)) {
+        return;
+    }
+
+    /*
+     * use pre allocated sip call id, if available.
+     */
+    if (ccb->type != SIP_REG_CCB) {
+        temp_call_id = ccsip_find_preallocated_sip_call_id(ccb->dn_line);
+        if (temp_call_id != NULL) {
+            sstrncpy(ccb->sipCallID, temp_call_id, MAX_SIP_CALL_ID);
+            CCSIP_DEBUG_STATE(DEB_F_PREFIX"using pre allocated call ID\n",
+                DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+            ccsip_free_preallocated_sip_call_id(ccb->dn_line);
+            return;
+        }
+    }
+    ipaddr2dotted(pSrcAddrStr, &ccb->src_addr);
+
+    platform_get_wired_mac_address(mac_address);
+
+    sip_create_new_sip_call_id(ccb->sipCallID, mac_address, pSrcAddrStr);
+}
+
+
+void
+sip_util_make_tag (char *pTagBuf)
+{
+    const char     *fname = "sip_util_make_tag";
+    uint8_t         mac_address[MAC_ADDRESS_LENGTH];
+    static uint16_t count = 1;
+
+    if (!pTagBuf) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args Check: pTagBuf is null\n", fname);
+        return;
+    }
+
+    platform_get_wired_mac_address(mac_address);
+    count++;
+
+    snprintf(pTagBuf, MAX_SIP_URL_LENGTH, "%.4x%.4x%.4x%.4x%.8x-%.8x",
+             mac_address[0] * 256 + mac_address[1],
+             mac_address[2] * 256 + mac_address[3],
+             mac_address[4] * 256 + mac_address[5],
+             count, (unsigned int)cpr_rand(), (unsigned int)cpr_rand());
+}
+
+int
+sip_sm_init (void)
+{
+    line_t          i;
+    const char     *fname = "sip_sm_init";
+    int            sdpmode = 0;
+
+       config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+       if (!sdpmode) {
+
+        if (ccsip_register_init() == SIP_ERROR) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"registration initialization failed\n", fname);
+            return SIP_ERROR;
+        }
+
+        if (ccsip_info_package_handler_init() == SIP_ERROR) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"info package initialization failed\n", fname);
+            return SIP_ERROR;
+        }
+
+        /*
+         * Allocate timers for CCBs
+         */
+        if (sip_platform_timers_init() == SIP_ERROR) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"timer initialization failed\n", fname);
+            return SIP_ERROR;
+        }
+
+        if (sipTransportInit() != SIP_OK) {
+            return SIP_ERROR;
+        }
+
+        DEF_DEBUG(DEB_F_PREFIX"Disabling mass reg state", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+        for (i = 0; i < MAX_CCBS; i++) {
+            if (i == 0 || i == (MAX_CCBS-1)) {
+                g_disable_mass_reg_debug_print = FALSE;
+            } else {
+                g_disable_mass_reg_debug_print = TRUE;
+            }
+            sip_sm_call_cleanup(&(gGlobInfo.ccbs[i]));
+            if (sip_sm_ccb_init(&(gGlobInfo.ccbs[i]), i, 1, SIP_REG_STATE_IDLE) < 0) {
+                return SIP_ERROR;
+            }
+        }
+        g_disable_mass_reg_debug_print = FALSE;
+
+        /* Initialize all timers */
+        sip_platform_msg_timers_init();
+
+        /* Initialize Subscription Manager */
+        if (sip_subsManager_init() != SIP_OK) {
+            return SIP_ERROR;
+        }
+
+    }
+
+    /* Initialize SDP Parser */
+    if (!sip_sdp_init()) {
+        /* Error initialize the SDP error */
+        return (SIP_ERROR);
+    }
+
+    return SIP_OK;
+}
+
+void
+ccsip_handle_sip_shutdown ()
+{
+    const char     *fname = "handle_sip_shutdown";
+    ccsipCCB_t     *ccb = NULL;
+    line_t          i;
+
+    for (i = TEL_CCB_START; i <= TEL_CCB_END; i++) {
+        ccb = sip_sm_get_ccb_by_index(i);
+        if (ccb) {
+            switch (ccb->state) {
+            case SIP_STATE_RECV_INVITE:
+            case SIP_STATE_RECV_INVITE_PROCEEDING:
+            case SIP_STATE_RECV_INVITE_ALERTING:
+                CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Received invite: %d: STATE: %s\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->dn_line, ccb->gsm_id, fname),
+                                                                 ccb->index, sip_util_state2string(ccb->state));
+                sipSPISendInviteResponse(ccb, SIP_SERV_ERR_INTERNAL,
+                                         SIP_SERV_ERR_INTERNAL_PHRASE, 0, NULL,
+                                         FALSE /* no SDP */ , FALSE /* reTx */);
+                sip_cc_release_complete(ccb->gsm_id, ccb->dn_line,
+                                        CC_CAUSE_FACILITY_REJECTED);
+                ccb->wait_for_ack = FALSE;
+                sip_sm_change_state(ccb, SIP_STATE_IDLE);
+                sip_sm_call_cleanup(ccb);
+                break;
+
+            case SIP_STATE_RECV_INVITE_CONNECTED:
+            case SIP_STATE_ACTIVE:
+            default:
+                CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Clearing %d STATE: %s\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->dn_line, ccb->gsm_id, fname),
+                                                                 ccb->index, sip_util_state2string(ccb->state));
+                sipSPISendBye(ccb, NULL, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_IDLE);
+                // send this to make sure GSM goes IDLE if it's currently in CONNECTED
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_call_cleanup(ccb);
+                break;
+
+            case SIP_STATE_SENT_INVITE:
+                CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Sent invite: Clearing %d STATE: %s\n",
+                                 DEB_L_C_F_PREFIX_ARGS(SIP_STATE,  ccb->dn_line, ccb->gsm_id, fname),
+                                                                ccb->index, sip_util_state2string(ccb->state));
+                sipSPISendCancel(ccb);
+                sip_sm_change_state(ccb, SIP_STATE_IDLE);
+                sip_cc_release_complete(ccb->gsm_id, ccb->dn_line,
+                                        CC_CAUSE_FACILITY_REJECTED);
+                sip_sm_call_cleanup(ccb);
+                break;
+
+            case SIP_STATE_SENT_INVITE_CONNECTED:
+                CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Sent invite connected: Clearing %d STATE: %s\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->dn_line, ccb->gsm_id, fname),
+                                                                 ccb->index, sip_util_state2string(ccb->state));
+                if (sipSPISendAck(ccb, NULL) == FALSE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                      fname, "sipSPISendAck");
+                }
+                sipSPISendBye(ccb, NULL, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_IDLE);
+                // send this to make sure GSM goes IDLE if it's currently in CONNECTED
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_call_cleanup(ccb);
+                break;
+
+            case SIP_STATE_RELEASE:
+                CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Release: Clearing %d STATE: %s\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->dn_line, ccb->gsm_id, fname),
+                                                                 ccb->index, sip_util_state2string(ccb->state));
+                sip_sm_change_state(ccb, SIP_STATE_IDLE);
+                sip_cc_release_complete(ccb->gsm_id, ccb->dn_line,
+                                        CC_CAUSE_FACILITY_REJECTED);
+                sip_sm_call_cleanup(ccb);
+                break;
+
+            case SIP_STATE_IDLE:
+                break;
+            }
+
+        }
+    }
+}
+
+void
+sip_shutdown (void)
+{
+
+    DEF_DEBUG(DEB_F_PREFIX"SIP Shutting down...\n", DEB_F_PREFIX_ARGS(SIP_TASK, "sip_shutdown"));
+
+    // The SIP SM is already shut down
+    if (sip.taskInited == FALSE) {
+        return;
+    }
+
+    sip.taskInited = FALSE;
+    DEF_DEBUG(DEB_F_PREFIX" sip.taskInited is set to false\n", DEB_F_PREFIX_ARGS(SIP_TASK, "sip_shutdown"));
+
+//CPR TODO: need reference for
+    if ((PHNGetState() == STATE_CONNECTED) ||
+        (PHNGetState() == STATE_DONE_LOADING) ||
+        (PHNGetState() == STATE_CFG_UPDATE)) {
+
+        // Disconnect calls and clean CCB
+        ccsip_handle_sip_shutdown();
+
+        // Unregister from all servers and deallocate reg ack timer
+        sip_regmgr_shutdown();
+
+        // Stop and deallocate timers
+        sip_platform_timers_shutdown();
+
+        // Shutdown Subscription Manager
+        (void) sip_subsManager_shut();
+        //reset publish handler
+        publish_reset();
+
+        // Close all sockets
+        sipTransportShutdown();
+        ccsip_remove_wlan_classifiers();
+    }
+
+    ccsip_info_package_handler_shutdown();
+}
+
+/******
+void sip_restart_phase2 (void *data)
+{
+    sip.taskInited = TRUE; // Forcing sip_shutdown() to execute
+    sip_shutdown();
+    if (sip_sm_init() < 0) {
+        CCSIP_DEBUG_ERROR(" Error: sip_sm_init failed\n");
+        return;
+    }
+    sip_platform_init();
+    sip.taskInited = TRUE;
+    sip_mode_quiet = FALSE;
+    sip_reg_all_failed = FALSE;
+}
+
+void sip_restart_phase1 (void)
+{
+    if (sip_reg_all_failed) {
+        // NO CCM available; need not wait for unreg timer
+        sip_restart_phase2(NULL);
+    } else {
+        // Unregister all lines
+        ccsip_register_cancel(TRUE, TRUE);
+        // Start timer with a 2sec expiration
+        sip_platform_unregistration_timer_start(2000);
+
+        // Stop the periodic timer
+        (void) sip_platform_subnot_periodic_timer_stop();
+    }
+}
+*******/
+void
+sip_restart (void)
+{
+    const char       *fname = "sip_restart";
+
+    DEF_DEBUG(DEB_F_PREFIX"In sip_restart\n", DEB_F_PREFIX_ARGS(SIP_CTRL, fname));
+    if (sip_sm_init() < 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_sm_init failed\n", fname);
+        return;
+    }
+    sip_platform_init();
+    sip.taskInited = TRUE;
+    DEF_DEBUG(DEB_F_PREFIX"sip.taskInited is set to true \n", DEB_F_PREFIX_ARGS(SIP_CTRL, fname));
+    sip_mode_quiet = FALSE;
+    sip_reg_all_failed = FALSE;
+    ccsip_remove_wlan_classifiers();
+
+    // Initialize all GSM modules
+    cc_fail_fallback_gsm(CC_SRC_SIP, CC_RSP_COMPLETE, CC_REG_FAILOVER_RSP);
+}
+
+void
+sip_shutdown_phase2 (int action)
+{
+    DEF_DEBUG(DEB_F_PREFIX"(%d)\n",
+                     DEB_F_PREFIX_ARGS(SIP_CTRL, "sip_shutdown_phase2"), action);
+    sip.taskInited = TRUE; // Forcing sip_shutdown() to execute
+    DEF_DEBUG(DEB_F_PREFIX"sip.taskInited is set to true\n", DEB_F_PREFIX_ARGS(SIP_CTRL, "sip_shutdown_phase2"));
+    sip_shutdown();
+    if (action == SIP_EXTERNAL || action == SIP_STOP) {
+        shutdownCCAck(action);
+    } else if (action == SIP_INTERNAL) {
+        // Continue on to reinit
+        sip_restart();
+    }
+}
+
+void
+sip_shutdown_phase1 (int action, int reason)
+{
+    DEF_DEBUG(DEB_F_PREFIX"In sip_shutdown_phase1 (%d)\n",
+                     DEB_F_PREFIX_ARGS(SIP_CTRL, "sip_shutdown_phase1"), action);
+    if (sip_reg_all_failed) {
+        // NO CCM available; need not wait for unreg timer
+        sip_shutdown_phase2(action);
+    } else {
+        // Unregister all lines
+        ccsip_register_cancel(TRUE, TRUE);
+        // Start timer with a 2sec expiration
+        (void) sip_platform_unregistration_timer_start(2000, (boolean) action);
+    }
+}
+
+ccsipCCB_t *
+sip_sm_get_ccb_by_ccm_id_and_index (int ccmid, line_t idx)
+{
+    static const char fname[] = "sip_sm_get_ccb_by_ccm_id_and_index";
+    fallback_ccb_t *fallback_ccb;
+    ccsipCCB_t * ccb = NULL;
+    CCM_ID ccm_id = ccmid;
+
+    if (ccm_id >= MAX_CCM) {
+        CCSIP_DEBUG_ERROR(DEB_F_PREFIX"invalid ccm_id=%d "
+            "ccb_index=%d\n",DEB_F_PREFIX_ARGS(SIP_BRANCH, fname),
+            ccm_id , idx);
+        return ccb;
+    }
+
+    if ((int) idx < MAX_CCBS) {
+        ccb = &(gGlobInfo.ccbs[idx]);
+    }
+    /*
+     * regmgr - Could be Fallback ccb's
+     */
+    if (ccb == NULL) {
+        fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(idx);
+        if (fallback_ccb != NULL) {
+            ccb = fallback_ccb->ccb;
+        }
+    }
+    if (ccb != NULL) {
+        if ((ccb->cc_cfg_table_entry == NULL) ||
+            (((ti_config_table_t *)(ccb->cc_cfg_table_entry))->ti_specific.ti_ccm.ccm_id != ccm_id)) {
+            /*
+             * the standby cucm must have moved to active position, but we did not
+             * yet have processed the failover response where ccb are updated to
+             * point to new active. That is why we are getting into this situation.
+             * Currently, we are throwing away the message, which is mainly the msg
+             * that standby cucm has sent. Should not do much harm.
+             */
+            DEF_DEBUG(DEB_F_PREFIX"ccb index has moved or cfg_table not initialized for the cucm=%s. "
+                "index=%d ccb=%d. Throwing away the msg.\n",DEB_F_PREFIX_ARGS(SIP_BRANCH, fname),
+                CCM_ID_PRINT(ccm_id), idx, ccb);
+            ccb = NULL;
+        }
+    }
+    if (ccb == NULL) {
+        CCSIP_DEBUG_ERROR(DEB_F_PREFIX"Could not find ccb ccb_index=%d",
+             DEB_F_PREFIX_ARGS(SIP_BRANCH, fname), idx);
+    }
+    return ccb;
+}
+
+ccsipCCB_t *
+sip_sm_get_ccb_by_index (line_t idx)
+{
+    fallback_ccb_t *fallback_ccb;
+
+    if ((int) idx < MAX_CCBS) {
+        return &(gGlobInfo.ccbs[idx]);
+    }
+    /*
+     * regmgr - Could be Fallback ccb's
+     */
+    fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(idx);
+    if (fallback_ccb != NULL) {
+        return (fallback_ccb->ccb);
+    }
+    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_LINE_NUMBER_INVALID), "sip_sm_get_ccb_by_index",
+                      idx);
+    return NULL;
+}
+
+
+ccsipCCB_t *
+sip_sm_get_ccb_by_callid (const char *callid)
+{
+    line_t i;
+
+    if (callid[0] == '\0') {
+        /* Requesting call ID is NULL string, not allow */
+        return (NULL);
+    }
+    for (i = 0; i < MAX_CCBS; i++) {
+        if (strcmp(callid, gGlobInfo.ccbs[i].sipCallID) == 0) {
+            return &(gGlobInfo.ccbs[i]);
+        }
+    }
+
+    return NULL;
+}
+
+callid_t
+sip_sm_get_blind_xfereror_ccb_by_gsm_id (callid_t gsm_id)
+{
+    uint16_t i;
+
+    for (i = 0; i < MAX_CCBS; i++) {
+        if (gsm_id == gGlobInfo.ccbs[i].blind_xfer_call_id) {
+            return gGlobInfo.ccbs[i].gsm_id;
+        }
+    }
+
+    return CC_NO_CALL_ID;
+}
+
+
+ccsipCCB_t *
+sip_sm_get_ccb_by_target_call_id (callid_t con_id)
+{
+    uint16_t i;
+
+    for (i = 0; i < MAX_CCBS; i++) {
+        if (con_id == gGlobInfo.ccbs[i].gsm_id) {
+            return &(gGlobInfo.ccbs[i]);
+        }
+    }
+
+    return NULL;
+}
+
+ccsipCCB_t *
+sip_sm_get_target_call_by_gsm_id (callid_t gsm_id)
+{
+    uint16_t i;
+
+    for (i = 0; i < MAX_CCBS; i++) {
+        if (gsm_id == gGlobInfo.ccbs[i].con_call_id) {
+            return &(gGlobInfo.ccbs[i]);
+        }
+    }
+
+    return NULL;
+}
+
+ccsipCCB_t *
+sip_sm_get_target_call_by_con_call_id (callid_t con_call_id)
+{
+    uint16_t i;
+
+    for (i = 0; i < MAX_CCBS; i++) {
+        if (con_call_id == gGlobInfo.ccbs[i].gsm_id) {
+            return &(gGlobInfo.ccbs[i]);
+        }
+    }
+
+    return NULL;
+}
+
+ccsipCCB_t *
+sip_sm_get_ccb_next_available (line_t *line_number)
+{
+    line_t i;
+
+    for (i = TEL_CCB_START; i <= TEL_CCB_END; i++) {
+        if (gGlobInfo.ccbs[i].state == SIP_STATE_IDLE) {
+            *line_number = i;
+            break;
+        }
+    }
+
+    if (i > TEL_CCB_END) {
+        return NULL;
+    }
+    return &(gGlobInfo.ccbs[i]);
+}
+
+/**
+ * sip_sm_get_ccb_by_gsm_id
+ *
+ * This fucntion tries to match the ccbs given the gsm id
+ * The algorithm tries to match the non duplicate CCB first
+ * If the only match is a DUP_CCB it shall be returned as a match
+ * @param[in] gsm_id   GSM ID in the CCB
+ *
+ * @return            ccsipCCB_t * or NULL
+ *
+ */
+ccsipCCB_t *
+sip_sm_get_ccb_by_gsm_id (callid_t gsm_id)
+{
+    line_t i;
+    ccsipCCB_t *dupCCB = NULL;
+
+    if ( gsm_id == CC_NO_CALL_ID )
+        return NULL;
+
+    for (i = 0; i < MAX_CCBS; i++) {
+        if (gGlobInfo.ccbs[i].gsm_id == gsm_id) {
+             if ( gGlobInfo.ccbs[i].dup_flags & DUP_CCB ) {
+                 dupCCB = &(gGlobInfo.ccbs[i]);
+             } else {
+                 return &(gGlobInfo.ccbs[i]);
+             }
+        }
+    }
+
+    return dupCCB;
+}
+
+/**
+ * sip_sm_ccb_match_branch_cseq
+ *
+ * This fucntion tries to match the Branch and Cseq ID for a
+ * given CCB to see if the values match in the response with the
+ * values sent in the request.
+ *
+ * @param[in] ccb      Pointer to ccsipCCB_t structure.
+ * @param[in] sipCseq  Pointer to sipCseq_t structure.
+ * @param[in] via_this Pointer to sipVia_t structure.
+ *
+ * @pre               (ccb      not_eq NULL)
+ * @pre               (sipCseq  not_eq NULL)
+ * @pre               (via_this not_eq NULL)
+ *
+ * @return            TRUE/FALSE
+ *
+ */
+static boolean
+sip_sm_ccb_match_branch_cseq (ccsipCCB_t *ccb,
+                              sipCseq_t  *sipCseq,
+                              sipVia_t   *via_this)
+{
+    const char       *fname = "sip_sm_ccb_match_branch_cseq";
+    int16_t          trx_index = -1;
+    sipTransaction_t *trx = NULL;
+
+    trx_index = get_method_request_trx_index(ccb, sipCseq->method,
+                                             TRUE);
+    if (trx_index != -1) {
+        // match cseq and viabranchid
+        trx = &(ccb->sent_request[trx_index]);
+        if ((trx->cseq_number == sipCseq->number) &&
+            (trx->u.sip_via_branch[0] != '\0') &&
+            (via_this->branch_param != NULL) &&
+            (strncmp(trx->u.sip_via_branch, via_this->branch_param,
+                                VIA_BRANCH_LENGTH) == 0)) {
+            CCSIP_DEBUG_STATE(DEB_F_PREFIX"Matched branch_id & CSeq\n", DEB_F_PREFIX_ARGS(SIP_BRANCH, fname));
+            return (TRUE);
+        } else {
+            CCSIP_DEBUG_ERROR(SIP_L_C_F_PREFIX"Mismatched CSeq or"
+                              " Via's branch parameter in response:"
+                              "ccb=0x%x,%d, cseq(trx,msg)=(%d,%d),"
+                              "branch(trx,msg)=(%s,%s)\n",
+                               ccb->dn_line, ccb->gsm_id, fname, ccb,
+                               ccb->index, trx->cseq_number, sipCseq->number,
+                               trx->u.sip_via_branch,
+                               via_this->branch_param);
+            return (FALSE);
+        }
+    }
+
+    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Method index not found\n", fname);
+    return (FALSE);
+}
+
+// The function below attempts to determine more intelligently if there
+// is an existing dialog to which an incoming message should be directed.
+// In addition to call-id, it also considers the Req-URI, CSeq, etc.
+uint16_t
+sip_sm_determine_ccb (const char *callid,
+                      sipCseq_t * sipCseq,
+                      sipMessage_t *pSipMessage,
+                      boolean is_request,
+                      ccsipCCB_t **ccb_ret)
+{
+
+    const char     *fname = "sip_sm_determine_ccb";
+    const char     *to = NULL;
+    sipLocation_t  *to_loc = NULL;
+    line_t          i;
+    ccsipCCB_t     *ccb = NULL;
+    sipReqLine_t   *requestURI = NULL;
+    genUrl_t       *genUrl = NULL;
+    sipUrl_t       *sipUriUrl = NULL;
+    char           *pUser = NULL;
+    char            reqURI[MAX_SIP_URL_LENGTH];
+    int16_t         trx_index = -1;
+    sipTransaction_t *trx = NULL;
+    sipVia_t       *via_this = NULL;
+    sipVia_t       *via_last = NULL;
+    const char     *pViaHeaderStr = NULL;
+    boolean        match = FALSE;
+
+    *ccb_ret = NULL;
+
+    // Dialog matching algorithm is as follows:
+    // First, obtain the CCB by matching call-id and to-tag, if present
+    to = sippmh_get_cached_header_val(pSipMessage, TO);
+    if (to) {
+        to_loc = sippmh_parse_from_or_to((char *)to, TRUE);
+        if (to_loc) {
+            if (to_loc->tag) {
+                for (i = 0; i < MAX_CCBS; i++) {
+                    if (strcmp(callid, gGlobInfo.ccbs[i].sipCallID) == 0) {
+                        ccb = &(gGlobInfo.ccbs[i]);
+                        if (ccb->sip_to_tag[0] != '\0') {
+                            if (strcmp(to_loc->tag, ccb->sip_to_tag) == 0) {
+                                *ccb_ret = ccb;
+                                CCSIP_DEBUG_STATE(DEB_F_PREFIX"Matched to_tag\n",
+                                    DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+                                break;
+                            } else if (strcmp(to_loc->tag, ccb->sip_from_tag) == 0) {
+                                *ccb_ret = ccb;
+                                CCSIP_DEBUG_STATE(DEB_F_PREFIX"Matched from_tag\n",
+                                    DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            sippmh_free_location(to_loc);
+        }
+    }
+
+    // Get the VIA parameters so proper matching can be done
+    pViaHeaderStr = sippmh_get_cached_header_val(pSipMessage, VIA);
+    if (pViaHeaderStr) {
+        via_this = sippmh_parse_via(pViaHeaderStr);
+    }
+    if (!pViaHeaderStr || !via_this) {
+        return (SIP_CLI_ERR_BAD_REQ);
+    }
+
+    // If not found, and this is a request, obtain by matching call-id,
+    // and user part of the ReqURI
+    if ((*ccb_ret == NULL) && is_request) {
+        reqURI[0] = '\0';
+        requestURI = sippmh_get_request_line(pSipMessage);
+        if (requestURI) {
+            if (requestURI->url) {
+                genUrl = sippmh_parse_url(requestURI->url, TRUE);
+                if (genUrl) {
+                    if (genUrl->schema == URL_TYPE_SIP) {
+                        sipUriUrl = genUrl->u.sipUrl;
+                        if (sipUriUrl) {
+                            pUser = sippmh_parse_user(sipUriUrl->user);
+                            if (pUser) {
+                                sstrncpy(reqURI, pUser, sizeof(reqURI));
+                                cpr_free(pUser);
+                            } else {
+                                sstrncpy(reqURI, sipUriUrl->user, sizeof(reqURI));
+                            }
+                        }
+                    }
+                    sippmh_genurl_free(genUrl);
+                }
+            }
+            SIPPMH_FREE_REQUEST_LINE(requestURI);
+        }
+
+        for (i = 0; i < MAX_CCBS; i++) {
+            if (strcmp(callid, gGlobInfo.ccbs[i].sipCallID) == 0) {
+                ccb = &(gGlobInfo.ccbs[i]);
+                if (ccb->ReqURI[0] != '\0') {
+                    if (strcmp(ccb->ReqURI, reqURI) == 0) {
+                        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Matched reqURI\n",
+                            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+                        *ccb_ret = ccb;
+                        break;
+                    }
+                }
+            }
+        }
+        if (*ccb_ret == NULL) {
+            for (i = 0; i < MAX_CCBS; i++) {
+                if (strcmp(callid, gGlobInfo.ccbs[i].sipCallID) == 0) {
+                    ccb = &(gGlobInfo.ccbs[i]);
+                    if ((sipCseq->method == sipMethodInvite) &&
+                        (ccb->state < SIP_STATE_ACTIVE)) {
+                        // Return this CCB if we match call-id but have
+                        // not connected yet
+                        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Matched Call-id - not active.\n",
+                            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+                        *ccb_ret = ccb;
+                        break;
+                    }
+                    if (((sipCseq->method == sipMethodCancel) &&
+                        (ccb->state < SIP_STATE_ACTIVE)) ||
+                        ((sipCseq->method == sipMethodAck) &&
+                        (ccb->state == SIP_STATE_RELEASE))) {
+                        // For CANCEL, try and match the via branch-id with the
+                        // corresponding via branch-id of the INVITE received earlier.
+                        // Normally the R-URI of the CANCEL should match the stored
+                        // ReqURI but it is possible that the stored value was overwritten.
+                        // via_last is the via header from the previous INVITE
+                        // For ACK, we might be receiving this if there we responded with
+                        // an error to the initial INVITE
+                        trx_index = get_method_request_trx_index(ccb,
+                                                                 sipMethodInvite,
+                                                                 FALSE);
+
+                        if (trx_index != -1) {
+                            const char *toString;
+                            const char *fromString;
+
+                            toString = sippmh_get_cached_header_val(pSipMessage, TO);
+                            fromString = sippmh_get_cached_header_val(pSipMessage, FROM);
+
+                            trx = &(ccb->recv_request[trx_index]);
+
+                            if (trx->u.sip_via_header[0] != '\0') {
+                                pViaHeaderStr = (char *) (trx->u.sip_via_header);
+                                via_last = sippmh_parse_via(pViaHeaderStr);
+                            }
+
+                            if (fromString && toString) {
+                                if ((strcmp(ccb->sip_from, fromString) == 0) &&
+                                    (strncmp(ccb->sip_to, toString, strlen(toString)) == 0) &&
+                                    (trx->cseq_number == sipCseq->number) &&
+                                    (via_last && (via_last->branch_param != NULL)) &&
+                                    (via_this->branch_param != NULL) &&
+                                    (strcmp(via_last->branch_param, via_this->branch_param) == 0)) {
+                                    CCSIP_DEBUG_STATE(DEB_F_PREFIX"Matched branch_id & CSeq for CANCEL/ACK\n",
+                                                      DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+                                    *ccb_ret = ccb;
+                                    sippmh_free_via(via_last);
+                                    via_last = NULL;
+                                    break;
+                                }
+                            }
+                            if (via_last) {
+                                sippmh_free_via(via_last);
+                                via_last = NULL;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // If found, and this is a request, check for any existing trx in
+    // progress by matching with recd trx's cseq method and number. If
+    // these are the same and the branch-id is different, this is a merged
+    // request. Here via_last is the via header from the last receipt of the same request
+    if ((*ccb_ret != NULL) && is_request) {
+        ccb = *ccb_ret;
+        trx_index = get_method_request_trx_index(ccb, sipCseq->method, FALSE);
+        if (trx_index != -1) {
+            if (ccb->recv_request[trx_index].u.sip_via_header[0] != '\0') {
+                pViaHeaderStr = (char *) (ccb->recv_request[trx_index].u.sip_via_header);
+                via_last = sippmh_parse_via(pViaHeaderStr);
+            }
+            if (via_last) {
+                if (sipCseq->number == ccb->recv_request[trx_index].cseq_number) {
+                    if (via_this->branch_param && via_last->branch_param) {
+                        if (strcmp(via_this->branch_param,
+                                   via_last->branch_param)) {
+                            // merged request
+                            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Found Merged Request\n", fname);
+                            sippmh_free_via(via_this);
+                            sippmh_free_via(via_last);
+                            return (SIP_CLI_ERR_LOOP_DETECT);
+                        }
+                    }
+                }
+                sippmh_free_via(via_last);
+            }
+        }
+
+    }
+    // If this is a response, match it with a sent transaction in CCB
+    // by obtaining the CCB by call-id and then going through the
+    // transactions within it and matching branch-id and CSeq
+    if ((*ccb_ret == NULL) && !is_request) {
+
+        for (i = 0; i < MAX_CCBS; i++) {
+            if (strcmp(callid, gGlobInfo.ccbs[i].sipCallID) == 0) {
+                ccb = &(gGlobInfo.ccbs[i]);
+                match = sip_sm_ccb_match_branch_cseq(ccb, sipCseq,
+                                                     via_this);
+                sippmh_free_via(via_this);
+                if (match) {
+                     *ccb_ret = ccb;
+                     return (0);
+                 } else {
+                     return (SIP_CLI_ERR_NOT_ACCEPT);
+                 }
+            }
+        }
+        /*
+         * If ret_ccb is NULL after all this and the message is a
+         * response, check the fallback ccb list if any of those match
+         * the message call id.
+         */
+        sip_regmgr_find_fallback_ccb_by_callid(callid, ccb_ret);
+    }
+
+    /*
+     * If the CCB is not NULL and this is a response, then check
+     * if the branch and Cseq match.
+     */
+    if ((*ccb_ret != NULL) && !is_request) {
+       match = sip_sm_ccb_match_branch_cseq(*ccb_ret, sipCseq,
+                                             via_this);
+        sippmh_free_via(via_this);
+       if (match) {
+            return (0);
+        } else {
+            return (SIP_CLI_ERR_NOT_ACCEPT);
+        }
+    }
+
+    sippmh_free_via(via_this);
+    return (0);
+}
+
+void
+ccsip_handle_default (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "ccsip_handle_default";
+
+    CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d No action -> %s\n",
+                      DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                      ccb->index, sip_util_state2string(ccb->state));
+}
+
+
+void
+ccsip_handle_default_sip_message (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char   *fname = "default_sip_message";
+    sipMessage_t *msg = NULL;
+    int16_t       trx_index = -1;
+
+    msg = event->u.pSipMessage;
+
+    if (event->type == E_SIP_ACK) {
+        // If this is an ACK, make sure and clean out the corresponding INVITE
+        // transaction
+        clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+    } else if (event->type == E_SIP_INVITE) {
+        // If this is an INVITE and we are already processing an earlier
+        // INVITE return a 500 response
+        trx_index = get_method_request_trx_index(ccb, sipMethodInvite, FALSE);
+        if (trx_index != -1) {
+            (void) sipSPISendErrorResponse(msg, SIP_SERV_ERR_INTERNAL,
+                                           SIP_SERV_ERR_INTERNAL_PHRASE,
+                                           SIP_WARN_PROCESSING_PREVIOUS_REQUEST,
+                                           "Earlier INVITE being processed",
+                                           NULL);
+        }
+    } else if (event->type == E_SIP_UPDATE) {
+        // If this is an UPDATE and we are already processing an earlier
+        // UPDATE return a 500 response
+        trx_index = get_method_request_trx_index(ccb, sipMethodUpdate, FALSE);
+        if (trx_index != -1) {
+            (void) sipSPISendErrorResponse(msg, SIP_SERV_ERR_INTERNAL,
+                                           SIP_SERV_ERR_INTERNAL_PHRASE,
+                                           SIP_WARN_PROCESSING_PREVIOUS_REQUEST,
+                                           "Earlier UPDATE being processed",
+                                           NULL);
+        }
+
+    } else if (event->type == E_SIP_CANCEL) {
+        (void) sipSPISendErrorResponse(msg, SIP_CLI_ERR_CALLEG,
+                                       SIP_CLI_ERR_CALLEG_PHRASE, 0, NULL, ccb);
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: Sent 481 (CANCEL) %s\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                          ccb->index, sip_util_state2string(ccb->state));
+    }
+
+    /* Deallocate the incoming SIP message */
+    if (msg) {
+        free_sip_message(msg);
+    }
+
+    CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: No action -> %s\n",
+                      DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                      ccb->index, sip_util_state2string(ccb->state));
+}
+
+/**
+ * ccsip_handle_default_ev_cc_feature
+ *
+ * The function is a default function for handling CC_FEATURE in the
+ * state that does not expect CC_FEATURE.
+ *
+ * @param[in] ccb     Pointer to ccsipCCB_t structure.
+ * @param[in] event   Pointer to sipSMEvent_t structure.
+ *
+ * @pre               (ccb not_eq NULL)
+ *
+ * @return            N/A
+ *
+ */
+void
+ccsip_handle_default_ev_cc_feature (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "ccsip_handle_default_ev_cc_feature";
+    cc_features_t   feature_type;
+
+    feature_type = event->u.cc_msg->msg.feature.feature_id;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FEATURE_UNSUPPORTED),
+                      ccb->index, ccb->dn_line, fname);
+    /*
+     * The feature event is not supported by the current state or
+     * it is inappropriate event in the current state. Send feature ack
+     * with error code.
+     */
+    sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type, NULL,
+                       CC_CAUSE_ERROR);
+}
+
+/**
+ * ccsip_handle_default_recvreq_ack_pending_ev_cc_feature
+ *
+ * The function is a default handler for CC_FEATURE while in the
+ * received INVITE, UPDATE and the state machine is waiting for
+ * feature ack from GSM or SIP ack pending.
+ *
+ * @param[in] ccb     Pointer to ccsipCCB_t structure.
+ * @param[in] event   Pointer to sipSMEvent_t structure.
+ *
+ * @pre               (ccb not_eq NULL)
+ *
+ * @return            N/A
+ */
+void
+ccsip_handle_default_recvreq_ack_pending_ev_cc_feature (ccsipCCB_t *ccb,
+                                                        sipSMEvent_t *event)
+{
+    const char *fname =
+        "ccsip_handle_default_recvreq_ack_pending_ev_cc_feature";
+    cc_features_t feature_type;
+
+    feature_type = event->u.cc_msg->msg.feature_ack.feature_id;
+
+    switch (feature_type) {
+    case CC_FEATURE_RESUME:
+    case CC_FEATURE_HOLD:
+    case CC_FEATURE_MEDIA:
+        /*
+         * Received resume/hold/media request while waiting for feauture ack or
+         * SIP ack for hold that was received. Indicate that the request
+         * can not proceed now and it should be retried later.
+         */
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type,
+                           NULL, CC_CAUSE_REQUEST_PENDING);
+        break;
+
+    case CC_FEATURE_SELECT:
+    case CC_FEATURE_CANCEL:
+        break;
+    default:
+        /* Other feature request is not supported or allowed now */
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FEATURE_UNSUPPORTED),
+                          ccb->index, ccb->dn_line, fname);
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type,
+                           NULL, CC_CAUSE_ERROR);
+        break;
+    }
+}
+
+/*
+ *  Function: sip_is_releasing()
+ *
+ *  Parameters: ccb - The current call control block
+ *
+ *  Description: This routine supports determining whether the
+ *  the call is in releasing state or not.
+ *
+ *  Returns:
+ *     TRUE  - call is being released.
+ *     FALSE - call is not being released.
+ */
+boolean
+sip_is_releasing (ccsipCCB_t *ccb)
+{
+    if (ccb != NULL) {
+        /* Call exists */
+        if (ccb->state == SIP_STATE_RELEASE) {
+            return (TRUE);
+        }
+    }
+    return (FALSE);
+}
+
+void
+ccsip_handle_default_sip_response (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "default_sip_response";
+    sipMessage_t   *response;
+    int             response_code = 0;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+
+    /* Get the response code */
+    if (sipGetResponseCode(response, &response_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipGetResponseCode");
+        free_sip_message(response);
+        return;
+    }
+
+    /* Check if this is an INVITE response */
+    if ((!sip_sm_is_invite_response(response)) || (response_code < 200)) {
+        free_sip_message(response);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED), ccb->index,
+                          ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        return;
+    }
+
+    if (sipSPISendAck(ccb, response) == FALSE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipSPISendAck");
+    }
+    /* Deallocate the memory for the response */
+    free_sip_message(response);
+}
+
+/*
+ * ccsip_restart_reTx_timer
+ *
+ * This function is called when resending a message
+ * due to a timeout or ICMP unreachable. It just
+ * restarts the re-transmit timer.
+ */
+void
+ccsip_restart_reTx_timer (ccsipCCB_t *ccb, sipMethod_t messageType)
+{
+    const char     *fname = "ccsip_restart_reTx_timer";
+    uint32_t        time_t1 = 0;
+    uint32_t        time_t2 = 0;
+    uint32_t        timeout = 0;
+
+    /* Restart the reTx timer */
+    config_get_value(CFGID_TIMER_T1, &time_t1, sizeof(time_t1));
+    timeout = time_t1 * (1 << ccb->retx_counter);
+    // Adjust the max timer - but only for non INVITE transactions
+    if (messageType != sipMethodInvite) {
+        config_get_value(CFGID_TIMER_T2, &time_t2, sizeof(time_t2));
+        if (timeout > time_t2) {
+            timeout = time_t2;
+        }
+    }
+    CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: Restarting timer (%d msec)"
+                      " (msg is %s)\n",
+                                         DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->dn_line, ccb->gsm_id, fname),
+                                         ccb->index, timeout, sipGetMethodString(messageType));
+
+    ccb->retx_flag = TRUE;
+    if (sip_platform_msg_timer_start(timeout, (void *)((long)ccb->index), ccb->index,
+                                     sipPlatformUISMTimers[ccb->index].message_buffer,
+                                     sipPlatformUISMTimers[ccb->index].message_buffer_len,
+                                     sipPlatformUISMTimers[ccb->index].message_type,
+                                     &(sipPlatformUISMTimers[ccb->index].ipaddr),
+                                     sipPlatformUISMTimers[ccb->index].port,
+                                     FALSE) != SIP_OK) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                          ccb->dn_line, fname,
+                          "sip_platform_msg_timer_start()");
+        ccb->retx_flag = FALSE;
+        return;
+    }
+}
+
+/*
+ * ccsip_handle_obp_error()
+ *
+ * This function is called when a sip re-try timer
+ * pops or an ICMP unreachable is received after a
+ * msg is sent and the cause is the msg bounced or
+ * timed out trying to reach the outbound proxy.
+ *
+ * Data is the IP address that bounced or -1 if it was
+ * a timeout.
+ *
+ */
+void
+ccsip_handle_obp_error (ccsipCCB_t *ccb, sipMethod_t messageType, cpr_ip_addr_t *data)
+{
+    const char *fname = "ccsip_handle_obp_error";
+    boolean     resend = FALSE;
+    uint32_t    max_retx = 0;
+
+    char        obp_address[MAX_IPADDR_STR_LEN];
+
+    config_get_string(CFGID_OUTBOUND_PROXY, obp_address, sizeof(obp_address));
+    /* Did the msg bounce? */
+    if (util_compare_ip(data, &ccb->outBoundProxyAddr)) {
+        /* Try next proxy if one exists */
+        ccb->outBoundProxyPort = 0;
+        ccb->retx_counter = 0;
+        if (str2ip(obp_address, &ccb->outBoundProxyAddr) != 0) {
+            resend = TRUE;
+        }
+
+        /* Msg timed out */
+    } else {
+        if (messageType == sipMethodInvite) {
+            config_get_value(CFGID_SIP_INVITE_RETX, &max_retx, sizeof(max_retx));
+            if (max_retx > MAX_INVITE_RETRY_ATTEMPTS) {
+                max_retx = MAX_INVITE_RETRY_ATTEMPTS;
+            }
+        } else {
+            config_get_value(CFGID_SIP_RETX, &max_retx, sizeof(max_retx));
+            if (max_retx > MAX_NON_INVITE_RETRY_ATTEMPTS) {
+                max_retx = MAX_NON_INVITE_RETRY_ATTEMPTS;
+            }
+        }
+
+        /* Retries exhausted, get next proxy if one exists */
+        if (ccb->retx_counter >= max_retx) {
+            ccb->outBoundProxyPort = 0;
+            ccb->retx_counter = 0;
+            if (str2ip(obp_address, &ccb->outBoundProxyAddr) != 0) {
+                /* if the obp is just an ip addr, there is nothing else to try */
+                resend = TRUE;
+            }
+
+            /* Retries not exhausted */
+        } else {
+            resend = TRUE;
+        }
+    }
+
+    if (resend) {
+        if (sipSPISendLastMessage(ccb) == TRUE) {
+            ccb->retx_counter++;
+            CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d:  Resent message: #%d\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_MSG_SEND, ccb->dn_line, ccb->gsm_id,  fname),
+                              ccb->index, ccb->retx_counter);
+            ccsip_restart_reTx_timer(ccb, messageType);
+            if (ccb->state == SIP_STATE_RELEASE) {
+                (void) sip_platform_supervision_disconnect_timer_stop(ccb->index);
+            }
+        } else {
+            sip_platform_msg_timer_outstanding_set(ccb->index, FALSE);
+            if ((ccb->state == SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING) ||
+                (ccb->state == SIP_STATE_RELEASE)) {
+                sip_sm_change_state(ccb, SIP_STATE_IDLE);
+                sip_sm_call_cleanup(ccb);
+            } else {
+                sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+                sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            }
+        }
+    } else {
+        sip_platform_msg_timer_outstanding_set(ccb->index, FALSE);
+        sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+    }
+}
+
+int
+ccsip_pick_a_proxy (ccsipCCB_t *ccb)
+{
+    uint32_t     max_retx = 0;
+    sipMethod_t  retxMessageType = sipPlatformUISMTimers[ccb->index].message_type;
+    char         addr[MAX_IPADDR_STR_LEN];
+    const char  *fname = "ccsip_pick_a_proxy";
+
+    memset(addr, 0, sizeof(addr));
+
+    /* Get the type of the retransmitted message.
+     *  If it INVITE, then we need to use a different timer count
+     */
+    if (retxMessageType == sipMethodInvite) {
+        config_get_value(CFGID_SIP_INVITE_RETX, &max_retx, sizeof(max_retx));
+        if (max_retx > MAX_INVITE_RETRY_ATTEMPTS) {
+            max_retx = MAX_INVITE_RETRY_ATTEMPTS;
+        }
+        if (gGlobInfo.backup_active) {
+            if (ccb->proxySelection != SIP_PROXY_BACKUP) {
+                /*
+                 * If we have previously failed to contract the normal/regular
+                 * proxy then use a reduce re-try count for subsequent attempts
+                 * i.e failover to the backup proxy faster
+                 */
+                if (ccb->retx_counter >= 3) {
+                    ccb->retx_counter = max_retx;
+                }
+            }
+        }
+        /*
+         * Check if we have maxed out the  re-transmits on invite
+         * and not yet tried the backup proxy.
+         */
+        if ((ccb->retx_counter >= max_retx) &&
+            (ccb->proxySelection != SIP_PROXY_BACKUP) &&
+            (ccb->proxySelection != SIP_PROXY_DO_NOT_CHANGE_MIDCALL)) {
+            dns_error_code = DNS_ERR_HOST_UNAVAIL;
+            /* Try and fetch a proxy using DNS SRV records */
+            sipTransportGetPrimServerAddress(ccb->dn_line, addr);
+            if (str2ip(addr, &ccb->dest_sip_addr) != 0) {
+                dns_error_code = sip_dns_gethostbysrv(addr, &ccb->dest_sip_addr,
+                                                      (uint16_t *)&ccb->dest_sip_port,
+                                                      &ccb->SRVhandle, TRUE);
+                if (dns_error_code == DNS_OK) {
+                    util_ntohl(&(ccb->dest_sip_addr), &(ccb->dest_sip_addr));
+                    /*
+                     * Modify destination fields in call back timer struct
+                     */
+                    (void) sip_platform_msg_timer_update_destination(ccb->index,
+                                                                     &(ccb->dest_sip_addr),
+                                                                     (uint16_t) ccb->dest_sip_port);
+                    /*
+                     * Reset re-transmit counter so we try again
+                     */
+                    ccb->retx_counter = 0;
+
+                }
+            }
+            /*
+             * Either we are using DNS SRV records and we exhausted the list
+             * Or we are not using DNS SRV and we need to try the backup
+             */
+            if (dns_error_code != DNS_OK) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Unable to reach proxy, attempting backup.\n",
+                    DEB_F_PREFIX_ARGS(SIP_PROXY, fname));
+                if (ccsip_attempt_backup_proxy(ccb)) {
+
+                    ccb->first_backup = TRUE;
+                    /*
+                     * Get rid of transaction block for the ccb, we may have
+                     * tried primary proxy etc
+                     */
+                    clean_method_request_trx(ccb, sipMethodInvite, TRUE);
+
+                    /*
+                     * If contact info is supplied, as in the case of a 3xx
+                     * redirect and perhaps other cases TBD. And since calls
+                     * to the backup are essentially a "NEW" call so to speak,
+                     * nuke it and run.
+                     */
+                    if (ccb->contact_info) {
+                        sippmh_free_contact(ccb->contact_info);
+                        ccb->contact_info = NULL;
+                    }
+                    /* Ditto for record route */
+                    if (ccb->record_route_info) {
+                        sippmh_free_record_route(ccb->record_route_info);
+                        ccb->record_route_info = NULL;
+                    }
+
+                    /*
+                     * Recreate the SIP message, because the IP address has
+                     * changed. Ie. the reqURI has the old proxy IP address,
+                     * but now it should have the new backup proxy address
+                     * so we are setting boolean initInvite to TRUE. initInvite
+                     * will be used in sipSPIGenRequestURI to make reqURI with
+                     * new backup proxy address
+                     */
+                    if ((sipSPISendInvite(ccb,
+                                          ccb->wastransferred ? SIP_INVITE_TYPE_TRANSFER :
+                                          SIP_INVITE_TYPE_NORMAL, TRUE) != TRUE)) {
+                        sip_sm_call_cleanup(ccb);
+                        return FALSE;
+                    }
+
+                    /*
+                     * Reset counters so we try again using the backup proxy
+                     */
+                    ccb->retx_counter = 0;
+
+                    /* Backup proxy failed as well. Broadcast once */
+                } else {
+                    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Attempt to reach backup proxy"
+                                     " failed. Message will be broadcast.\n",
+                                     DEB_F_PREFIX_ARGS(SIP_PROXY, fname));
+                    return 1;
+                }
+            }
+        }
+    } else {
+        config_get_value(CFGID_SIP_RETX, &max_retx, sizeof(max_retx));
+        if (max_retx > MAX_NON_INVITE_RETRY_ATTEMPTS) {
+            max_retx = MAX_NON_INVITE_RETRY_ATTEMPTS;
+        }
+    }
+    return max_retx;
+}
+
+/*
+ * Function: ccsip_handle_icmp_unreachable:
+ * Description: This function handles ICMP events. For failed attempts
+ *              for requests (that get back an ICMP) this function checks
+ *              to see if we are in a dialog. If so, there is place holder
+ *              to follow through with DNS SRV (future work) and if all
+ *              DNS SRV calls have failed then it tears down the call.
+ *
+ *              If request was an out-of-dialog request, the code sets
+ *              the the ccb->retx_count to exceed max retries so it will
+ *              be treated as a failure to send the server (so retries to
+ *              the same server will not be attempted).
+ */
+void
+ccsip_handle_icmp_unreachable (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "ccsip_handle_icmp_unreachable";
+
+    /*
+     * 1. Check to see if we are in a dialog
+     *     1.1. If we are in a dialog and the fqdn was resolved by SRV,
+     *        then we must iterate to the next SRV result
+     *     1.2.  if we have no more servers from the SRV lookup,
+     *        we have to call it quits.
+     * 2. if we are not in a dialog, then map the icmp event to a timer event
+     *    but set the retry count to appropriate value so the timer handler can
+     *    pick up the next server rather than retry the same one.
+     */
+
+    if (ccb->sip_to_tag[0] != '\0') {
+        //we are in a dialog
+        // NOT USED: sipMessage_t *response;
+
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"ICMP received within a dialog.\n",
+            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+        /*
+         * At this point we should be checking to see if we picked up an
+         * fqdn that picked up from the route set was a SRV RR. If so,
+         * we must iterate to the next result returned.
+         *
+         * But we cannot do that now. Our SRV mechanism is incorrect.
+         * Firstly we must store SRV associations (handle to srv returns)
+         * in the sipTransaction_t object.
+         *
+         * This is future work.
+         *
+         * So for now we clobber the call.
+         */
+
+        ccb->wait_for_ack = FALSE;
+        sip_cc_release_complete(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL);
+        sip_sm_call_cleanup(ccb);
+    } else {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"ICMP received outside of a dialog.\n",
+            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+        //ccb->retx_counter = 100; //to cause failover to next proxy if one is available
+        ccsip_handle_default_sip_timer(ccb, event);
+    }
+}
+
+/*
+ * ccsip_handle_default_sip_timer()
+ *
+ * This function is called when a sip re-try timer
+ * pops or an ICMP unreachable is received after a
+ * msg is sent. The ICMP unreachable code sets
+ * retx_counter to MAX to force the code to try the
+ * next proxy in the list. Event.u.usrInfo is set to
+ * the IP address that bounced or -1 if it is a timeout.
+ *
+ * Note: Registration bounces and timeouts are handled
+ * by the function ccsip_handle_ev_tmr_retry.
+ */
+void
+ccsip_handle_default_sip_timer (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "ccsip_handle_default_sip_timer";
+    uint32_t    max_retx = 0;
+    sipMethod_t retxMessageType = sipPlatformUISMTimers[ccb->index].message_type;
+    char        addr[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t out_ip;
+
+    CPR_IP_ADDR_INIT(out_ip);
+
+    /* Timing issue on re-transmission and receiving an Ack.
+     * The timer could pop and we re-transmit and the Ack
+     * already be in the queue. When this happens, we process
+     * the Ack and go into the Active/Hold state. In the Active/Hold
+     * state, we don't expect an ack, so nothing happens and
+     * thus the new timer will eventually pop and start the
+     * whole process over. If we are in the Active/Hold state, we
+     * did receive the Ack. On the next time-out, just ignore it
+     * and don't re-start or re-transmit. The phone could be in
+     * the ACTIVE or HOLD states if a REFER has been sent with
+     * no response. Therefore ensure messagetype is not REFER
+     * before kicking out.
+     */
+    if ((retxMessageType != sipMethodRefer) &&
+        ((ccb->state == SIP_STATE_ACTIVE))) {
+        return;
+    }
+
+    /* OK, we got a timer pop, but the re-transmit flag
+     * isn't set. This means someone got a response that
+     * told them they could stop re-transmitting. Simply
+     * exit this function and do not re-start timer or
+     * re-transmit the message.
+     */
+    if (ccb->retx_flag == FALSE) {
+        if (ccb->state == SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING) {
+            sip_platform_msg_timer_outstanding_set(ccb->index, FALSE);
+            sip_sm_change_state(ccb, SIP_STATE_IDLE);
+            sip_sm_call_cleanup(ccb);
+        }
+        return;
+    }
+
+    /* Determine if msg to outbound proxy bounced or timed out */
+    util_ntohl(&out_ip, &event->u.UsrInfo);
+    if ((util_compare_ip(&out_ip, &(ccb->outBoundProxyAddr))) ||
+        ((event->u.UsrInfo.type == CPR_IP_ADDR_INVALID) &&
+            util_check_if_ip_valid(&(ccb->outBoundProxyAddr)))) {
+        ccsip_handle_obp_error(ccb, retxMessageType, &(event->u.UsrInfo));
+        return;
+    }
+
+    /* Determine if there is a proxy to send to */
+    max_retx = ccsip_pick_a_proxy(ccb);
+
+    /* Increment counter */
+    ccb->retx_counter++;
+
+    /*
+     * Our work is done if the backup proxy has just been activated.
+     * We know that is has just been activated if the first_backup flag is TRUE.
+     */
+    if ((ccb->proxySelection == SIP_PROXY_BACKUP) && (ccb->first_backup)) {
+        ccb->first_backup = FALSE;
+        return;
+    }
+
+    /* Resend */
+    if (ccb->retx_counter <= max_retx) {
+        if (sipSPISendLastMessage(ccb) == TRUE) {
+            CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d:Resent message: #%d\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_MSG_SEND, ccb->dn_line, ccb->gsm_id, fname),
+                              ccb->index, ccb->retx_counter);
+        }
+        if (ccb->state == SIP_STATE_RELEASE) {
+            (void) sip_platform_supervision_disconnect_timer_stop(ccb->index);
+        }
+        ccsip_restart_reTx_timer(ccb, retxMessageType);
+
+        /* All retransmit attempts have been exhausted */
+    } else {
+        ccb->retx_counter = 0;
+        sip_platform_msg_timer_outstanding_set(ccb->index, FALSE);
+
+        /* Check for redirection */
+        if (ccb->state == SIP_STATE_SENT_INVITE) {
+            if (ccb->redirect_info != NULL) {
+                sip_redirect(ccb, NULL, SIP_CLI_ERR_REQ_TIMEOUT);
+                return;
+            }
+        }
+        /*
+         * Resend msg to next proxy in the list if one exists
+         * Try and fetch another proxy using DNS SRV or A records
+         */
+        sipTransportGetPrimServerAddress(ccb->dn_line, addr);
+        if (str2ip(addr, &ccb->dest_sip_addr) != 0) {
+            dns_error_code = sipTransportGetServerAddrPort(addr,
+                                                           &ccb->dest_sip_addr,
+                                                           (uint16_t *)&ccb->dest_sip_port,
+                                                           &ccb->SRVhandle,
+                                                           TRUE);
+        } else {
+            /* This ip addr has already been tried */
+            dns_error_code = DNS_ERR_HOST_UNAVAIL;
+        }
+        if (dns_error_code == 0) {
+             util_ntohl(&(ccb->dest_sip_addr), &(ccb->dest_sip_addr));
+            /* Modify destination fields in call back timer */
+            (void) sip_platform_msg_timer_update_destination(ccb->index,
+                                                             &(ccb->dest_sip_addr),
+                                                             (uint16_t)ccb->dest_sip_port);
+            if (sipSPISendLastMessage(ccb) == TRUE) {
+                CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: Resent message: #%d\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_MSG_SEND, ccb->dn_line, ccb->gsm_id, fname),
+                                  ccb->index, ccb->retx_counter);
+            }
+            ccsip_restart_reTx_timer(ccb, retxMessageType);
+            if (ccb->state == SIP_STATE_RELEASE) {
+                (void) sip_platform_supervision_disconnect_timer_stop(ccb->index);
+            }
+        } else {
+            if ((ccb->state == SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING) ||
+                (ccb->state == SIP_STATE_RELEASE)) {
+                sip_sm_change_state(ccb, SIP_STATE_IDLE);
+                sip_sm_call_cleanup(ccb);
+                return;
+            } else if (retxMessageType == sipMethodRefer &&
+                       (ccb->featuretype == CC_FEATURE_B2BCONF ||
+                        ccb->featuretype == CC_FEATURE_SELECT ||
+                        ccb->featuretype == CC_FEATURE_B2B_JOIN ||
+                        ccb->featuretype == CC_FEATURE_CANCEL)) {
+                sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, ccb->featuretype,
+                                   NULL, CC_CAUSE_ERROR);
+                return;
+            } else {
+                handle_error_for_state(ccb, SIP_CLI_ERR_REQ_TIMEOUT);
+                return;
+            }
+        }
+    }
+
+    /* Restart re-transmit timer */
+    ccsip_restart_reTx_timer(ccb, retxMessageType);
+}
+
+
+void
+sip_decrement_backup_active_count (ccsipCCB_t *ccb)
+{
+    /*
+     *  OK If we successfully received a response we need to back off from using the backup
+     *  proxy if that was the case. Note if this is a response from the backup chuck it
+     */
+    if ((gGlobInfo.backup_active) && (ccb->proxySelection != SIP_PROXY_BACKUP))
+        gGlobInfo.backup_active -= 1;
+}
+
+boolean
+sip_sm_is_previous_call_id (const char *pCallID, line_t *pPreviousCallIndex)
+{
+    line_t i;
+
+    for (i = TEL_CCB_START; i <= TEL_CCB_END; i++) {
+        if (strcmp(gCallHistory[i].last_call_id, pCallID) == 0) {
+            *pPreviousCallIndex = i;
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+void
+sip_sm_200and300_update (ccsipCCB_t *ccb, sipMessage_t *response, int response_code)
+{
+    const char     *fname = "sip_sm_200and300_update";
+    const char     *to;
+    const char     *from;
+    const char     *contact;
+    const char     *record_route = NULL;
+    sipLocation_t  *to_loc = NULL;
+
+    to = sippmh_get_cached_header_val(response, TO);
+    from = sippmh_get_cached_header_val(response, FROM);
+    contact = sippmh_get_cached_header_val(response, CONTACT);
+
+    if (ccb->state < SIP_STATE_ACTIVE) {
+        // We are not allowed to update the route once a call is established
+        record_route = sippmh_get_cached_header_val(response, RECORD_ROUTE);
+    }
+    /*
+     * Record the "tag=" parameter - only if the call is not active yet
+     */
+    if (ccb->state < SIP_STATE_ACTIVE) {
+        if (to) {
+            to_loc = sippmh_parse_from_or_to((char *) to, TRUE);
+            if (to_loc) {
+                if (to_loc->tag) {
+                    ccb->sip_to_tag = strlib_update(ccb->sip_to_tag,
+                                                    sip_sm_purify_tag(to_loc->tag));
+                    if ( ccb->callref == 0 ) {
+                        ccb->callref = get_callref(ccb->sip_to_tag);
+                    }
+                } else {
+                    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                                      ccb->index, ccb->dn_line, fname,
+                                      "TO header:missing \"tag=\" param");
+                }
+                CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: Recorded to_tag=<%s>\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                                  ccb->index, ccb->sip_to_tag);
+                sippmh_free_location(to_loc);
+            }
+        }
+    }
+
+    /*
+     * Update To/From (to capture tag). Also Contact, and Record-Route
+     */
+    if (response_code == SIP_STATUS_SUCCESS) {
+        if (ccb->flags & INCOMING) {
+            ccb->sip_to = strlib_update(ccb->sip_to, from);
+            if (to) {
+                ccb->sip_from = strlib_update(ccb->sip_from, to);
+            }
+        } else {
+            if (to) {
+                ccb->sip_to = strlib_update(ccb->sip_to, to);
+            }
+            ccb->sip_from = strlib_update(ccb->sip_from, from);
+        }
+    }
+
+    if (response_code == SIP_STATUS_SUCCESS) {
+        if (contact) {
+            if (ccb->contact_info) {
+                sippmh_free_contact(ccb->contact_info);
+            }
+            ccb->contact_info = sippmh_parse_contact(contact);
+        }
+    }
+    if (record_route) {
+        if (ccb->record_route_info) {
+            sippmh_free_record_route(ccb->record_route_info);
+        }
+        ccb->record_route_info = sippmh_parse_record_route(record_route);
+    }
+}
+
+
+char *
+sip_sm_purify_tag (char *tag)
+{
+    char *p;
+
+    p = tag;
+    while (((*p == ';') || (*p == ' ') || (*p == '\t')) && (*p != '\0')) {
+        p++;
+    }
+
+    return p;
+}
+
+
+boolean
+sip_sm_is_invite_response (sipMessage_t *response)
+{
+    const char *cseq;
+    sipCseq_t  *sipCseq;
+
+    if (response == NULL) {
+        return FALSE;
+    }
+
+    cseq = sippmh_get_cached_header_val(response, CSEQ);
+    sipCseq = sippmh_parse_cseq(cseq);
+    if (!sipCseq) {
+        return FALSE;
+    }
+
+    if (sipCseq->method == sipMethodInvite) {
+        cpr_free(sipCseq);
+        return TRUE;
+    }
+    cpr_free(sipCseq);
+    return FALSE;
+}
+
+boolean
+sip_sm_is_bye_or_cancel_response (sipMessage_t *response)
+{
+    const char *cseq;
+    sipCseq_t  *sipCseq;
+
+    if (response == NULL) {
+        return FALSE;
+    }
+
+    cseq = sippmh_get_cached_header_val(response, CSEQ);
+    sipCseq = sippmh_parse_cseq(cseq);
+    if (!sipCseq) {
+        return FALSE;
+    }
+
+    if ((sipCseq->method == sipMethodBye) ||
+        (sipCseq->method == sipMethodCancel)) {
+        cpr_free(sipCseq);
+        return TRUE;
+    }
+    cpr_free(sipCseq);
+    return FALSE;
+}
+
+
+
+void
+sip_sm_dequote_string (char *str, int max_size)
+{
+    char *p;
+
+    /* Get rid of leading white space and double quote */
+    p = str;
+    while (((*p == '\"') || (*p == ' ') || (*p == '\t')) && (*p != '\0')) {
+        p++;
+    }
+
+    // The following use of sstrncpy is using over-lapping memory regions
+    sstrncpy(str, p, max_size);
+
+    /* Get rid of trailing double quote and white space */
+    // should be...
+    // end the string at the trailing double quote, if it exists
+    p = str;
+    while ((*p != '\"') && (*p != '\0')) {
+        p++;
+    }
+    *p = '\0';
+}
+
+
+void
+sip_sm_check_retx_timers (ccsipCCB_t *ccb, sipMessage_t *canceller_message)
+{
+    const char     *fname = "sip_sm_check_retx_timers";
+    uint32_t        canceller_cseq;
+    sipMethod_t     canceller_cseq_method;
+    const char     *canceller_callid;
+
+    sipMessage_t   *retx_message = NULL;
+    uint32_t        retx_message_buf_length = 0;
+    uint32_t        retx_cseq = 0;
+    sipMethod_t     retx_cseq_method = sipMethodInvalid;
+    const char     *retx_callid = NULL;
+
+    const char     *cseq;
+    sipCseq_t      *sipCseq;
+    int             response_code = -1;
+    const char     *conn_type = NULL;
+
+    if (!ccb) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "CCB is NULL");
+        return;
+    }
+
+    if (ccb->index >= MAX_CCBS) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_LINE_NUMBER_INVALID),
+                          fname, ccb->index);
+        return;
+    }
+
+    /* In the case of TCP/TLS, no messages are retransmitted unless there was a
+     * socket open (CPR_ENOTCONN) error. Hence there are no retx timers started
+     * in this case. If any retx timers were started, then, in the
+     * sip_platform_msg_timer_start procedure, the message_buffer_len would be
+     * set to a valid non-zero value.
+     */
+    conn_type = sipTransportGetTransportType(1, TRUE, ccb);
+    if ((!cpr_strcasecmp(conn_type, "TCP") || !cpr_strcasecmp(conn_type, "TLS")) &&
+        0 == sipPlatformUISMTimers[ccb->index].message_buffer_len) {
+        /* This is not an error in case of TCP/TLS.
+         */
+        return;
+    }
+
+
+    /*
+     * Get canceller_message callid, cseq number, cseq method
+     */
+    cseq = sippmh_get_cached_header_val(canceller_message, CSEQ);
+    sipCseq = sippmh_parse_cseq(cseq);
+    if (sipCseq) {
+        canceller_cseq = sipCseq->number;
+        canceller_cseq_method = sipCseq->method;
+        cpr_free(sipCseq);
+    } else {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          "sippmh_parse_cseq()");
+        return;
+    }
+
+    canceller_callid = sippmh_get_cached_header_val(canceller_message, CALLID);
+
+    /*
+     * Get canceller_message response code if it is a response
+     */
+    if (!sippmh_is_request(canceller_message)) {
+        if (sipGetResponseCode(canceller_message, &response_code) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                              ccb->dn_line, fname, "sipGetResponseCode()");
+            return;
+        }
+    }
+
+    /*
+     * Get retx_message callid, cseq number, cseq method
+     */
+    retx_message = sippmh_message_create();
+    if (!retx_message) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                          ccb->dn_line, fname, "sippmh_message_create()");
+        return;
+    }
+    retx_message_buf_length = sipPlatformUISMTimers[ccb->index].message_buffer_len;
+    if (sippmh_process_network_message(retx_message,
+                                       sipPlatformUISMTimers[ccb->index].message_buffer,
+                                       &retx_message_buf_length) == STATUS_FAILURE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                          ccb->dn_line, fname,
+                          "sippmh_process_network_message()");
+        free_sip_message(retx_message);
+        return;
+    }
+    if (!sippmh_is_message_complete(retx_message)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                          ccb->dn_line, fname, "sippmh_is_message_complete()");
+        free_sip_message(retx_message);
+        return;
+    }
+    cseq = sippmh_get_cached_header_val(retx_message, CSEQ);
+    sipCseq = sippmh_parse_cseq(cseq);
+    if (sipCseq) {
+        retx_cseq = sipCseq->number;
+        retx_cseq_method = sipCseq->method;
+        cpr_free(sipCseq);
+    } else {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          "sippmh_parse_cseq()");
+        free_sip_message(retx_message);
+        return;
+    }
+
+    retx_callid = sippmh_get_cached_header_val(retx_message, CALLID);
+
+    /*
+     * Check whether callid, cseq number, cseq method match.  If match,
+     * stop the reTx timer.
+     */
+    if ((canceller_cseq == retx_cseq) &&
+        ((canceller_cseq_method == retx_cseq_method) ||
+         ((canceller_cseq_method == sipMethodAck) &&
+          (retx_cseq_method == sipMethodInvite))) &&
+          (strcmp(canceller_callid, retx_callid) == 0)) {
+        sip_platform_msg_timer_stop(ccb->index);
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: Stopping reTx timer.\n"
+                          "(callid=%s, cseq=%u, cseq_method=%s)\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->dn_line, ccb->gsm_id, fname),
+                          ccb->index, retx_callid, retx_cseq,
+                          sipGetMethodString(retx_cseq_method));
+
+        UNBIND_UDP_ICMP_HANDLER(ccb->udpId);
+
+        if (ccb->state == SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING) {
+            if ((response_code == 401) || (response_code == 407) ||
+                ((response_code < 200) &&
+                (!sippmh_is_request(canceller_message)))) {
+                CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED), ccb->index,
+                                  ccb->dn_line, fname,
+                                  sip_util_state2string(ccb->state));
+            } else {
+                sip_sm_change_state(ccb, SIP_STATE_IDLE);
+                sip_sm_call_cleanup(ccb);
+            }
+        }
+    } else {
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d:CSeq mismatch:\n(Rx: callid=%s,"
+                          " cseq=%u, cseq_method=%s),\n(reTx: callid=%s,"
+                          " cseq=%u, cseq_method=%s)\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                                                 ccb->index,
+                          canceller_callid, canceller_cseq,
+                          sipGetMethodString(canceller_cseq_method),
+                          retx_callid, retx_cseq,
+                          sipGetMethodString(retx_cseq_method));
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                          ccb->index, ccb->dn_line, fname,
+                          "Not stopping retx timer");
+    }
+
+    free_sip_message(retx_message);
+}
+
+
+static int
+sip_sm_request_check_and_store (ccsipCCB_t *ccb, sipMessage_t *request,
+                                sipMethod_t request_method, boolean midcall,
+                                uint16_t *request_check_reason_code,
+                                char *request_check_reason_phrase,
+                                boolean store_invite)
+{
+    const char     *fname = "sip_sm_request_check_and_store";
+    const char     *request_cseq = NULL;
+    sipCseq_t      *request_cseq_structure = NULL;
+    uint32_t        request_cseq_number = 0;
+    sipMethod_t     request_cseq_method = sipMethodInvalid;
+    const char     *callID = NULL;
+    int             content_length = 0;
+    boolean         request_uri_error = FALSE;
+    sipReqLine_t   *requestURI = NULL;
+    sipLocation_t  *uri_loc = NULL;
+    const char     *sip_from = NULL;
+    const char     *sip_to = NULL;
+    sipLocation_t  *to_loc = NULL;
+    sipLocation_t  *from_loc = NULL;
+    const char     *pViaHeaderStr = NULL;
+    int16_t         trx_index = -1;
+    sipVia_t       *via = NULL;
+
+
+    /* test incoming parameter for NULL */
+    if (!request_check_reason_phrase) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Input parameter request_check_reason_phrase is NULL\n",
+                          fname);
+        return (-1);
+    }
+
+    /*
+     * Parse Call-Id
+     */
+    callID = sippmh_get_cached_header_val(request, CALLID);
+    if (!callID) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to obtain request's "
+                          "Call-ID header.\n", fname);
+        *request_check_reason_code = SIP_WARN_MISC;
+        sstrncpy(request_check_reason_phrase,
+                SIP_CLI_ERR_BAD_REQ_CALLID_ABSENT,
+                SIP_WARNING_LENGTH);
+        return (-1);
+    }
+
+    /*
+     * Parse CSeq
+     */
+    request_cseq = sippmh_get_cached_header_val(request, CSEQ);
+    if (!request_cseq) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to obtain request's CSeq "
+                          "header.\n", fname);
+        *request_check_reason_code = SIP_WARN_MISC;
+        sstrncpy(request_check_reason_phrase,
+                SIP_CLI_ERR_BAD_REQ_VIA_OR_CSEQ,
+                SIP_WARNING_LENGTH);
+        return (-1);
+    }
+    request_cseq_structure = sippmh_parse_cseq(request_cseq);
+    if (!request_cseq_structure) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to parse request's CSeq "
+                          "header.\n", fname);
+        *request_check_reason_code = SIP_WARN_MISC;
+        sstrncpy(request_check_reason_phrase,
+                SIP_CLI_ERR_BAD_REQ_VIA_OR_CSEQ,
+                SIP_WARNING_LENGTH);
+        return (-1);
+    }
+    request_cseq_number = request_cseq_structure->number;
+    request_cseq_method = request_cseq_structure->method;
+    cpr_free(request_cseq_structure);
+
+    /*
+     * Parsing Request-Uri
+     */
+    requestURI = sippmh_get_request_line(request);
+    if (requestURI) {
+        if (requestURI->url) {
+            uri_loc = sippmh_parse_from_or_to(requestURI->url, TRUE);
+            if (uri_loc) {
+                if (uri_loc->genUrl->schema != URL_TYPE_SIP) {
+                    request_uri_error = TRUE;
+                }
+                sippmh_free_location(uri_loc);
+            } else {
+                request_uri_error = TRUE;
+            }
+        } else {
+            request_uri_error = TRUE;
+        }
+        SIPPMH_FREE_REQUEST_LINE(requestURI);
+    } else {
+        request_uri_error = TRUE;
+    }
+    if (request_uri_error) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Invalid Request URI"
+                          "failed.\n", fname);
+        *request_check_reason_code = SIP_WARN_MISC;
+        sstrncpy(request_check_reason_phrase,
+                SIP_CLI_ERR_BAD_REQ_REQLINE_ERROR,
+                SIP_WARNING_LENGTH);
+        return (-1);
+    }
+
+    /*
+     * Parse From
+     */
+    sip_from = sippmh_get_cached_header_val(request, FROM);
+    from_loc = sippmh_parse_from_or_to((char *)sip_from, TRUE);
+    if (!from_loc) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_FROM));
+        *request_check_reason_code = SIP_WARN_MISC;
+        sstrncpy(request_check_reason_phrase,
+                SIP_CLI_ERR_BAD_REQ_FROMURL_ERROR,
+                SIP_WARNING_LENGTH);
+        return (-1);
+    }
+    sippmh_free_location(from_loc);
+
+    /*
+     * Parse To
+     */
+    sip_to = sippmh_get_cached_header_val(request, TO);
+    to_loc = sippmh_parse_from_or_to((char *)sip_to, TRUE);
+    if (!to_loc) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO));
+        *request_check_reason_code = SIP_WARN_MISC;
+        sstrncpy(request_check_reason_phrase,
+                SIP_CLI_ERR_BAD_REQ_ToURL_ERROR,
+                SIP_WARNING_LENGTH);
+        return (-1);
+    }
+    sippmh_free_location(to_loc);
+
+    /*
+     * Parse Via
+     */
+    pViaHeaderStr = sippmh_get_cached_header_val(request, VIA);
+    if (pViaHeaderStr) {
+        via = sippmh_parse_via(pViaHeaderStr);
+    }
+    if (!pViaHeaderStr || !via) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), NULL,
+                          NULL, fname, "sippmh_parse_via");
+        *request_check_reason_code = SIP_WARN_MISC;
+        sstrncpy(request_check_reason_phrase,
+                SIP_CLI_ERR_BAD_REQ_VIA_OR_CSEQ,
+                SIP_WARNING_LENGTH);
+        return (-1);
+
+    }
+    sippmh_free_via(via);
+
+    /*
+     * Check
+     */
+    switch (request_method) {
+        /* INVITE */
+    case sipMethodInvite:
+
+        content_length = sippmh_get_content_length(request);
+
+        if (request->raw_body) {
+
+            if ((size_t) content_length != strlen(request->raw_body)) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"\n Mismatched Content length and "
+                                  "Actual message body length:content length=%d\n"
+                                  "and message as %s\n and strlen of messagebody = %d\n",
+                                  fname, content_length, request->raw_body,
+                                  strlen(request->raw_body));
+                *request_check_reason_code = SIP_WARN_MISC;
+                sstrncpy(request_check_reason_phrase,
+                        SIP_CLI_ERR_BAD_REQ_CONTENT_LENGTH_ERROR,
+                        SIP_WARNING_LENGTH);
+                return (-1);
+            }
+        } else {
+            if ((size_t) content_length != 0) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"\n Mismatched Content length and "
+                                  "Actual message body length:content length=%d\n"
+                                  "and message is empty and strlen of messagebody = 0\n",
+                                  fname, content_length);
+                *request_check_reason_code = SIP_WARN_MISC;
+                sstrncpy(request_check_reason_phrase,
+                        SIP_CLI_ERR_BAD_REQ_CONTENT_LENGTH_ERROR,
+                        SIP_WARNING_LENGTH);
+                return (-1);
+            }
+        }
+        if (midcall &&
+            ((ccb->last_recv_request_cseq_method == sipMethodInvite) ||
+             (ccb->last_recv_request_cseq_method == sipMethodAck))) {
+            if (request_cseq_number <= ccb->last_recv_invite_cseq) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error: CSeq (%d) is not greater "
+                                  "than previous INVITE (%d).\n", fname,
+                                  request_cseq_number,
+                                  ccb->last_recv_invite_cseq);
+                *request_check_reason_code = SIP_WARN_MISC;
+                sstrncpy(request_check_reason_phrase,
+                        SIP_CLI_ERR_BAD_REQ_CSEQ_FIELD,
+                        SIP_WARNING_LENGTH);
+                return (-1);
+            }
+        }
+        ccb->last_recv_invite_cseq = request_cseq_number;
+        break;
+
+        /* BYE */
+    case sipMethodBye:
+        if (ccb->flags & INCOMING) {
+            if (request_cseq_number <= ccb->last_recv_invite_cseq) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error: CSeq (%d) is not greater "
+                                  "than original INVITE (%d).\n", fname,
+                                  request_cseq_number,
+                                  ccb->last_recv_invite_cseq);
+                *request_check_reason_code = SIP_WARN_MISC;
+                sstrncpy(request_check_reason_phrase,
+                        SIP_CLI_ERR_BAD_REQ_CSEQ_FIELD,
+                        SIP_WARNING_LENGTH);
+                return (-1);
+            }
+        }
+        break;
+
+        /* ACK and CANCEL */
+    case sipMethodCancel:
+    case sipMethodAck:
+        if (request_cseq_number != ccb->last_recv_invite_cseq) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"CSeq (%d) is not the same as "
+                              "original INVITE (%d).\n", fname,
+                              request_cseq_number, ccb->last_recv_request_cseq);
+            *request_check_reason_code = SIP_WARN_MISC;
+            sstrncpy(request_check_reason_phrase,
+                    SIP_CLI_ERR_BAD_REQ_CSEQ_FIELD,
+                    SIP_WARNING_LENGTH);
+            return (-1);
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    // Allocate a tx block and store the various params there
+    trx_index = get_next_request_trx_index(ccb, FALSE);
+    if (trx_index < 0) {
+        // Internal server error
+        *request_check_reason_code = SIP_WARN_MISC;
+        sstrncpy(request_check_reason_phrase,
+                "Too many Transactions",
+                SIP_WARNING_LENGTH);
+        return (-2);
+    }
+    /*
+     * Don't free it since this request will be again required
+     * for sending 487 response. This is a temporary solution
+     * and should be removed in Moonpie
+     */
+    if (!store_invite) {
+        if (ccb->last_request) {
+            free_sip_message(ccb->last_request);
+            ccb->last_request = NULL;
+        }
+    }
+    /* Store request.Done after alloc of trx, to handle allocation failure. */
+    ccb->last_request = request;
+
+    ccb->last_recv_request_cseq = request_cseq_number;
+    ccb->last_recv_request_cseq_method = request_cseq_method;
+    ccb->recv_request[trx_index].cseq_number = request_cseq_number;
+    ccb->recv_request[trx_index].cseq_method = request_cseq_method;
+    pViaHeaderStr = sippmh_get_cached_header_val(request, VIA);
+    if (pViaHeaderStr) {
+        ccb->recv_request[trx_index].u.sip_via_header =
+            strlib_update(ccb->recv_request[trx_index].u.sip_via_header,
+                          pViaHeaderStr);
+    }
+
+    return (0);
+}
+
+
+/*
+ *  Function: sip_sm_update_to_from_on_callsetup_finalresponse
+ *
+ *  Parameters: ccb, response
+ *
+ *  Description:  Updates to and from from response
+ *
+ *  Returns:Void
+ *
+ */
+void
+sip_sm_update_to_from_on_callsetup_finalresponse (ccsipCCB_t *ccb,
+                                                  sipMessage_t *response)
+{
+    const char     *fname = "sip_sm_update_to_from_on_callsetup_finalresponse";
+    const char     *to;
+    const char     *from;
+    sipLocation_t  *to_loc = NULL;
+
+    to = sippmh_get_cached_header_val(response, TO);
+    from = sippmh_get_cached_header_val(response, FROM);
+
+    /*
+     * Record the "tag=" parameter
+     */
+    if (to) {
+        to_loc = sippmh_parse_from_or_to((char *)to, TRUE);
+        if (to_loc) {
+            if (to_loc->tag) {
+                ccb->sip_to_tag = strlib_update(ccb->sip_to_tag, sip_sm_purify_tag(to_loc->tag));
+            } else {
+                CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                                  ccb->index, ccb->dn_line, fname,
+                                  "TO header missing \"tag=\" param");
+                /* ccb->sip_to_tag[0] = '\0'; */
+            }
+            CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d: Recorded to_tag=<%s>\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                              ccb->index, ccb->sip_to_tag);
+            sippmh_free_location(to_loc);
+        }
+    }
+
+    /*
+     * Update To/From (to capture tag). Also Contact, and Record-Route
+     */
+    if (to) {
+        ccb->sip_to = strlib_update(ccb->sip_to, to);
+    }
+    ccb->sip_from = strlib_update(ccb->sip_from, from);
+}
+
+
+/*
+ *  Function: sip_sm_update_contact_recordroute
+ *
+ *  Parameters:ccb, response , response_code, midcall boolean
+ *
+ *  Description: Puts contact info and record_route info into ccb
+ *
+ *  Returns:void
+ *
+ */
+void
+sip_sm_update_contact_recordroute (ccsipCCB_t *ccb, sipMessage_t *response,
+                                   int response_code, boolean midcall)
+{
+    const char *contact;
+    const char *record_route;
+
+    contact = sippmh_get_cached_header_val(response, CONTACT);
+    record_route = sippmh_get_cached_header_val(response, RECORD_ROUTE);
+
+    if (response_code == SIP_STATUS_SUCCESS) {
+        if (contact) {
+            if (ccb->contact_info) {
+                sippmh_free_contact(ccb->contact_info);
+            }
+            ccb->contact_info = sippmh_parse_contact(contact);
+        }
+    }
+    if (record_route && (!midcall)) {
+        if (ccb->record_route_info) {
+            sippmh_free_record_route(ccb->record_route_info);
+        }
+        ccb->record_route_info = sippmh_parse_record_route(record_route);
+    }
+}
+
+
+/*
+ * SIP_STATE_ACTIVE
+ */
+void
+ccsip_handle_active_ev_cc_feature (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "active_ev_cc_feature";
+    cc_features_t   feature_type;
+
+    feature_type = event->u.cc_msg->msg.feature.feature_id;
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY),
+                      ccb->index, ccb->dn_line, fname,
+                      sip_util_state2string(ccb->state),
+                      cc_feature_name(feature_type));
+
+    switch (feature_type) {
+    case CC_FEATURE_HOLD:
+        ccb->hold_initiated = TRUE;
+        ccb->featuretype = CC_FEATURE_HOLD;
+        ccsip_handle_active_ev_cc_feature_hold(ccb, event);
+        break;
+    case CC_FEATURE_MEDIA:
+        ccb->featuretype = CC_FEATURE_MEDIA;
+        ccsip_handle_active_ev_cc_feature_resume_or_media(ccb, event);
+        break;
+    case CC_FEATURE_RESUME:
+        ccb->featuretype = CC_FEATURE_RESUME;
+        ccsip_handle_active_ev_cc_feature_resume_or_media(ccb, event);
+        break;
+    case CC_FEATURE_BLIND_XFER:
+    case CC_FEATURE_XFER:
+        ccsip_handle_active_ev_cc_feature_xfer(ccb, event);
+        break;
+    case CC_FEATURE_NOTIFY:
+        if (event->u.cc_msg->msg.feature.data.notify.final == TRUE) {
+            ccb->flags |= FINAL_NOTIFY;
+        }
+        if (CC_CAUSE_OK != event->u.cc_msg->msg.feature.data.notify.cause) {
+            // Get the data from msg.get data for error code but currently we do not have any.
+            (void) sipSPISendNotify(ccb, event->u.cc_msg->msg.feature.data.notify.cause_code);
+            ccb->xfer_status = event->u.cc_msg->msg.feature.data.notify.cause_code;
+        } else {
+            (void) sipSPISendNotify(ccb, SIP_SUCCESS_SETUP); // Get the data from msg.
+            ccb->xfer_status = SIP_SUCCESS_SETUP;
+        }
+        break;
+    case CC_FEATURE_B2BCONF:
+    case CC_FEATURE_SELECT:
+    case CC_FEATURE_B2B_JOIN:
+    case CC_FEATURE_CANCEL:
+        break;
+    default:
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FEATURE_UNSUPPORTED),
+                          ccb->index, ccb->dn_line, fname);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type, NULL,
+                           CC_CAUSE_ERROR);
+        break;
+    }
+}
+
+
+/**
+ * This fucntion handles CC_FEATURE_HOLD in active state.
+ *
+ * @param[in] ccb      Pointer to ccsipCCB_t structure.
+ * @param[in] event    Pointer to sipSMEvent_t structure.
+ *
+ * @pre               (ccb      not_eq NULL)
+ *
+ * @return            None.
+ *
+ */
+void
+ccsip_handle_active_ev_cc_feature_hold (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    cc_msgbody_info_t *msg_body;
+
+    /* Copy the call-info into the CCB */
+    ccsip_store_call_info(&event->u.cc_msg->msg.feature.data.hold.call_info, ccb);
+    if (event->u.cc_msg->msg.feature.data_valid) {
+        /* Replace the local copy of the msg. body */
+        msg_body = &event->u.cc_msg->msg.feature.data.hold.msg_body;
+        ccsip_save_local_msg_body(ccb, msg_body);
+    }
+
+    sip_sm_change_state(ccb, SIP_STATE_SENT_MIDCALL_INVITE);
+    if (send_resume_or_hold_request(ccb, TRUE) == FALSE) {
+        sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+    }
+}
+
+
+/**
+ * This fucntion handles CC_FEATURE_RESUME or CC_FEATURE_MEDIA in
+ * active state.
+ *
+ * @param[in] ccb      Pointer to ccsipCCB_t structure.
+ * @param[in] event    Pointer to sipSMEvent_t structure.
+ *
+ * @pre               (ccb      not_eq NULL)
+ *
+ * @return            None.
+ *
+ */
+void
+ccsip_handle_active_ev_cc_feature_resume_or_media (ccsipCCB_t *ccb,
+                                                   sipSMEvent_t *event)
+{
+    cc_msgbody_info_t *msg_body;
+
+    if (event->u.cc_msg->msg.feature.data_valid) {
+        /* Replace the local copy of the msg. body */
+        msg_body = &event->u.cc_msg->msg.feature.data.resume.msg_body;
+        ccsip_save_local_msg_body(ccb, msg_body);
+    }
+
+    /* Copy the call-info into the CCB */
+    ccsip_store_call_info(&event->u.cc_msg->msg.feature.data.resume.call_info, ccb);
+    sip_sm_change_state(ccb, SIP_STATE_SENT_MIDCALL_INVITE);
+    if (send_resume_or_hold_request(ccb, FALSE) == FALSE) {
+        sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+    }
+}
+
+void
+ccsip_handle_active_ev_cc_feature_xfer (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "active_ev_cc_feature_xfer";
+    char            referto[MAX_SIP_URL_LENGTH];
+    char           *domainloc = NULL;
+    char           *pTransferNumberString;
+    cc_feature_data_t data;
+    cc_xfer_methods_t method;
+    char            addr[MAX_IPADDR_STR_LEN];
+    ccsipCCB_t     *xfer_ccb = NULL;
+    int             n = 0;
+    static char     dialtranslate[MAX_SIP_URL_LENGTH];
+    char           *pReferToStr = referto;
+    sipRefEnum_e   refto_type = SIP_REF_XFER;
+
+    memset(addr, 0, MAX_IPADDR_STR_LEN);
+    method = event->u.cc_msg->msg.feature.data.xfer.method;
+
+    if (CC_FEATURE_BLIND_XFER == event->u.cc_msg->msg.feature.feature_id) {
+        ccb->featuretype = CC_FEATURE_BLIND_XFER;
+        ccb->con_call_id = CC_NO_CALL_ID;
+    } else if (CC_FEATURE_XFER == event->u.cc_msg->msg.feature.feature_id) {
+        ccb->featuretype = CC_FEATURE_XFER;
+        ccb->con_call_id = event->u.cc_msg->msg.feature.data.xfer.target_call_id;
+        xfer_ccb = sip_sm_get_ccb_by_target_call_id(ccb->con_call_id);
+    }
+
+
+    pTransferNumberString = event->u.cc_msg->msg.feature.data.xfer.dialstring;
+    (void) MatchDialTemplate(pTransferNumberString, ccb->dn_line, CAST_N &n,
+                             dialtranslate, sizeof(dialtranslate),
+                             (RouteMode *) &(ccb->routeMode), NULL);
+    /* Escape the characters in userinfo */
+    domainloc = strchr(dialtranslate, '@');
+    if (domainloc == NULL) {
+        (void) sippmh_convertURLCharToEscChar(dialtranslate,
+                                              strlen(dialtranslate),
+                                              pReferToStr, MAX_SIP_URL_LENGTH, TRUE);
+    } else {
+        (void) sippmh_convertURLCharToEscChar(dialtranslate,
+                                              domainloc - dialtranslate,
+                                              pReferToStr, MAX_SIP_URL_LENGTH, TRUE);
+        /* Append the host Part including @ */
+        sstrncat(pReferToStr, domainloc, MAX_SIP_URL_LENGTH - strlen(pReferToStr));
+    }
+    /* Re-write the escaped URL to dial translate */
+    sstrncpy(dialtranslate, referto, MAX_SIP_URL_LENGTH);
+    pTransferNumberString = dialtranslate;
+
+
+    ccb->authen.cred_type = 0;
+    //add refer as SIP mURI
+
+    // If there is no hostname, add proxy address as hostname
+    domainloc = strchr(referto, '@');
+    if (domainloc == NULL) {
+        char *semi = NULL;
+        size_t len;
+
+        /* see if we have ;user= */
+        semi = strchr(pTransferNumberString, ';');
+
+        if (semi) {
+            sstrncpy(referto, "<sip:", MAX_SIP_URL_LENGTH);
+            len = semi - pTransferNumberString;
+        } else {
+            sstrncpy(referto, "sip:", MAX_SIP_URL_LENGTH);
+            len = MAX_SIP_URL_LENGTH - sizeof("sip:");
+        }
+
+        /* if we have a ;user=, only copy up until that point */
+        sstrncat(referto, pTransferNumberString, len);
+
+        domainloc = referto + strlen(referto);
+        if ((domainloc - referto) < (MAX_SIP_URL_LENGTH - 1)) {
+            /*
+             * We need to check and see if we are already truncating a
+             * string string that goes into ReqURI.  If we are, then we
+             * CANNOT add any more characters without overwriting memory
+             */
+            *domainloc++ = '@';
+            xfer_ccb = sip_sm_get_ccb_by_target_call_id(ccb->con_call_id);
+            if ((xfer_ccb != NULL) &&
+                util_check_if_ip_valid(&(xfer_ccb->dest_sip_addr)) &&
+                (ccb->featuretype == CC_FEATURE_XFER)) {
+                /*
+                 * Populate addr with proxy through which transferor and
+                 * target are talking
+                 */
+                if ((xfer_ccb->routeMode == RouteEmergency) ||
+                    (xfer_ccb->proxySelection == SIP_PROXY_BACKUP)) {
+                    ipaddr2dotted(addr, &xfer_ccb->dest_sip_addr);
+                } else {
+                    sipTransportGetPrimServerAddress(xfer_ccb->dn_line, addr);
+                }
+            } else {
+                /*
+                 * In case of blind transfer xfer_ccb->dest_sip_addr will
+                 * always show primary proxy. Since we don't know whether
+                 * target of transfer is reachable by primary proxy or not,
+                 * We are going to use proxy through which transferor is
+                 * talking to transferee, assuming that transferee and target
+                 * should be able to talk through the same proxy
+                 */
+                if ((ccb->routeMode == RouteEmergency) ||
+                    (ccb->proxySelection == SIP_PROXY_BACKUP)) {
+                    ipaddr2dotted(addr, &ccb->dest_sip_addr);
+                } else {
+                    sipTransportGetPrimServerAddress(ccb->dn_line, addr);
+                }
+            }
+
+            sstrncpy(domainloc, addr,
+                     MAX_SIP_URL_LENGTH - (domainloc - referto - 1));
+        }
+
+        /* if we have a ;user=, then add it to the end of the string if we can */
+        if (semi) {
+            domainloc = referto + strlen(referto);
+            sstrncpy(domainloc, semi,
+                     MAX_SIP_URL_LENGTH - (domainloc - referto - 1));
+        }
+
+        if (semi) {
+            sstrncat(domainloc, ">", MAX_SIP_URL_LENGTH - strlen(referto));
+        }
+    }
+    /* If the method is direct trasnfer then
+     * add more information to referto header
+     */
+    if (method == CC_XFER_METHOD_DIRXFR) {
+        refto_type = SIP_REF_DIR_XFER;
+    }
+
+    if (CC_XFER_METHOD_REFER == method ||
+        method == CC_XFER_METHOD_DIRXFR) {
+        // Add refer as SIP Refer
+        ccsipCCB_t *xfer_refer_ccb;
+
+        ccb->sip_referTo = strlib_update(ccb->sip_referTo, referto);
+        xfer_refer_ccb = sip_sm_get_target_call_by_con_call_id(ccb->con_call_id);
+
+        if ((xfer_refer_ccb != NULL) &&  (xfer_ccb != NULL) && (xfer_ccb->sip_referTo[0] != '\0')) {
+            ccb->sip_referTo = strlib_update(ccb->sip_referTo,
+                                             xfer_refer_ccb->sip_referTo);
+        }
+
+        if (ccb->sip_referTo) {
+            if (sipSPISendRefer(ccb, (char *)ccb->sip_referTo, refto_type) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                  ccb->index, ccb->dn_line, fname,
+                                  "sipSPISendRefer Failed");
+                return;
+            }
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "ccb->sipreferTo is NULL");
+            return;
+        }
+
+    } else if (CC_XFER_METHOD_BYE == method) {
+        cc_features_t   feature;
+
+        ccb->referto = strlib_update(ccb->referto, referto);
+        data.xfer.cause = CC_CAUSE_XFER_LOCAL;
+        feature = fsmxfr_type_to_feature(fsmxfr_get_xfr_type(ccb->gsm_id));
+        data.xfer.method = CC_XFER_METHOD_BYE; // Temp Fix Need to remove
+        data.xfer.dialstring[0] = '\0';
+        data.xfer.target_call_id = CC_NO_CALL_ID;
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature, &data,
+                           CC_CAUSE_NORMAL);
+    } else {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          "Unspecified Method of Transfer");
+
+    }
+
+    /* Pre-fill the ARP table */
+    ADD_TO_ARP_CACHE(ccb->dest_sip_addr);
+}
+
+void
+ccsip_handle_active_ev_cc_feature_ack (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "ccsip_handle_active_ev_cc_feature_ack";
+    ccsipCCB_t     *other_ccb = NULL;
+    cc_features_t   feature_type;
+
+    feature_type = event->u.cc_msg->msg.feature.feature_id;
+    switch (feature_type) {
+    case CC_FEATURE_XFER:
+    case CC_FEATURE_BLIND_XFER:
+        if (CC_XFER_METHOD_REFER == event->u.cc_msg->msg.feature.data.xfer.method) {
+            if (CC_CAUSE_ERROR == event->u.cc_msg->msg.feature.data.xfer.cause) {
+                (void) sipSPISendErrorResponse(ccb->last_request,
+                                               SIP_SERV_ERR_UNAVAIL,
+                                               SIP_SERV_ERR_UNAVAIL_PHRASE,
+                                               0, NULL, ccb);
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Got CC_CAUSE_ERROR"
+                                  "from GSM \n", fname);
+                return;
+            }
+            if (CC_NO_CALL_ID != event->u.cc_msg->msg.feature.data.xfer.target_call_id) {
+                ccb->con_call_id = event->u.cc_msg->msg.feature.data.xfer.target_call_id;
+                if (feature_type == CC_FEATURE_BLIND_XFER) {
+                    ccb->blind_xfer_call_id = event->u.cc_msg->msg.feature.data.xfer.target_call_id;
+                }
+                if (!sipSPISendReferResponse202(ccb)) {
+                    (void) sipSPISendErrorResponse(ccb->last_request,
+                                                   SIP_SERV_ERR_UNAVAIL,
+                                                   SIP_SERV_ERR_UNAVAIL_PHRASE,
+                                                   0, NULL, ccb);
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPISendReferResponse202"
+                                      " failed, sending 503\n", fname);
+                    return;
+                } else {
+                    /* Send NOTIFY 100 Trying */
+                    if (!sipSPISendNotify(ccb, SIP_1XX_TRYING)) {
+                        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPISendNotify"
+                                          " failed, sending 100\n", fname);
+                    }
+                    ccb->xfer_status = 100;
+
+                    if (feature_type == CC_FEATURE_BLIND_XFER) {
+                        /*
+                         * Release this call and tell GSM to start
+                         * the Blind Transfer
+                         */
+                        sip_cc_release(ccb->gsm_id, ccb->dn_line,
+                                       CC_CAUSE_NORMAL, NULL);
+                        sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+                    }
+                }
+            }
+        } else if (CC_XFER_METHOD_BYE == event->u.cc_msg->msg.feature.data.xfer.method) {
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_NORMAL, NULL);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+        }
+        break;
+    case CC_FEATURE_NOTIFY:
+        /* check for special case early attended transfer */
+        other_ccb = sip_sm_get_target_call_by_con_call_id(ccb->con_call_id);
+        if ((other_ccb != NULL) && (other_ccb->early_transfer)) {
+            other_ccb->early_transfer = FALSE;
+            sipSPISendCancel(other_ccb);
+            sip_cc_release_complete(other_ccb->gsm_id, other_ccb->dn_line,
+                                    CC_CAUSE_NORMAL);
+            sip_sm_change_state(other_ccb, SIP_STATE_RELEASE);
+        } else {
+            // This might be a simple acknowledgement for a NOTIFY
+            // received from the network. Reply to the remote side
+            // including the cause-code received from GSM
+            (void) sipSPISendNotifyResponse(ccb,
+                                            event->u.cc_msg->msg.feature_ack.cause);
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+void
+ccsip_handle_active_ev_sip_invite (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "active_ev_sip_invite";
+    sipMessage_t   *request;
+
+    // currently not used: const char *require = NULL;
+    const char     *contact = NULL;
+    uint16_t        request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    cc_feature_data_t data;
+    sipsdp_status_t sdp_status;
+    boolean         apply_ringout = FALSE;
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    // Check for glare conditions
+    // If we have an outstanding INVITE that we sent then we have glare
+    if (get_method_request_trx_index(ccb, sipMethodInvite, TRUE) > -1) {
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"%d Glare condition detected \n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_CALL_STATUS, ccb->dn_line, ccb->gsm_id, fname),
+                                                 ccb->index);
+        // return 491
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_REQ_PENDING,
+                                       SIP_CLI_ERR_REQ_PENDING_PHRASE,
+                                       0, NULL, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+    /* Request check and store */
+    if (sip_sm_request_check_and_store(ccb, request, sipMethodInvite, TRUE,
+                                       &request_check_reason_code,
+                                       request_check_reason_phrase, FALSE) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                          ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       request_check_reason_code,
+                                       request_check_reason_phrase, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+
+    /* update the contact information if needed */
+    contact = sippmh_get_cached_header_val(request, CONTACT);
+    if (contact) {
+        if (ccb->contact_info) {
+            sippmh_free_contact(ccb->contact_info);
+        }
+        ccb->contact_info = sippmh_parse_contact(contact);
+    }
+
+    /*
+     * Process SDP
+     */
+    sdp_status = sip_util_extract_sdp(ccb, request);
+
+    switch (sdp_status) {
+    case SIP_SDP_SESSION_AUDIT:
+        ccb->oa_state = OA_OFFER_RECEIVED;
+        if (((ccb->state == SIP_STATE_SENT_MIDCALL_INVITE) &&
+             (ccb->hold_initiated)) || (ccb->state == SIP_STATE_ACTIVE)) {
+            /*
+             * Respond to the BroadSoft Session Audit Message.
+             * Do same for Refresh case / ReInvite with the same SDP
+             * First request for the current sdp from gsm.
+             *
+             * Need to check for Call-Info ringout state and pass along so that session audit
+             * does not disable CCM spoofed ringout.
+             */
+            if (ccb->in_call_info &&
+                ccb->in_call_info->data.call_info_feat_data.feature_flag & CC_UI_STATE) {
+                apply_ringout =
+                    (ccb->in_call_info->data.call_info_feat_data.ui_state ==
+                     CC_UI_STATE_RINGOUT ? TRUE : FALSE);
+            }
+            /* Update connected party info from RPID and Call-Info header */
+            ccsip_update_callinfo(ccb, request, TRUE, TRUE, FALSE);
+            sip_cc_audit(ccb->gsm_id, ccb->dn_line, apply_ringout);
+            return;
+        }
+        /*FALLTHROUGH*/
+
+    case SIP_SDP_SUCCESS:
+        /*
+         * Check to see if received SDP indicates hold. If it is not
+         * a hold SDP, then we received a new media invite.
+         * Send FEATURE CC event to GSM.
+         */
+        ccb->oa_state = OA_OFFER_RECEIVED;
+        /*
+         * Update connected party info from RPID and Call-Info header.
+         * Media will effect media, delay call information update.
+         */
+        ccsip_update_callinfo(ccb, request, TRUE, TRUE, TRUE);
+        /* Move the message body from the SIP msg. into CCAPI msg */
+        sip_cc_mv_msg_body_to_cc_msg(&data.resume.msg_body, request);
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_MEDIA, &data);
+        sip_sm_change_state(ccb,
+                      SIP_STATE_RECV_MIDCALL_INVITE_CCFEATUREACK_PENDING);
+        break;
+
+    case SIP_SDP_ERROR:
+        (void) sipSPISendErrorResponse(ccb->last_request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC, "Invalid SDP", ccb);
+        return;
+
+    case SIP_SDP_DNS_FAIL:
+        sipSPISendInviteResponse(ccb, SIP_SERV_ERR_INTERNAL,
+                                 SIP_SERV_ERR_INTERNAL_PHRASE,
+                                 SIP_WARN_MISC,
+                                 "DNS lookup failed for media destination",
+                                 FALSE, FALSE);
+        return;
+
+    case SIP_SDP_NO_MEDIA:
+        (void) sipSPISendErrorResponse(ccb->last_request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       "No acceptable media line in SDP", ccb);
+        return;
+
+    case SIP_SDP_NOT_PRESENT:
+       /*FALLTHROUGH*/
+
+    default:
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Waiting for SDP in ACK\n",
+            DEB_F_PREFIX_ARGS(SIP_SDP, fname));
+        /*
+         * Update connected party info from RPID and Call-Info header.
+         * Resuming the media,update call info now.
+         */
+        ccsip_update_callinfo(ccb, request, TRUE, TRUE, FALSE);
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_MEDIA, NULL);
+        sip_sm_change_state(ccb,
+                            SIP_STATE_RECV_MIDCALL_INVITE_CCFEATUREACK_PENDING);
+        break;
+    }
+
+    sipSPISendInviteResponse100(ccb, FALSE);
+}
+
+
+void
+ccsip_handle_active_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "Active_2xx";
+    sipMessage_t   *response;
+    int             response_code = 0;
+
+    /* Unpack the event */
+    response = event->u.pSipMessage;
+    if (sipGetResponseCode(response, &response_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipGetResponseCode");
+        free_sip_message(response);
+        return;
+    }
+    if (response_code == SIP_ACCEPTED) {
+        ccsip_handle_accept_2xx(ccb, event);
+        return;
+    }
+
+    if (sipSPISendAck(ccb, response) != TRUE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipSPISendAck");
+    }
+    /* Update connected party info from RPID and Call-Info header */
+    ccsip_update_callinfo(ccb, response, TRUE, FALSE, FALSE);
+
+    free_sip_message(response);
+}
+
+
+/*
+ * SIP_STATE_SENT_MIDCALL_INVITE
+ */
+
+/**
+ * ccsip_handle_sentinvite_midcall_ev_cc_feature
+ *
+ * The function handles for CC_FEATURE during mid call invite.
+ *
+ * @param[in] ccb     Pointer to ccsipCCB_t structure.
+ * @param[in] event   Pointer to sipSMEvent_t structure.
+ *
+ * @pre               (ccb not_eq NULL)
+ *
+ * @return            N/A
+ */
+void ccsip_handle_sentinvite_midcall_ev_cc_feature (ccsipCCB_t *ccb,
+                                                    sipSMEvent_t *event)
+{
+    const char *fname = "ccsip_handle_sentinvite_midcall_ev_cc_feature";
+    cc_features_t feature_type;
+
+    feature_type = event->u.cc_msg->msg.feature_ack.feature_id;
+
+    switch (feature_type) {
+    case CC_FEATURE_RESUME:
+    case CC_FEATURE_HOLD:
+    case CC_FEATURE_MEDIA:
+        /*
+         * Send resume/hold/media request and waiting for the response.
+         * Indicate that the request can not proceed now and it should be
+         * retried later. GSM should not attempt this.
+         */
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type,
+                           NULL, CC_CAUSE_REQUEST_PENDING);
+        break;
+
+    default:
+        /* Other feature request is not supported or allowed now */
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FEATURE_UNSUPPORTED),
+                          ccb->index, ccb->dn_line, fname);
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type,
+                           NULL, CC_CAUSE_ERROR);
+        break;
+    }
+}
+
+/**
+ * ccsip_handle_sentinvite_midcall_ev_sip_2xx
+ *
+ * The function handles for SIP 2xx during mid call invite.
+ *
+ * @param[in] ccb     Pointer to ccsipCCB_t structure.
+ * @param[in] event   Pointer to sipSMEvent_t structure.
+ *
+ * @pre               (ccb not_eq NULL)
+ *
+ * @return            N/A
+ */
+void ccsip_handle_sentinvite_midcall_ev_sip_2xx (ccsipCCB_t *ccb,
+                                                 sipSMEvent_t *event)
+{
+    const char   *fname = "ccsip_handle_sentinvite_midcall_ev_sip_2xx";
+    sipMessage_t *response;
+    cc_feature_data_t data;
+    sipsdp_status_t sdp_status;
+
+    response = event->u.pSipMessage;
+    if (!sip_sm_is_invite_response(response)) {
+        free_sip_message(response);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        return;
+    }
+
+    /* Stop the expires timer started to await this response */
+    (void) sip_platform_expires_timer_stop(ccb->index);
+
+    sip_sm_200and300_update(ccb, response, SIP_STATUS_SUCCESS);
+
+    /* Reset credentials flag since Hold was successfully processed */
+    ccb->authen.cred_type = 0;
+
+    /*
+     * Send ACK back soon to minimize delayed cutting through at the
+     * remote end.
+     */
+    if (sipSPISendAck(ccb, response) == FALSE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipSPISendAck");
+    }
+
+    /* Update connected party info from RPID and Call-Info header */
+    ccsip_update_callinfo(ccb, response, TRUE, FALSE, FALSE);
+
+    /* Extract destination SDP and related fields */
+    sdp_status = sip_util_extract_sdp(ccb, response);
+
+    switch (sdp_status) {
+    case SIP_SDP_SUCCESS:
+    case SIP_SDP_SESSION_AUDIT:
+    case SIP_SDP_NOT_PRESENT:
+        ccb->oa_state = OA_IDLE;
+        /* Move the messag bodies from SIP msg. to CCAPI msg. */
+        switch (ccb->featuretype) {
+        case CC_FEATURE_HOLD:
+            sip_cc_mv_msg_body_to_cc_msg(&data.hold.msg_body, response);
+            sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, ccb->featuretype,
+                               &data, CC_CAUSE_NORMAL);
+            break;
+        case CC_FEATURE_RESUME:
+        case CC_FEATURE_MEDIA:
+            sip_cc_mv_msg_body_to_cc_msg(&data.resume.msg_body, response);
+            sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, ccb->featuretype,
+                               &data, CC_CAUSE_NORMAL);
+            break;
+        default:
+            /*
+             * Other features are not expected.
+             */
+            CCSIP_DEBUG_ERROR(DEB_L_C_F_PREFIX"%d: unexpected feature %d\n",
+                              ccb->dn_line, ccb->gsm_id, fname,
+                              ccb->index, ccb->featuretype);
+            sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, ccb->featuretype,
+                               NULL, CC_CAUSE_ERROR);
+            break;
+        }
+        break;
+
+    case SIP_SDP_DNS_FAIL:
+    case SIP_SDP_NO_MEDIA:
+    case SIP_SDP_ERROR:
+    default:
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, ccb->featuretype, NULL,
+                           CC_CAUSE_ERROR);
+        break;
+    }
+
+    free_sip_message(response);
+    sip_sm_change_state(ccb, SIP_STATE_ACTIVE);
+}
+
+void
+ccsip_handle_accept_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    cc_feature_data_t data;
+    cc_features_t   feature;
+    sipMessage_t   *response;
+    int             response_code = 0;
+    const char     *cseq = NULL;
+    sipCseq_t      *sipCseq = NULL;
+    char           *fname = "ccsip_handle_accept_2xx";
+    sipMethod_t     response_method;
+
+    response = event->u.pSipMessage;
+    if (sipGetResponseCode(response, &response_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipGetResponseCode");
+        free_sip_message(response);
+        return;
+    }
+
+    /* Update connected party info from RPID and Call-Info header */
+    ccsip_update_callinfo(ccb, response, TRUE, FALSE, FALSE);
+
+    /* don't ack a 200 on a Notify message */
+    // if ((response_code == SIP_SUCCESS_SETUP) &&
+    //    (ccb->last_sent_request_cseq_method == sipMethodNotify)) {
+    //    free_sip_message(response);
+    //    return;
+    // }
+    // Instead of checking the last request, check the CSeq method of
+    // the response and then determine which request it is responding to
+    // Then clear that request from the cseq array
+    cseq = sippmh_get_cached_header_val(response, CSEQ);
+    if (!cseq) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(CSEQ)");
+        free_sip_message(response);
+        return;
+    }
+    sipCseq = sippmh_parse_cseq(cseq);
+    if (!sipCseq) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_parse_cseq()");
+        free_sip_message(response);
+        return;
+    }
+    response_method = sipCseq->method;
+    cpr_free(sipCseq);
+
+    if ((response_code == SIP_SUCCESS_SETUP) &&
+        (response_method == sipMethodNotify)) {
+        clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+        free_sip_message(response);
+        return;
+    }
+
+    if (response_code != SIP_ACCEPTED) {
+        if (sipSPISendAck(ccb, response) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPISendAck");
+            free_sip_message(response);
+            return;
+        }
+    }
+    switch (ccb->featuretype) {
+    case CC_FEATURE_B2BCONF:
+    case CC_FEATURE_SELECT:
+    case CC_FEATURE_B2B_JOIN:
+    case CC_FEATURE_CANCEL:
+        ccb->authen.cred_type = 0;
+        feature = ccb->featuretype;
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature, NULL,
+                           CC_CAUSE_OK);
+        clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+        free_sip_message(response);
+        return;
+    default:
+        break;
+    }
+
+    data.xfer.cause = CC_CAUSE_XFER_LOCAL;
+    data.xfer.method = CC_XFER_METHOD_REFER;
+    data.xfer.dialstring[0] = '\0';
+    data.xfer.target_call_id = ccb->gsm_id;
+    feature = fsmxfr_type_to_feature(fsmxfr_get_xfr_type(ccb->gsm_id));
+
+    sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature, &data, CC_CAUSE_OK);
+    clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+    free_sip_message(response);
+}
+
+/*
+ * SIP_STATE_RECV_MIDCALLINVITE_CCFEATUREACK_PENDING
+ */
+void
+ccsip_handle_recvmidcallinvite_ccfeatureackpending_ev_cc_feature_ack (
+                                           ccsipCCB_t *ccb,
+                                           sipSMEvent_t *event)
+{
+    cc_features_t feature_type;
+    cc_msgbody_info_t *msg_body;
+
+    feature_type = event->u.cc_msg->msg.feature_ack.feature_id;
+
+    switch (feature_type) {
+    case CC_FEATURE_HOLD:
+        if (event->u.cc_msg->msg.feature_ack.data_valid) {
+            /* Save the msg. body */
+            msg_body = &event->u.cc_msg->msg.feature_ack.data.hold.msg_body;
+            ccsip_save_local_msg_body(ccb, msg_body);
+        }
+
+        sipSPISendInviteResponse200(ccb); /* HOLD */
+        sip_sm_change_state(ccb,
+                            SIP_STATE_RECV_MIDCALL_INVITE_SIPACK_PENDING);
+        break;
+
+    case CC_FEATURE_RESUME:
+        /* fall through to send the response */
+    case CC_FEATURE_MEDIA:
+        /* new media acks put the sdp in the resume area as well */
+        if (event->u.cc_msg->msg.feature_ack.data_valid) {
+            msg_body = &event->u.cc_msg->msg.feature_ack.data.resume.msg_body;
+            ccsip_save_local_msg_body(ccb, msg_body);
+        }
+        if (event->u.cc_msg->msg.feature_ack.cause ==  CC_CAUSE_PAYLOAD_MISMATCH ||
+            event->u.cc_msg->msg.feature_ack.cause ==  CC_CAUSE_NO_MEDIA ||
+            event->u.cc_msg->msg.feature_ack.cause ==  CC_CAUSE_ERROR) {
+            /*
+             * Rejecting the new sdp
+             * The above errors are the ones currently thrown by gsm sdp processing.
+             * If any new errors are added the above check must be updated.
+             */
+            ccb->oa_state = OA_IDLE;
+            sipSPISendInviteResponse(ccb, SIP_CLI_ERR_NOT_ACCEPT_HERE,
+                                     SIP_CLI_ERR_NOT_ACCEPT_HERE_PHRASE,
+                                     SIP_WARN_MISC,
+                                     "SDP Not Acceptable",
+                                     FALSE, /* no SDP */ TRUE /* reTx */);
+        } else {
+            sipSPISendInviteResponse200(ccb);
+        }
+        sip_sm_change_state(ccb, SIP_STATE_RECV_MIDCALL_INVITE_SIPACK_PENDING);
+        break;
+
+    default:
+        break;
+    }
+}
+
+
+/*
+ * SIP_STATE_RECV_MIDCALL_INVITE_SIPACK_PENDING
+ */
+void
+ccsip_handle_recvmidcallinvite_sipackpending_ev_sip_ack (ccsipCCB_t *ccb,
+                                                         sipSMEvent_t *event)
+{
+    const char     *fname =
+                 "ccsip_handle_recvmidcallinvite_sipackpending_ev_sip_ack";
+    sipMessage_t   *request;
+    uint16_t        request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    cc_feature_data_t data;
+    sipsdp_status_t sdp_status;
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    /* Request check and store */
+    if (sip_sm_request_check_and_store(ccb, request, sipMethodAck, FALSE,
+                                       &request_check_reason_code,
+                                       request_check_reason_phrase, FALSE) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                          ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+        free_sip_message(request);
+        return;
+    }
+
+    /*
+     * Process SDP
+     */
+    sdp_status = sip_util_extract_sdp(ccb, request);
+
+    switch (sdp_status) {
+    case SIP_SDP_SUCCESS:
+    case SIP_SDP_SESSION_AUDIT:
+        if (ccb->oa_state != OA_OFFER_SENT) {
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                              ccb->index, ccb->dn_line, fname,
+                              "Unexpected SDP in ACK, releasing call");
+            sipSPISendBye(ccb, NULL, NULL);
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            clean_method_request_trx(ccb, sipMethodAck, FALSE);
+            clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            return;
+        }
+
+        /*
+         * Check to see if received SDP indicates hold. If it is not
+         * a hold SDP, then we received a new media invite.
+         * Send FEATURE CC event to GSM.
+         */
+        ccb->oa_state = OA_IDLE;
+
+        /*
+         * Update connected party info from RPID and Call-Info header.
+         * MEDIA request, delay UI update.
+         */
+        ccsip_update_callinfo(ccb, request, TRUE, TRUE, TRUE);
+
+        /* Move the msg. body from the SIP msg. to CCAPI msg. */
+        sip_cc_mv_msg_body_to_cc_msg(&data.resume.msg_body, request);
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_MEDIA, &data);
+        break;
+
+    case SIP_SDP_ERROR:
+        (void) sipSPISendErrorResponse(ccb->last_request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC, "Invalid SDP", ccb);
+        clean_method_request_trx(ccb, sipMethodAck, FALSE);
+        clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+        return;
+
+    case SIP_SDP_DNS_FAIL:
+        sipSPISendInviteResponse(ccb, SIP_SERV_ERR_INTERNAL,
+                                 SIP_SERV_ERR_INTERNAL_PHRASE,
+                                 SIP_WARN_MISC,
+                                 "DNS lookup failed for media destination",
+                                 FALSE, FALSE);
+        break;
+
+    case SIP_SDP_NO_MEDIA:
+        (void) sipSPISendErrorResponse(ccb->last_request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       "No acceptable media line in SDP", ccb);
+        clean_method_request_trx(ccb, sipMethodAck, FALSE);
+        clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+        return;
+
+    case SIP_SDP_NOT_PRESENT:
+        if (ccb->oa_state == OA_OFFER_SENT) {
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                              ccb->index, ccb->dn_line, fname,
+                              "No answer SDP in ACK, releasing call");
+            sipSPISendBye(ccb, NULL, NULL);
+            sip_cc_release(ccb->gsm_id, ccb->dn_line, CC_CAUSE_ERROR, NULL);
+            clean_method_request_trx(ccb, sipMethodAck, FALSE);
+            clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+            sip_sm_change_state(ccb, SIP_STATE_RELEASE);
+            return;
+        } else {
+            /* Update connected party info from RPID and Call-Info header */
+            ccsip_update_callinfo(ccb, request, TRUE, TRUE, FALSE);
+        }
+        break;
+
+    default:
+        /* Update connected party info from RPID and Call-Info header */
+        ccsip_update_callinfo(ccb, request, TRUE, TRUE, FALSE);
+        break;
+
+    }
+
+    /*
+     * Update state
+     */
+    sip_sm_change_state(ccb, SIP_STATE_ACTIVE);
+    clean_method_request_trx(ccb, sipMethodAck, FALSE);
+    clean_method_request_trx(ccb, sipMethodInvite, FALSE);
+}
+
+
+void
+ccsip_handle_transienthold_ev_cc_feature (ccsipCCB_t *ccb, int feature_type,
+                                          sipSMStateType_t final_resume_state,
+                                          sipSMEvent_t *event)
+{
+    const char *fname = "transienthold_ev_cc_feature";
+    cc_msgbody_info_t *msg_body;
+
+    switch (feature_type) {
+    case CC_FEATURE_RESUME:
+        if (event->u.cc_msg->msg.feature.data_valid) {
+            msg_body = &event->u.cc_msg->msg.feature.data.resume.msg_body;
+            ccsip_save_local_msg_body(ccb, msg_body);
+        }
+
+        ccb->hold_initiated = FALSE;
+        ccb->featuretype = CC_FEATURE_RESUME;
+        sip_sm_change_state(ccb, final_resume_state);
+        break;
+
+    default:
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FEATURE_UNSUPPORTED),
+                          ccb->index, ccb->dn_line, fname);
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type, NULL,
+                           CC_CAUSE_ERROR);
+        break;
+    }
+}
+
+
+int
+strcasecmp_ignorewhitespace (const char *cs, const char *ct)
+{
+    const char *p;
+    const char *q;
+
+    if (cpr_strcasecmp(cs, ct) == 0) {
+        return (0);
+    }
+
+    p = cs;
+    q = ct;
+
+    /* Ignore leading white space */
+    while (((*p == ' ') || (*p == '\t')) && (*p != '\0')) {
+        p++;
+    }
+    while (((*q == ' ') || (*q == '\t')) && (*q != '\0')) {
+        q++;
+    }
+
+    /* Compare until hit end or whitespace */
+    while ((*p != ' ') && (*p != '\t') && (*p != '\0') &&
+           (*q != ' ') && (*q != '\t') && (*q != '\0')) {
+        if (toupper(*p) != toupper(*q)) {
+            return (-1);
+        }
+        p++;
+        q++;
+    }
+
+    /* Make sure that what's left (if any) is whitespace */
+    while (*p != '\0') {
+        if ((*p != ' ') && (*p != '\t')) {
+            return (-1);
+        }
+        p++;
+    }
+    while (*q != '\0') {
+        if ((*q != ' ') && (*q != '\t')) {
+            return (-1);
+        }
+        q++;
+    }
+
+    return (0);
+}
+
+
+void
+sip_sm_util_normalize_name (ccsipCCB_t *ccb, char *dialString)
+{
+    char           *outputString;
+    uint32_t        usernameLength = 0;
+    const char     *hostnameString = NULL;
+    uint32_t        hostnameLength = 0;
+    char            proxy_ipaddr_str[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t   proxy_ipaddr;
+    char           *extraString = NULL;
+    char           *parse_token;
+    uint32_t        extraLength = 0;
+    uint32_t        n = 0;
+    static char     dialtranslate[MAX_SIP_URL_LENGTH];
+    char            dest_sip_addr_str[MAX_IPADDR_STR_LEN];
+    char            addr[MAX_IPADDR_STR_LEN];
+    int             port;
+    int             dialStringLength;
+
+    CPR_IP_ADDR_INIT(proxy_ipaddr);
+    /*
+     * Save away what they sent us so that we can display it to them later
+     */
+    dialStringLength = strlen(dialString);
+    memcpy((void *)ccb->calledDisplayedName, dialString, dialStringLength);
+    /*
+     * See if the string needs to be rewritten by applying the dial template
+     */
+    ccb->routeMode = RouteDefault;
+    (void) MatchDialTemplate(ccb->calledDisplayedName, ccb->dn_line, CAST_N &n, dialtranslate,
+                             sizeof(dialtranslate),
+                             (RouteMode *) &(ccb->routeMode), NULL);
+    dialString = dialtranslate;
+    dialStringLength = strlen(dialString);
+    /*
+     * Throw away any part of <SIP: that they might have entered
+     */
+    if ((dialStringLength > 0) && (dialString[0] == '<')) {
+        dialString++;
+        dialStringLength--;
+    }
+    /*
+     * For the SIP: part, we have to have 4 characters
+     */
+    if ((dialStringLength > 4) &&
+        (tolower(dialString[0]) == 's') &&
+        (tolower(dialString[1]) == 'i') &&
+        (tolower(dialString[2]) == 'p') &&
+        (tolower(dialString[3]) == ':')) {
+        dialStringLength -= 4;
+        dialString += 4;
+    }
+
+    /*
+     * Parse the remainder of the string looking for the host name
+     */
+    parse_token = strpbrk(dialString, "@;>"); /* Skip the user name first */
+    if (parse_token == NULL) { /* Only user name exists and no host, extra and ;> */
+        usernameLength = dialStringLength;
+    } else {
+        usernameLength = parse_token - dialString; /* save username length */
+        if (parse_token[0] == '@') { /* host name exists in addition */
+            extraString = strpbrk(parse_token, ";>"); /* Skip the host name */
+            if (extraString != NULL) { /* extra params exist */
+                extraLength = dialStringLength - (extraString - dialString);
+            }
+            /*
+             * Save the host name and host length if proxy selection is
+             * not backup. In case of proxy selection being backup,
+             * ignore the hostname & rebuild using dotted IP of backup proxy later
+             */
+            if (ccb->proxySelection != SIP_PROXY_BACKUP) {
+                hostnameString = parse_token + 1;
+                /* In case of host name, the length must exclude @ and hence -1 */
+                hostnameLength = dialStringLength - usernameLength - extraLength - 1;
+            }
+        } else { /* No host, but extra param exist */
+            extraLength = dialStringLength - usernameLength;
+            extraString = parse_token;
+        }
+    }
+
+    /*
+     * At this point we have
+     *   usernameLength - Number of characters in the user name
+     *                    starting from dialString[0]
+     *   hostnameString - NULL if no @ was encountered, otherwise it
+     *                      points to the start of the host name string
+     *   hostnameLength - 0 if no host name characters were encountered
+     *   extraString - Points to any extra parameters
+     *   extraLength - number of extra parameter characters
+     */
+    if (hostnameLength == 0) {
+        /*
+         * At this junctor in the quantum,
+         *
+         */
+        switch (ccb->routeMode) {
+        case RouteEmergency:
+                /*
+                 * If we have failed over to the backup we need
+                 * to not reselect the emergency proxy
+                 */
+                if (ccb->proxySelection != SIP_PROXY_BACKUP) {
+                    // Get the Emergency Proxy
+                    sipTransportGetEmerServerAddress(ccb->dn_line, proxy_ipaddr_str);
+                    hostnameString = &proxy_ipaddr_str[0];
+                    hostnameLength = strlen(hostnameString);
+                    if (hostnameLength) {
+                        if (!str2ip((const char *) proxy_ipaddr_str, &proxy_ipaddr)) {
+                            /* Fill in address and port in CCB */
+                            util_ntohl(&(ccb->dest_sip_addr), &(proxy_ipaddr));
+
+                            /*
+                             * If the Proxy Emergency Port isn't contained in the
+                             * config table, use the PROXYN port instead.
+                             */
+                            port = sipTransportGetEmerServerPort(ccb->dn_line);
+                            if (port) {
+                                ccb->dest_sip_port = port;
+                            } else {
+                                ccb->dest_sip_port = sipTransportGetPrimServerPort(ccb->dn_line);
+                            }
+                            break;
+                        }
+                    }
+                }
+                // Otherwise Emergency proxy is not in configuration follow thru
+             /*FALLTHROUGH*/
+        default:
+            /*
+             * set the proxy address differently if the backup proxy
+             * has been activated. The address has already been placed into
+             * ccb->dest_sip_addr and ccb->dest_sip_port so just use
+             * those values
+             */
+            if (ccb->proxySelection == SIP_PROXY_BACKUP) {
+                ipaddr2dotted(dest_sip_addr_str, &ccb->dest_sip_addr);
+                hostnameString = dest_sip_addr_str;
+                hostnameLength = strlen(hostnameString);
+            } else {
+                sipTransportGetPrimServerAddress(ccb->dn_line, addr);
+                hostnameString = addr;
+                hostnameLength = strlen(hostnameString);
+                sipTransportGetServerIPAddr(&(ccb->dest_sip_addr), ccb->dn_line);
+                ccb->dest_sip_port = sipTransportGetPrimServerPort(ccb->dn_line);
+            }
+        }
+    }
+
+    /*
+     * Construct the actual dial out string
+     */
+    outputString = (char *) ccb->calledNumber;
+    sstrncpy(outputString, "<sip:", MAX_SIP_URL_LENGTH);
+    outputString += 5;
+    if (usernameLength) {
+        outputString += sippmh_convertURLCharToEscChar(dialString,
+                                                       usernameLength,
+                                                       outputString,
+                                                       (MAX_SIP_URL_LENGTH * 2) - 5, FALSE);
+        *outputString++ = '@';
+    }
+    if (hostnameLength) {
+        memcpy(outputString, hostnameString, hostnameLength);
+        outputString += hostnameLength;
+    }
+    if (extraLength) {
+        memcpy(outputString, extraString, extraLength);
+        /*
+         * Null terminate the string so we can quickly look for a >
+         */
+        outputString[extraLength] = 0;
+        /*
+         * If there was no > in what they gave us, put one in for them
+         */
+        if (strchr(outputString, '>') == NULL) {
+            outputString[extraLength++] = '>';
+        }
+        outputString += extraLength;
+    } else {
+        *outputString++ = '>';
+    }
+    /*
+     * Null terminate the string for good measure and note how long it is
+     */
+    *outputString = 0;
+    ccb->calledNumberLen = (uint16_t) (outputString - ccb->calledNumber);
+}
+
+
+void
+get_sip_error_string (char *errortext, int response)
+{
+
+    if (NULL == errortext)
+        return;
+    switch (response) {
+    case SIP_SUCCESS_SETUP:
+        sstrncpy(errortext, SIP_SUCCESS_SETUP_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_ACCEPTED:
+        sstrncpy(errortext, SIP_ACCEPTED_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_1XX_TRYING:
+        sstrncpy(errortext, SIP_1XX_TRYING_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_RED_MULT_CHOICES:
+        sstrncpy(errortext, SIP_RED_MULT_CHOICES_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_RED_MOVED_PERM:
+        sstrncpy(errortext, SIP_RED_MOVED_PERM_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_RED_MOVED_TEMP:
+        sstrncpy(errortext, SIP_RED_MOVED_TEMP_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_RED_SEE_OTHER:
+        sstrncpy(errortext, SIP_RED_SEE_OTHER_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_RED_USE_PROXY:
+        sstrncpy(errortext, SIP_RED_USE_PROXY_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_RED_ALT_SERVICE:
+        sstrncpy(errortext, SIP_RED_ALT_SERVICE_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_BAD_REQ:  /* 400   Bad Request */
+        sstrncpy(errortext, SIP_CLI_ERR_BAD_REQ_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_UNAUTH:   /* 401   Unauthorized */
+        sstrncpy(errortext, SIP_CLI_ERR_UNAUTH_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_PAY_REQD: /* 402   Payment Required */
+        sstrncpy(errortext, SIP_CLI_ERR_PAY_REQD_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_FORBIDDEN:    /* 403   Forbidden */
+        sstrncpy(errortext, SIP_CLI_ERR_FORBIDDEN_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_NOT_FOUND:    /* 404   Not Found */
+        sstrncpy(errortext, SIP_CLI_ERR_NOT_FOUND_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_NOT_ALLOWED:  /* 405   Method Not Allowed */
+        sstrncpy(errortext, SIP_CLI_ERR_NOT_ALLOWED_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_NOT_ACCEPT:   /* 406   Not Acceptable */
+        sstrncpy(errortext, SIP_CLI_ERR_NOT_ACCEPT_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_PROXY_REQD:   /* 407   Proxy Authentication Required */
+        sstrncpy(errortext, SIP_CLI_ERR_PROXY_REQD_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_REQ_TIMEOUT:  /* 408  Request Timeout */
+        sstrncpy(errortext, SIP_CLI_ERR_REQ_TIMEOUT_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_CONFLICT: /* 409  Conflict */
+        sstrncpy(errortext, SIP_CLI_ERR_CONFLICT_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_GONE:     /* 410  Gone */
+        sstrncpy(errortext, SIP_CLI_ERR_GONE_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_LEN_REQD: /* 411  Length Required */
+        sstrncpy(errortext, SIP_CLI_ERR_LEN_REQD_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_LARGE_MSG:    /* 413  Request Message Body Too Large */
+        sstrncpy(errortext, SIP_CLI_ERR_LARGE_MSG_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_LARGE_URI:    /* 414  Request-URI Too Large */
+        sstrncpy(errortext, SIP_CLI_ERR_LARGE_URI_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_MEDIA:    /* 415  Unsupported Media Type */
+        sstrncpy(errortext, SIP_CLI_ERR_MEDIA_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_EXTENSION:    /* 420  Bad Extension */
+        sstrncpy(errortext, SIP_CLI_ERR_EXTENSION_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED:    /* 433  Anonymity Disallowed */
+        sstrncpy(errortext, SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_NOT_AVAIL:    /* 480  Temporarily Not Available */
+        sstrncpy(errortext, SIP_CLI_ERR_NOT_AVAIL_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_CALLEG:   /* 481  Call Leg/Transaction Does Not Exist */
+        sstrncpy(errortext, SIP_CLI_ERR_CALLEG_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_LOOP_DETECT:  /* 482  Loop Detected */
+        sstrncpy(errortext, SIP_CLI_ERR_LOOP_DETECT_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_MANY_HOPS:    /* 483  Too Many Hops */
+        sstrncpy(errortext, SIP_CLI_ERR_MANY_HOPS_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_ADDRESS:  /* 484  Address Incomplete */
+        sstrncpy(errortext, SIP_CLI_ERR_ADDRESS_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_AMBIGUOUS:    /* 485  Ambiguous */
+        sstrncpy(errortext, SIP_CLI_ERR_AMBIGUOUS_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_BUSY_HERE:    /* 486  Busy here */
+        sstrncpy(errortext, SIP_CLI_ERR_BUSY_HERE_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_REQ_CANCEL:   /* 487  Request Cancelled */
+        sstrncpy(errortext, SIP_CLI_ERR_REQ_CANCEL_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_CLI_ERR_BAD_EVENT:    /* 489  Bad Event */
+        sstrncpy(errortext, SIP_CLI_ERR_BAD_EVENT_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_SERV_ERR_INTERNAL:    /*500   Internal Server Error */
+        sstrncpy(errortext, SIP_SERV_ERR_INTERNAL_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_SERV_ERR_NOT_IMPLEM:  /* 501 Not Implemented */
+        sstrncpy(errortext, SIP_SERV_ERR_NOT_IMPLEM_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_SERV_ERR_BAD_GW:  /*502  Bad Gateway */
+        sstrncpy(errortext, SIP_SERV_ERR_BAD_GW_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_SERV_ERR_UNAVAIL: /* 503 Service Unavailable */
+        sstrncpy(errortext, SIP_SERV_ERR_UNAVAIL_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_SERV_ERR_GW_TIMEOUT:  /*504   Gateway Timeout */
+        sstrncpy(errortext, SIP_SERV_ERR_GW_TIMEOUT_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_SERV_ERR_SIP_VER: /*505   SIP Version not supported */
+        sstrncpy(errortext, SIP_SERV_ERR_SIP_VER_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_SERV_ERR_PRECOND_FAILED:  /*580  Precondition Failed */
+        sstrncpy(errortext, SIP_SERV_ERR_PRECOND_FAILED_PHRASE,
+                 MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_FAIL_BUSY:        /*600   BUSY */
+        sstrncpy(errortext, SIP_FAIL_BUSY_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_FAIL_DECLINE:     /*603   Decline */
+        sstrncpy(errortext, SIP_FAIL_DECLINE_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_FAIL_NOT_EXIST:   /*604   Does not exist anywhere */
+        sstrncpy(errortext, SIP_FAIL_NOT_EXIST_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    case SIP_FAIL_NOT_ACCEPT:  /*606   Not Acceptable */
+        sstrncpy(errortext, SIP_FAIL_NOT_ACCEPT_PHRASE, MAX_SIP_URL_LENGTH);
+        break;
+    default:
+        sstrncpy(errortext, SIP_STATUS_PHRASE_NONE, MAX_SIP_URL_LENGTH);
+        break;
+    }
+}
+
+/***********************************************************************
+ * Function: ccsip_handle_early_ev_sip_update
+ * Description: This function handles an incoming UPDATE message, parses
+ *              and sends it up to GSM as cc_feature call
+ ***************************************  ********************************/
+void
+ccsip_handle_early_ev_sip_update (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "early_ev_sip_update";
+    sipMessage_t   *request = NULL;
+    unsigned short  request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    sipMethod_t     method = sipMethodInvalid;
+    sipsdp_status_t sdp_status;
+    boolean         display_valid = TRUE;
+
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    sipGetRequestMethod(request, &method);
+
+    // Check if we are already processing a previously received UPDATE
+    if (get_method_request_trx_index(ccb, method, FALSE) > -1) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received UPDATE while processing an old one!\n",
+                          fname);
+        (void) sipSPISendErrorResponse(request, SIP_SERV_ERR_INTERNAL,
+                                       SIP_SERV_ERR_INTERNAL_PHRASE,
+                                       SIP_WARN_PROCESSING_PREVIOUS_REQUEST,
+                                       NULL, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+    // Check if we have an UPDATE outstanding - if so we have a glare condition
+    if (get_method_request_trx_index(ccb, method, TRUE) > -1) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received UPDATE while old one outstanding!\n",
+                          fname);
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_REQ_PENDING,
+                                       SIP_CLI_ERR_REQ_PENDING_PHRASE, 0, NULL, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+    memset(request_check_reason_phrase, 0, SIP_WARNING_LENGTH);
+
+    /* Request check and store */
+    if (sip_sm_request_check_and_store(ccb, request, method, TRUE,
+                                       &request_check_reason_code,
+                                       request_check_reason_phrase, FALSE) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       request_check_reason_code,
+                                       request_check_reason_phrase, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+      /*
+     * Check if display options are acceptable.
+     */
+    display_valid = ccsip_check_display_validity(ccb, request);
+    if (!display_valid) {
+         CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Rejecting UPDATE with callerid blocked.Anonymous Callback configured!\n",
+                          fname);
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED,
+                                       SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED_PHRASE,
+                                       SIP_WARN_PROCESSING_PREVIOUS_REQUEST,
+                                       NULL, NULL);
+        return;
+    }
+
+    // Check for anything in the REQUIRE header
+    // Update the contact information, if any
+
+    // Since this is still an early dialog and we have not updated our from/to headers
+    // Copy the To header so that a response can be generated correctly
+    if (!(ccb->flags & INCOMING)) {
+        const char *from;
+
+        from = sippmh_get_cached_header_val(request, FROM);
+        ccb->sip_to = strlib_update(ccb->sip_to, from);
+    }
+
+    // If there is SDP in this message, extract it and send it up to GSM
+    sdp_status = sip_util_extract_sdp(ccb, request);
+    switch (sdp_status) {
+    case SIP_SDP_SUCCESS:
+        /*
+         * Since we do not support PRACK, UPDATE received for an early
+         * dialog can not contain SDP. RFC3311 section 5.2 describes
+         * the behavior to follow when receiving UPDATE.
+         *
+         * If UAS receives UPDATE before UAS has generated answer to
+         * previous offer, UAS must respond with 500 which includes a
+         * Retry-after header field with a random value between 0 and
+         * 10 seconds.
+         *
+         * If UAS receives UPDATE before receiving an answer to an offer
+         * made by the UAS, the UAS must respond with 491.
+         */
+        if (ccb->oa_state == OA_OFFER_SENT) {
+            (void) sipSPISendUpdateResponse(ccb, FALSE, CC_CAUSE_REQUEST_PENDING, FALSE);
+        } else {
+            /*
+             * Must be case that ccb->oa_stat == OA_OFFER_RECEIVED, since we
+             * are an early dialog. In either case, we send 500 response.
+             */
+            (void) sipSPISendUpdateResponse(ccb, FALSE, CC_CAUSE_NO_RESOURCE, FALSE);
+        }
+        return;
+
+    case SIP_SDP_ERROR:
+        (void) sipSPISendErrorResponse(ccb->last_request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC, "Invalid SDP", ccb);
+        return;
+
+    case SIP_SDP_DNS_FAIL:
+        (void) sipSPISendErrorResponse(ccb->last_request, SIP_SERV_ERR_INTERNAL,
+                                       SIP_SERV_ERR_INTERNAL_PHRASE,
+                                       SIP_WARN_MISC,
+                                       "DNS lookup failed for media destination", ccb);
+        return;
+
+    case SIP_SDP_NO_MEDIA:
+        (void) sipSPISendErrorResponse(ccb->last_request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       "No acceptable media line in SDP", ccb);
+        return;
+
+    case SIP_SDP_NOT_PRESENT:
+    default:
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX":Update received without SDP\n", fname);
+        break;
+    }
+
+    /* Update connected party info from RPID and Call-Info header */
+    ccsip_update_callinfo(ccb, request, TRUE, TRUE, FALSE);
+
+    /*
+     * Since we do not support PRACK, media can not be changed with UPDATE
+     * during early dialog. We alread sent the call info changes to GSM above
+     * so there is nothing else to inform GSM about. Go ahead and send the
+     * response to the far end.
+     * NOTE: When PRACK is supported, we will need to update this function
+     * to send media to GSM.
+     */
+    (void) sipSPISendUpdateResponse(ccb, FALSE, CC_CAUSE_OK, FALSE);
+}
+
+/***********************************************************************
+ * Function: ccsip_handle_early_ev_sip_update_response
+ * Description: This function handles an incoming response to a previously
+ *              send UPDATE message, and sends it up to GSM via cc_feature_ack
+ ***********************************************************************/
+void
+ccsip_handle_early_ev_sip_update_response (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+}
+
+/***********************************************************************
+ * Function: ccsip_handle_early_ev_cc_feature
+ * Description: This function handles GSM's request to invoke a feature
+ *              before the dialog is fully established.
+ ***********************************************************************/
+void
+ccsip_handle_early_ev_cc_feature (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+
+    const char     *fname = "early_ev_cc_feature";
+    cc_features_t   feature_type;
+    cc_msgbody_info_t *msg_body;
+
+    feature_type = event->u.cc_msg->msg.feature.feature_id;
+    if (feature_type == CC_FEATURE_UPDATE) {
+        if (event->u.cc_msg->msg.feature.data_valid) {
+            msg_body = &event->u.cc_msg->msg.feature.data.update.msg_body;
+            ccsip_save_local_msg_body(ccb, msg_body);
+        }
+        (void) sipSPISendUpdate(ccb);
+    } else if (feature_type == CC_FEATURE_SELECT) {
+    } else {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FEATURE_UNSUPPORTED),
+                          ccb->index, ccb->dn_line, fname);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        sip_cc_feature_ack(ccb->gsm_id, ccb->dn_line, feature_type, NULL,
+                           CC_CAUSE_ERROR);
+    }
+}
+
+/***********************************************************************
+ * Function: ccsip_handle_early_ev_cc_feature_ack
+ * Description: This function handles GSM's acknowledgment to an early
+ *              feature invocation. At this time only responses to the
+ *              UPDATE message is supported
+ ***********************************************************************/
+void
+ccsip_handle_early_ev_cc_feature_ack (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "early_ev_cc_feature_ack";
+    cc_features_t   feature_type;
+    cc_msgbody_info_t *msg_body;
+
+    feature_type = event->u.cc_msg->msg.feature_ack.feature_id;
+
+    switch (feature_type) {
+    case CC_FEATURE_UPDATE:
+        if (event->u.cc_msg->msg.feature.data_valid) {
+            msg_body = &event->u.cc_msg->msg.feature.data.update.msg_body;
+            ccsip_save_local_msg_body(ccb, msg_body);
+        }
+        (void) sipSPISendUpdateResponse(ccb, event->u.cc_msg->msg.feature.data_valid,
+                                        event->u.cc_msg->msg.feature_ack.cause,
+                                        FALSE);
+        break;
+
+    default:
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FEATURE_UNSUPPORTED),
+                          ccb->index, ccb->dn_line, fname);
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_STATE_UNCHANGED),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_state2string(ccb->state));
+        break;
+    }
+}
+
+/***********************************************************************
+ * Function: ccsip_handle_active_ev_sip_update
+ *
+ * Description: This function handles receipt of a SIP UPDATE message
+ * when the call is in the confirmed state. Based on the SDP contained,
+ * this may be put the call on hold or to propose new media. In either
+ * case, the new SDP is sent upto the GSM using cc_feature_update.
+ *
+ * In addition or instead of a media change, the UPDATE may also send
+ * updated values of the call-info header and RPID so these need to parsed
+ * and sent to GSM as well
+ *
+ * This function is analogous to the one the processes reINVITEs
+ ***********************************************************************/
+void
+ccsip_handle_active_ev_sip_update (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "active_ev_sip_update";
+    sipMessage_t   *request;
+    const char     *require = NULL;
+    const char     *contact = NULL;
+    uint16_t        request_check_reason_code = 0;
+    char            request_check_reason_phrase[SIP_WARNING_LENGTH];
+    cc_feature_data_t data;
+    sipsdp_status_t sdp_status;
+    boolean         display_valid = TRUE;
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    // Check if we are already processing a previously received UPDATE
+    if (get_method_request_trx_index(ccb, sipMethodUpdate, FALSE) > -1) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received UPDATE while processing an old one!\n",
+                          fname);
+        (void) sipSPISendErrorResponse(request, SIP_SERV_ERR_INTERNAL,
+                                       SIP_SERV_ERR_INTERNAL_PHRASE,
+                                       SIP_WARN_PROCESSING_PREVIOUS_REQUEST,
+                                       NULL, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+    /* Request check and store */
+    if (sip_sm_request_check_and_store(ccb, request, sipMethodUpdate, TRUE,
+                                       &request_check_reason_code,
+                                       request_check_reason_phrase, FALSE) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIP_SM_REQUEST_CHECK_AND_STORE));
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       request_check_reason_code,
+                                       request_check_reason_phrase, NULL);
+        free_sip_message(request);
+        return;
+    }
+
+    /*
+     * Check if display options are acceptable.
+     */
+    display_valid = ccsip_check_display_validity(ccb, request);
+    if (!display_valid) {
+         CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Rejecting UPDATE with callerid blocked.Anonymous Callback configured!\n",
+                          fname);
+        (void) sipSPISendErrorResponse(request, SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED,
+                                       SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED_PHRASE,
+                                       SIP_WARN_PROCESSING_PREVIOUS_REQUEST,
+                                       NULL, NULL);
+        return;
+    }
+
+    /* Require: header */
+    require = sippmh_get_cached_header_val(request, REQUIRE);
+    if (require) {
+        ccb->sip_require = strlib_update(ccb->sip_require, require);
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Unsupported Require Header in UPDATE\n",
+                          DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+        sipSPISendInviteResponse(ccb, SIP_CLI_ERR_EXTENSION,
+                                 SIP_CLI_ERR_EXTENSION_PHRASE,
+                                 0, NULL, FALSE, /*no SDP */ TRUE /*reTx */);
+        return;
+    }
+
+    /* update the contact information if needed */
+    contact = sippmh_get_cached_header_val(request, CONTACT);
+    if (contact) {
+        if (ccb->contact_info) {
+            sippmh_free_contact(ccb->contact_info);
+        }
+        ccb->contact_info = sippmh_parse_contact(contact);
+    }
+
+
+
+    /*
+     * Process SDP
+     */
+    sdp_status = sip_util_extract_sdp(ccb, request);
+
+    switch (sdp_status) {
+    case SIP_SDP_SESSION_AUDIT:
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Received Session Audit SDP in UPDATE\n",
+            DEB_F_PREFIX_ARGS(SIP_SDP, fname));
+        /*FALLTHROUGH*/
+
+    case SIP_SDP_SUCCESS:
+        /*
+         * If UAS receives UPDATE before UAS has generated answer to
+         * previous offer, UAS must respond with 500 which includes a
+         * Retry-after header field with a random value between 0 and
+         * 10 seconds.
+         *
+         * If UAS receives UPDATE before receiving an answer to an offer
+         * made by the UAS, the UAS must respond with 491.
+         */
+        if (ccb->oa_state != OA_IDLE) {
+            cc_causes_t cause;
+
+            cause = (ccb->oa_state == OA_OFFER_SENT ?
+                    CC_CAUSE_REQUEST_PENDING : CC_CAUSE_NO_RESOURCE);
+            (void) sipSPISendUpdateResponse(ccb, FALSE, cause, FALSE);
+            return;
+        }
+        /*
+         * Process Call-info header, if any
+         */
+        if (sdp_status == SIP_SDP_SESSION_AUDIT) {
+            /* Update call info to UI can be not delayed */
+            ccsip_update_callinfo(ccb, request, TRUE, TRUE, FALSE);
+        } else {
+            /* Update call info to UI can be delayed */
+            ccsip_update_callinfo(ccb, request, TRUE, TRUE, TRUE);
+        }
+
+        /*
+         * Check to see if received SDP indicates hold. If it is not
+         * a hold SDP, then we received a new media invite.
+         * Send FEATURE CC event to GSM.
+         */
+        ccb->oa_state = OA_OFFER_RECEIVED;
+        /* Move the message body from the SIP msg. into CCAPI msg */
+        sip_cc_mv_msg_body_to_cc_msg(&data.resume.msg_body, request);
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_MEDIA, &data);
+        sip_sm_change_state(ccb,
+                            SIP_STATE_RECV_UPDATEMEDIA_CCFEATUREACK_PENDING);
+        break;
+
+    case SIP_SDP_ERROR:
+        (void) sipSPISendErrorResponse(ccb->last_request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC, "Invalid SDP", ccb);
+        return;
+
+    case SIP_SDP_DNS_FAIL:
+        sipSPISendInviteResponse(ccb, SIP_SERV_ERR_INTERNAL,
+                                 SIP_SERV_ERR_INTERNAL_PHRASE,
+                                 SIP_WARN_MISC,
+                                 "DNS lookup failed for media destination",
+                                 FALSE, FALSE);
+        return;
+
+    case SIP_SDP_NO_MEDIA:
+        (void) sipSPISendErrorResponse(ccb->last_request, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       "No acceptable media line in SDP", ccb);
+        return;
+
+    case SIP_SDP_NOT_PRESENT:
+    default:
+        /* Update call info to UI can be not delayed */
+        ccsip_update_callinfo(ccb, request, TRUE, TRUE, FALSE);
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_UPDATE, NULL);
+        (void) sipSPISendUpdateResponse(ccb, FALSE, CC_CAUSE_OK, FALSE);
+        break;
+    }
+
+}
+
+/***********************************************************************
+ * Function: ccsip_handle_recvupdatemedia_ccfeatureackpending_ev_cc_feature_ack
+ *
+ * Description: This function handles a CC_FEATURE_ACK from GSM that the
+ * GSM sends in response to an new media SDP or hold received in an UPDATE
+ * message and sent to it.
+ ***********************************************************************/
+void
+ccsip_handle_recvupdatemedia_ccfeatureackpending_ev_cc_feature_ack (
+                                                           ccsipCCB_t *ccb,
+                                                           sipSMEvent_t *event)
+{
+    cc_features_t   feature_type;
+    cc_causes_t     cause;
+    cc_msgbody_info_t *msg_body;
+
+    feature_type = event->u.cc_msg->msg.feature_ack.feature_id;
+    cause = event->u.cc_msg->msg.feature_ack.cause;
+
+    switch (feature_type) {
+    case CC_FEATURE_HOLD:
+        if (cause == CC_CAUSE_NORMAL) {
+            cause = CC_CAUSE_OK;
+        }
+        if (event->u.cc_msg->msg.feature_ack.data_valid) {
+            msg_body = &event->u.cc_msg->msg.feature_ack.data.hold.msg_body;
+            ccsip_save_local_msg_body(ccb, msg_body);
+        }
+        (void) sipSPISendUpdateResponse(ccb,
+                                        event->u.cc_msg->msg.feature.data_valid,
+                                        cause, FALSE);
+        sip_sm_change_state(ccb, SIP_STATE_ACTIVE);
+        break;
+
+    case CC_FEATURE_RESUME:
+    case CC_FEATURE_MEDIA:
+        if (cause == CC_CAUSE_NORMAL || cause == CC_CAUSE_NO_RESUME) {
+            cause = CC_CAUSE_OK;
+        }
+
+        /* new media acks put the sdp in the resume area as well */
+        if (event->u.cc_msg->msg.feature_ack.data_valid) {
+            msg_body = &event->u.cc_msg->msg.feature_ack.data.resume.msg_body;
+            ccsip_save_local_msg_body(ccb, msg_body);
+        }
+
+        (void) sipSPISendUpdateResponse(ccb,
+                                        event->u.cc_msg->msg.feature.data_valid,
+                                        cause, FALSE);
+        sip_sm_change_state(ccb, SIP_STATE_ACTIVE);
+        break;
+
+    default:
+        break;
+    }
+}
+
+/***********************************************************************
+ * Function: ccsip_handle_timer_glare_avoidance
+ *
+ * Description: This function handles the glare condition previously
+ * detected
+ ***********************************************************************/
+void
+ccsip_handle_timer_glare_avoidance (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "timer_glare_avoidance";
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY), ccb->index,
+                      ccb->dn_line, fname, "Resending message");
+
+    // Check if this message still needs to be sent.
+    if (ccb->state == SIP_STATE_IDLE ||
+        ccb->state == SIP_STATE_RELEASE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"LINE %d CCB no longer used - message not sent!\n",
+                          fname, ccb->index);
+        return;
+    }
+
+    // Note that the assumption here is that the glare condition can only happen
+    // on an INVITE. Perhaps some more checks could be made here to make sure
+    // this was the case
+    (void) sipSPISendInviteMidCall(ccb, FALSE);
+}
+
+/***********************************************************************
+ *
+ * SIPTaskProcessSIPNotifyServiceControl
+ *
+ * Handles an incoming unsolicited NOTIFY service control message
+ *
+ * Parameters:   Incoming pSipMessage
+ *
+ * Return Value: scb if message is validated
+ *               NULL if validation fails
+ *
+ ***********************************************************************/
+sipServiceControl_t *
+ccsip_get_notify_service_control (sipMessage_t *pSipMessage)
+{
+    const char     *fname = "ccsip_get_notify_service_control";
+    sipServiceControl_t *scp;
+    line_t          i;
+    ccsipCCB_t     *ccb = NULL;
+    boolean         param_match = FALSE;
+
+    // Check the body
+    if (pSipMessage->mesg_body[0].msgBody == NULL) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received NOTIFY with no body\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_NO_BODY,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return NULL;
+    }
+    if (pSipMessage->mesg_body[0].msgContentTypeValue != SIP_CONTENT_TYPE_TEXT_PLAIN_VALUE) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received NOTIFY with unknown body type\n",
+            DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_BAD_BODY_ENCODING,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return NULL;
+    }
+    // Parse the body
+    scp = sippmh_parse_service_control_body(pSipMessage->mesg_body[0].msgBody,
+                                pSipMessage->mesg_body[0].msgLength);
+
+    if (scp == NULL) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received NOTIFY but couldn't parse body\n",
+            DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_BAD_BODY_ENCODING,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return NULL;
+    }
+    // Verify common mandatory parms
+    if (scp->registerCallID == NULL) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received NOTIFY but no mandatory params\n",
+            DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    0, NULL, NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        sippmh_free_service_control_info(scp);
+        return NULL;
+    }
+
+
+    if (scp->action == SERVICE_CONTROL_ACTION_CHECK_VERSION) {
+        // make sure they gave us all the version stamps
+        boolean all_provided;
+
+        all_provided = ((scp->configVersionStamp != NULL) &&
+                        (scp->dialplanVersionStamp != NULL) &&
+                        (scp->softkeyVersionStamp != NULL)) ? TRUE : FALSE;
+        if (!all_provided) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received NOTIFY but no mandatory params\n",
+                DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+            sippmh_free_service_control_info(scp);
+            return NULL;
+        }
+    }
+
+    if (scp->action == SERVICE_CONTROL_ACTION_APPLY_CONFIG) {
+        // make sure they gave us all the mandatory parameters
+        boolean all_provided;
+
+        /*
+         * following condition checks that all mandatory filed are present.
+         * firmwareInactiveLoadId is not mandatory field because of  backward
+         * compatibility purposes. So, it is not included in the following
+         * check.
+         */
+        all_provided = ((scp->configVersionStamp != NULL) &&
+                        (scp->dialplanVersionStamp != NULL) &&
+                        (scp->softkeyVersionStamp != NULL) &&
+                        (scp->cucm_result != NULL) &&
+                        (scp->firmwareLoadId != NULL) &&
+                        (scp->loadServer != NULL) &&
+                        (scp->logServer != NULL)) ? TRUE : FALSE;
+        if (!all_provided)
+        {
+            CCSIP_DEBUG_TASK(SIP_F_PREFIX "Incorrect message format or missing "
+                   "param value for [configVer/cucmResult] in"
+                   " apply-config NOTIFY\n\n"
+                   "configVersionStamp=%s \ndialplanVersionStamp=%s"
+                   "\nsoftkeyVersionStamp=%s \ncucmResult=%s "
+                   "\nloadId=%s \ninactiveLoadId=%s \nloadServer=%s \nlogServer=%s "
+                   "\nppid=%s\n\n", fname,
+                   (scp->configVersionStamp != NULL) ? scp->configVersionStamp
+                                                     : "",
+                   (scp->dialplanVersionStamp != NULL) ?
+                               scp->dialplanVersionStamp:"",
+                   (scp->softkeyVersionStamp != NULL) ?
+                               scp->softkeyVersionStamp : "",
+                   scp->cucm_result != NULL ? scp->cucm_result : "",
+                   (scp->firmwareLoadId != NULL) ? scp->firmwareLoadId : "",
+                   (scp->firmwareInactiveLoadId != NULL) ? scp->firmwareInactiveLoadId : "",
+                   (scp->loadServer != NULL) ? scp->loadServer : "",
+                   (scp->logServer != NULL) ? scp->logServer : "",
+                   scp->ppid == TRUE? "True": "False");
+
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+            sippmh_free_service_control_info(scp);
+            return NULL;
+        }
+        /*
+         * Now if firmwareInactiveLoadId is null, then allocate empty string to
+         * it so that an empty string can be passed on to java side.
+         */
+        if (scp->firmwareInactiveLoadId == NULL) {
+            scp->firmwareInactiveLoadId = cpr_calloc(1, 2);
+        }
+    }
+
+
+    // Take action
+    // Compare the reg call id received with call-id that we sent out
+    for (i = REG_CCB_START; i <= TEL_CCB_END + 1; i++) {
+        ccb = sip_sm_get_ccb_by_index(i);
+        if (ccb) {
+            if (strcmp(scp->registerCallID, ccb->sipCallID) == 0) {
+                param_match = TRUE;
+                break;
+            }
+        }
+    }
+
+    // Call Platform API for actually performing the called for action
+    if (param_match && ccb != NULL) {
+        if (sip_regmgr_get_cc_mode(ccb->dn_line) != REG_MODE_CCM) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received NOTIFY in non CCM mode\n",
+                DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+            sippmh_free_service_control_info(scp);
+            return NULL;
+        }
+    } else {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received NOTIFY, callid doesn't match\n",
+            DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    0, NULL, NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        sippmh_free_service_control_info(scp);
+        return NULL;
+    }
+
+    /* NOTIFY message validated and scp allocated */
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received NOTIFY, callid matches\n",
+        DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+    return scp;
+}
+
+void
+ccsip_handle_unsolicited_notify (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "ccsip_handle_unsolicited_notify";
+    sipMessage_t   *request;
+    sipServiceControl_t *scp;
+
+    /* Unpack the event */
+    request = event->u.pSipMessage;
+
+    scp = ccsip_get_notify_service_control(request);
+    if (scp != NULL) {
+        if (scp->action == SERVICE_CONTROL_ACTION_CALL_PRESERVATION) {
+            if (ccb->state == SIP_STATE_ACTIVE) {
+                sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_CALL_PRESERVATION, NULL);
+            } else {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SIP state %s ignoring call preservation request\n",
+                                  fname, sip_util_state2string(ccb->state));
+            }
+            if (sipSPISendErrorResponse(request, 200, SIP_SUCCESS_SETUP_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_SUCCESS_SETUP);
+
+
+            }
+        } else {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Unsupported unsolicited notify event\n",
+                DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+            if (sipSPISendErrorResponse(request, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+        }
+        sippmh_free_service_control_info(scp);
+    }
+}
+
+
+
+/**
+ * This function frees a duped CCB.
+ *
+ * @param dup_ccb  The duplicate CCB to free
+ */
+void free_duped (ccsipCCB_t *dup_ccb)
+{
+
+}
+
+/**
+ * This function dups the origCCB and returns a new ccb. It applies
+ * to only TEL CCBs. The contents of the new CCB is determined by dupCCBFlags.
+ *
+ * dupCCBFlag values are:
+ *     DUPED_CCB             0x01
+ *     DUP_CCB_NEW_CALLID    0x02
+ *     DUP_CCB_INIT_STATE    0x04
+ *     DUP_CCB_REINIT_DNS    0x08
+ *     DUP_CCB_STOLEN_FEAT_DATA 0x10
+ * if this flag is set and ccb->feature_data is NULL, then
+ * feature data was stolen from this ccb.
+ *
+ * if this flag is set and ccb->feature_data is non-NULL, then
+ * this ccb stole the feature_data pointer from the mother_ccb.
+ *
+ * The concept of duping is to maximize sharing of
+ * data structures by pointer sharing where it makes sense.
+ * The companion function of freeDupedCCB() is aware that
+ * of the duping. ccb->dup_flags will remember the duping
+ * attributes (flags shown above) and will free members
+ * appropriately.
+ *
+ * @param origCCB
+ * @param dup_flags
+ *
+ * @return duplicated CCB or NULL
+ */
+ccsipCCB_t *
+create_dupCCB (ccsipCCB_t *origCCB, int dup_flags)
+{
+    ccsipCCB_t *dupCCB;
+    line_t child_line;
+    const char *fname = "dupCCB()";
+    const char *outOfDialogPrefix = "OutOfDialog--";
+
+    dupCCB = sip_sm_get_ccb_next_available(&child_line); //need to check for failure
+
+    if (dupCCB == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_sm_get_ccb_next_available()"
+            " returned null.\n", fname);
+        return NULL;
+    }
+
+    dupCCB->dup_flags = dup_flags|DUP_CCB;
+    dupCCB->mother_ccb = (void *)origCCB;
+
+    dupCCB->flags = origCCB->flags;
+    sstrncpy(dupCCB->sipCallID, origCCB->sipCallID, MAX_SIP_CALL_ID);
+    dupCCB->gsm_id = origCCB->gsm_id;
+    dupCCB->con_call_id = origCCB->con_call_id;
+    dupCCB->blind_xfer_call_id = origCCB->blind_xfer_call_id;
+
+    dupCCB->state = origCCB->state;
+    dupCCB->index = origCCB->index;
+    dupCCB->dn_line = origCCB->dn_line;
+    dupCCB->retx_counter = 0;
+    dupCCB->type = origCCB->type;
+
+    dupCCB->proxySelection = origCCB->proxySelection;
+    dupCCB->outBoundProxyAddr = origCCB->outBoundProxyAddr;
+    dupCCB->outBoundProxyPort = origCCB->outBoundProxyPort;
+
+    if (!(dup_flags & DUP_CCB_REINIT_DNS)) {
+        dupCCB->SRVhandle = NULL;
+        dupCCB->ObpSRVhandle = NULL;
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Reiniting DNS fields in duped ccb\n",
+            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+    } else {
+        //TBD
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Not reiniting DNS fields in duped ccb\n",
+            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+    }
+
+    dupCCB->routeMode = origCCB->routeMode;
+    dupCCB->udpId = origCCB->udpId;
+
+    dupCCB->contact_info = NULL;
+
+    if (dup_flags & DUP_CCB_NEW_CALLID) {
+        dupCCB->record_route_info = NULL;
+        dupCCB->sipCallID[0] = '\0';
+        sip_util_get_new_call_id(dupCCB);
+        sstrncpy(dupCCB->sipCallID, outOfDialogPrefix,
+                sizeof(dupCCB->sipCallID));
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Using new Call-ID for OutofDialog ccb\n",
+            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+    } else {
+        dupCCB->record_route_info =
+            sippmh_copy_record_route(origCCB->record_route_info);
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Copied mother CCB's route set.\n",
+            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+    }
+
+    dupCCB->calledDisplayedName = strlib_update(dupCCB->calledDisplayedName,
+                                                origCCB->calledDisplayedName);
+    dupCCB->callingNumber = strlib_update(dupCCB->callingNumber,
+                                          origCCB->callingNumber);
+    dupCCB->callingDisplayName = strlib_update(dupCCB->callingDisplayName,
+                                               origCCB->callingDisplayName);
+    dupCCB->calledNumber = strlib_update(dupCCB->calledNumber,
+                                         origCCB->calledNumber);
+    dupCCB->calledNumberLen = origCCB->calledNumberLen;
+    dupCCB->calledNumberFirstDigitDialed = origCCB->calledNumberFirstDigitDialed;
+
+    dupCCB->src_addr = origCCB->src_addr;
+    dupCCB->dest_sip_addr = origCCB->dest_sip_addr;
+    dupCCB->local_port = origCCB->local_port;
+    dupCCB->dest_sip_port = origCCB->dest_sip_port;
+    //int16_t             sip_socket_handle; NOT USED ANYWHERE!
+
+
+    /*
+     * Headers
+     */
+    sstrncpy(dupCCB->ReqURI, origCCB->ReqURI, sizeof(dupCCB->ReqURI));
+    dupCCB->ReqURIOriginal = strlib_update(dupCCB->ReqURIOriginal,
+                                           origCCB->ReqURIOriginal);
+
+    dupCCB->sip_to = strlib_update(dupCCB->sip_to, origCCB->sip_to);
+    if (dup_flags & DUP_CCB_NEW_CALLID) {
+        char *str, *str2;
+        str = strlib_open(dupCCB->sip_to, 0);
+
+        if (str == NULL) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"strlib_open returned NULL while trying"
+                              " to process To.", fname);
+            return NULL;
+        }
+
+        str2 = strstr(str,";tag");
+        if (str2 != NULL) {
+            *str2 = '\0';
+        }
+        dupCCB->sip_to = strlib_close(str);
+    }
+
+    dupCCB->sip_from = strlib_update(dupCCB->sip_from, origCCB->sip_from);
+    if (dup_flags & DUP_CCB_NEW_CALLID) {
+        char *str, *str2;
+        str = strlib_open(dupCCB->sip_from, 0);
+
+        if (str == NULL) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"strlib_open returned NULL while trying"
+                              " to process From.", fname);
+            return NULL;
+        }
+
+        str2 = strstr(str,";tag");
+        if (str2 != NULL) {
+            *str2 = '\0';
+        }
+        dupCCB->sip_from = strlib_close(str);
+    }
+
+    {
+        char tag[MAX_SIP_URL_LENGTH];
+        sip_util_make_tag(tag);
+
+        if (dupCCB->flags & INCOMING) {
+            dupCCB->sip_to = strlib_append(dupCCB->sip_to, ";tag=");
+            dupCCB->sip_to = strlib_append(dupCCB->sip_to, tag);
+            dupCCB->sip_to_tag = strlib_update(dupCCB->sip_to_tag, tag);
+        } else {
+            dupCCB->sip_from = strlib_append(dupCCB->sip_from, ";tag=");
+            dupCCB->sip_from = strlib_append(dupCCB->sip_from, tag);
+            dupCCB->sip_from_tag = strlib_update(dupCCB->sip_from_tag, tag);
+        }
+    }
+
+    dupCCB->sip_contact = strlib_update(dupCCB->sip_contact,
+                                        origCCB->sip_contact);
+
+    dupCCB->featuretype = origCCB->featuretype;
+
+    if (dup_flags & DUP_CCB_STOLEN_FEAT_DATA) {
+        dupCCB->feature_data = origCCB->feature_data;
+        dupCCB->dup_flags |= DUP_CCB_STOLEN_FEAT_DATA;
+        origCCB->feature_data = NULL;
+        origCCB->dup_flags |= DUP_CCB_STOLEN_FEAT_DATA;
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Stealing feature_data pointer from mother ccb\n",
+                DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+    }
+
+    return dupCCB;
+}
+
+void
+ccsip_handle_sent_ood_refer_ev_sip_1xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "ccsip_handle_sent_ood_refer_ev_sip_1xx";
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY),
+                      ccb->index, ccb->dn_line, fname,
+                      sip_util_state2string(ccb->state),
+                      sip_util_event2string(event->type));
+
+    ccsip_handle_sentinvite_ev_sip_1xx(ccb, event);
+}
+
+void
+ccsip_handle_sent_ood_refer_ev_sip_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "ccsip_handle_sent_ood_refer_ev_sip_2xx";
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY),
+                      ccb->index, ccb->dn_line, fname,
+                      sip_util_state2string(ccb->state),
+                      sip_util_event2string(event->type));
+
+    ccsip_handle_accept_2xx(ccb, event);
+    sip_sm_call_cleanup(ccb);
+}
+
+void
+ccsip_handle_sent_ood_refer_ev_sip_fxx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "ccsip_handle_sent_ood_refer_ev_sip_fxx";
+    sipMessage_t   *response;
+    sipRespLine_t  *respLine = NULL;
+    uint16_t        status_code = 0;
+    sipMethod_t     method = sipMethodInvalid;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY),
+                      ccb->index, ccb->dn_line, fname,
+                      sip_util_state2string(ccb->state),
+                      sip_util_event2string(event->type));
+
+    response = event->u.pSipMessage;
+
+    if (sipGetResponseMethod(response, &method) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipGetResponseMethod");
+        free_sip_message(response);
+        return;
+    }
+
+    /* Get the status code */
+    respLine = sippmh_get_response_line(response);
+    if (respLine) {
+        status_code = respLine->status_code;
+        //status_code = 408;
+        SIPPMH_FREE_RESPONSE_LINE(respLine);
+    }
+
+    ccsip_handle_sentinvite_ev_sip_fxx(ccb, event);
+
+    if ((status_code != SIP_CLI_ERR_UNAUTH) &&
+        (status_code != SIP_CLI_ERR_PROXY_REQD)) {
+        sip_sm_call_cleanup(ccb);
+    }
+}
+
+void
+ccsip_handle_ev_cc_info (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    sipSPISendInfo(ccb,
+                   (const char *)event->u.cc_msg->msg.info.info_package,
+                   (const char *)event->u.cc_msg->msg.info.content_type,
+                   (const char *)event->u.cc_msg->msg.info.message_body);
+}
+
+/*
+ *  Function: ccsip_set_replace_info
+ *
+ *  Parameters:
+ *     ccb   - pointer to ccsipCCB_t.
+ *     setup - pointer to the cc_setup_t for the cc setup msg. that
+ *             contains "replace" flag.
+ *
+ *  Description:
+ *     The ccsip_set_replace_info populates the information in the CCB
+ *     for replace information of the INVITE to send out. The function
+ *     may derives certain information from the remote dialog, etc.
+ *
+ *  Returns:
+ *     TRUE  - when successfully set replace information.
+ *     FALSE - when fail to set replace information.
+ */
+static boolean
+ccsip_set_replace_info (ccsipCCB_t *ccb, cc_setup_t * setup)
+{
+    cc_call_info_t *call_info = &setup->call_info;
+
+    if (call_info->type != CC_FEAT_REPLACE) {
+        /* The call info does not contain the replace information */
+        return (FALSE);
+    }
+
+    if (call_info->data.replace.remote_call_id != CC_NO_CALL_ID) {
+        /* This is a replacement of the remote dialog */
+        strlib_free(ccb->sipxfercallid);
+        ccb->sipxfercallid = strlib_empty();
+    }
+    return (FALSE);
+}
+
+/*
+ *  Function: ccsip_handle_cc_select_event
+ *
+ *  Parameters:
+ *     sip_sm_event - Pointer to sipSMEvent_t
+ *
+ *  Description:
+ *     The ccsip_handle_cc_select_event checks for CC_FEATURE_SELECT and
+ *     handled the request.
+ *
+ *  Returns:
+ *     TRUE  - CC_FEATURE select has been handled.
+ *     FALSE - CC_FEATURE select has not been handled.
+ */
+static boolean
+ccsip_handle_cc_select_event (sipSMEvent_t *sip_sm_event)
+{
+    cc_feature_t *msg;
+
+    // currently not used: line_t        line_number = 0;
+
+    msg = &(sip_sm_event->u.cc_msg->msg.feature);
+    if (!((msg->msg_id == CC_MSG_FEATURE) &&
+          (msg->feature_id == CC_FEATURE_SELECT))) {
+        /* Some other feature or msg. */
+        return (FALSE);
+    }
+
+    return FALSE;
+}
+
+/*
+ *  Function: ccsip_handle_cc_b2bjoin_event
+ *
+ *  Parameters:
+ *     sip_sm_event - Pointer to sipSMEvent_t
+ *
+ *  Description:
+ *     The ccsip_handle_cc_b2bjoin_event checks for CC_FEATURE_B2BJOIN and
+ *     handled the request.
+ *
+ *  Returns:
+ *     TRUE  - CC_FEATURE select has been handled.
+ *     FALSE - CC_FEATURE select has not been handled.
+ */
+static boolean
+ccsip_handle_cc_b2bjoin_event (sipSMEvent_t *sip_sm_event)
+{
+    cc_feature_t *msg;
+
+    // currently not used: line_t        line_number = 0;
+
+    msg = &(sip_sm_event->u.cc_msg->msg.feature);
+    if (!((msg->msg_id == CC_MSG_FEATURE) &&
+          (msg->feature_id == CC_FEATURE_B2B_JOIN))) {
+        /* Some other feature or msg. */
+        return (FALSE);
+    }
+
+    return FALSE;
+}
+
+/*
+ *  Function: ccsip_set_join_info
+ *
+ *  Parameters:
+ *     ccb   - pointer to ccsipCCB_t.
+ *     setup - pointer to the cc_setup_t for the cc setup msg.
+ *
+ *  Description:
+ *     The ccsip_set_join_info populates the information in the CCB
+ *     for join header to be sent out in the INVITE. The function
+ *     may derives certain information from the remote dialog, etc.
+ *
+ *  Returns:
+ *     None.
+ */
+static void
+ccsip_set_join_info (ccsipCCB_t *ccb, cc_setup_t * setup)
+{
+    const char     *fname = "ccsip_set_join_info";
+    cc_call_info_t *call_info = &setup->call_info;
+
+    if (((call_info->type != CC_FEAT_BARGE) &&
+         (call_info->type != CC_FEAT_CBARGE)) ||
+        (call_info->data.join.join_call_id == CC_NO_CALL_ID)) {
+        /* The call info does not contain any barge information */
+        return;
+    }
+
+    ccb->join_info = (sipJoinInfo_t *) cpr_calloc(1, sizeof(sipJoinInfo_t));
+
+    if (!ccb->join_info) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname, "malloc(join_info)");
+        return;
+    }
+}
+
+/*
+ *  Function: ccsip_get_join_info
+ *
+ *  Parameters:
+ *     ccb     - pointer to ccsipCCB_t.
+ *     request - pointer to the sipMessage_t for the sip msg.
+ *
+ *  Description:
+ *     The ccsip_get_join_info parses the join header, finds the
+ *     the call specified by the call id, matches the from/to tag and
+ *     makes sure that this call is in active state. Then, it finds the
+ *     gsm of the join target call id and populates the call info with that
+ *
+ *  Returns:
+ *     TRUE  - join hdr is good.
+ *     FALSE - join hdr is not good.
+ */
+static boolean
+ccsip_get_join_info (ccsipCCB_t *ccb, sipMessage_t *request)
+{
+    const char *fname = "ccsip_get_join_info";
+    const char *joinhdr = NULL;
+
+    /* Parse Join header */
+    joinhdr = sippmh_get_header_val(request, SIP_HEADER_JOIN, NULL);
+    if (joinhdr) {
+
+        ccsipCCB_t *join_ccb = NULL;
+
+               // From-tag and To-tag check to match an active call
+               int ff_check = 0;
+               int ft_check = 0;
+               int tf_check = 0;
+               int tt_check = 0;
+
+        // currently not used: boolean joinhdr_not_valid = FALSE;
+
+        if (ccb->join_info) {
+            sippmh_free_join_info(ccb->join_info);
+        }
+        ccb->join_info = sippmh_parse_join_header(joinhdr);
+
+        if (ccb->join_info) {
+            join_ccb = sip_sm_get_ccb_by_callid(ccb->join_info->call_id);
+        }
+        if (join_ccb == NULL) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              0, 0, fname,
+                              "Attempted Join call does not exist");
+            return (FALSE);
+        }
+
+        if (!ccb->in_call_info) {
+            ccb->in_call_info = (cc_call_info_t *)
+                cpr_calloc(1, sizeof(cc_call_info_t));
+        }
+        if (!ccb->in_call_info) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, ccb->dn_line, fname,
+                              "malloc(call_info)");
+            return (FALSE);
+        }
+
+        ccb->in_call_info->type = CC_FEAT_MONITOR;
+
+        if (join_ccb->state != SIP_STATE_ACTIVE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              join_ccb->index,
+                              join_ccb->dn_line, fname,
+                              "Attempted Join call is not active or held, but OK to continue.");
+            /* return (FALSE); CSCtd11077 */
+            /* OK to continue to send CC_MSG_SETUP because DCSM will hold CC_FEATURE_JOIN until */
+            /* DEF is in the CONNECTED state to process the JOIN msg.                           */
+        }
+
+               ff_check = cpr_strcasecmp(join_ccb->sip_from_tag, ccb->join_info->from_tag);
+               ft_check = cpr_strcasecmp(join_ccb->sip_from_tag, ccb->join_info->to_tag);
+               tt_check = cpr_strcasecmp(join_ccb->sip_to_tag, ccb->join_info->to_tag);
+               tf_check = cpr_strcasecmp(join_ccb->sip_to_tag, ccb->join_info->from_tag);
+
+               // In this issue, the correct match is to match F/F and T/T, or to match F/T and T/F.
+               // All other cases are error.
+               // So, (ff_check==0 && tt_check==0) || (ft_check==0 && tf_check==0) is the correct match
+               // thus,  !( (ff_check==0 && tt_check==0) || (ft_check==0 && tf_check==0) ) represents all other cases.
+               if (  !( (ff_check==0 && tt_check==0) || (ft_check==0 && tf_check==0) ) ) {
+                       CCSIP_DEBUG_ERROR(
+                          get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          join_ccb->index,
+                          join_ccb->dn_line, fname,
+                          "Join Header's From/To tag does not match any ccb's From/To tag");
+               return (FALSE);
+               }
+               ccb->in_call_info->data.join.join_call_id = join_ccb->gsm_id;
+    }
+    return (TRUE);
+}
+
+/**
+ *
+ * Returns SIP call ID that is pre-allocated during off hook.
+ *
+ * @param dn_line - line_t of the line that is goes off hook.
+ *
+ * @return  pointer to character string of the SIP call ID.
+ *
+ * @pre     none
+ */
+char *
+getPreallocatedSipCallID (line_t dn_line)
+{
+    static const char *fname = "getPreallocatedSipCallID";
+    uint8_t  mac_address[MAC_ADDRESS_LENGTH];
+    char     pSrcAddrStr[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t src_addr;
+    int      nat_enable = 0;
+
+    CPR_IP_ADDR_INIT(src_addr);
+
+    if ((dn_line > MAX_REG_LINES) && (dn_line < 1)){
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"dn_line=%d is greater than %d or less than 1",
+                          fname, dn_line, MAX_REG_LINES);
+        return NULL;
+    }
+    /*
+     * if one is already created, use it.
+     */
+    if (preAllocatedSipCallID[dn_line - 1] != NULL) {
+        return (preAllocatedSipCallID[dn_line - 1]);
+    }
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        sip_config_get_net_device_ipaddr(&src_addr);
+    } else {
+        sip_config_get_nat_ipaddr(&src_addr);
+    }
+
+    platform_get_wired_mac_address(mac_address);
+    ipaddr2dotted(pSrcAddrStr, &src_addr);
+
+    preAllocatedSipCallID[dn_line - 1] = (char *) cpr_malloc(MAX_SIP_CALL_ID);
+
+    if (preAllocatedSipCallID[dn_line - 1] != NULL) {
+        sip_create_new_sip_call_id(preAllocatedSipCallID[dn_line - 1],
+                                   mac_address, pSrcAddrStr);
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed", fname);
+    }
+
+    return (preAllocatedSipCallID[dn_line - 1]);
+}
+
+/**
+ *
+ * Returns preallocate local SIP tag.
+ *
+ * @param dn_line - line_t of the line that is goes off hook.
+ *
+ * @return  pointer to character string of the tag.
+ *
+ * @pre     none
+ */
+char *
+getPreallocatedSipLocalTag (line_t dn_line)
+{
+    static const char *fname = "getPreallocatedSipLocalTag";
+
+    if ((dn_line > MAX_REG_LINES) || (dn_line < 1)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"dn_line=%d. The valid  range is 1 to %d\n",
+                          fname, dn_line, MAX_REG_LINES);
+        return NULL;
+    }
+    if (preAllocatedTag[dn_line - 1] == NULL) {
+        preAllocatedTag[dn_line - 1] = (char *) cpr_malloc(MAX_SIP_TAG_LENGTH);
+        if (preAllocatedTag[dn_line - 1] == NULL) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed\n", fname);
+        } else {
+            sip_util_make_tag(preAllocatedTag[dn_line - 1]);
+        }
+    }
+
+    return (preAllocatedTag[dn_line - 1]);
+}
+
+/**
+ *
+ * Returns the pre-allocated local TAG based on a given dn line number.
+ *
+ * @param dn_line - line_t of the line that is goes off hook.
+ *
+ * @return  pointer to character string of the tag.
+ *
+ * @pre     none
+ */
+char *
+ccsip_find_preallocated_sip_local_tag (line_t dn_line)
+{
+    static const char *fname = "ccsip_find_preallocated_sip_local_tag";
+
+    if ((dn_line > MAX_REG_LINES) || (dn_line < 1)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"dn_line=%d. The valid  range is 1 to %d\n",
+                          fname, dn_line, MAX_REG_LINES);
+        return NULL;
+    }
+
+    return (preAllocatedTag[dn_line - 1]);
+}
+
+/**
+ *
+ * Frees pre-allocated local tag that.
+ *
+ * @param dn_line - line_t of the line that is goes off hook.
+ *
+ * @return  none
+ *
+ * @pre     none
+ */
+void
+ccsip_free_preallocated_sip_local_tag (line_t dn_line)
+{
+    static const char *fname = "ccsip_free_preallocated_sip_local_tag";
+
+    if ((dn_line > MAX_REG_LINES) || (dn_line < 1)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"dn_line=%d. The valid  range is 1 to %d\n",
+                          fname, dn_line, MAX_REG_LINES);
+        return;
+    }
+
+    cpr_free(preAllocatedTag[dn_line - 1]);
+    preAllocatedTag[dn_line - 1] = NULL;
+}
+
+/**
+ *
+ * Returns the SIP Call ID based on the dn line number given.
+ *
+ * @param dn_line - line_t of the line that is goes off hook.
+ *
+ * @return  pointer to character string of the tag.
+ *
+ * @pre     none
+ */
+static char *
+ccsip_find_preallocated_sip_call_id (line_t dn_line)
+{
+    static const char *fname = "ccsip_find_preallocated_sip_call_id";
+
+    if ((dn_line > MAX_REG_LINES) && (dn_line < 1)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"dn_line=%d is greater than %d or less than 1\n",
+                          fname, dn_line, MAX_REG_LINES);
+        return NULL;
+    }
+    return (preAllocatedSipCallID[dn_line - 1]);
+}
+
+/**
+ *
+ * Frees the SIP Call ID based on the dn line number given.
+ *
+ * @param dn_line - line_t of the line that is goes off hook.
+ *
+ * @return  none
+ *
+ * @pre     none
+ */
+static void
+ccsip_free_preallocated_sip_call_id (line_t dn_line)
+{
+    static const char *fname = "ccsip_free_preallocated_sip_call_id";
+
+    if ((dn_line > MAX_REG_LINES) && (dn_line < 1)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"dn_line=%d is greater than %d or less than 1\n",
+                          fname, dn_line, MAX_REG_LINES);
+        return;
+    }
+    cpr_free(preAllocatedSipCallID[dn_line - 1]);
+    preAllocatedSipCallID[dn_line - 1] = NULL;
+}
+
+/*
+ *  Function: ccsip_handle_cc_hook_event
+ *
+ *  Parameters:
+ *     sip_sm_event - Pointer to sipSMEvent_t
+ *
+ *  Description:
+ *     The ccsip_handle_cc_hook_event checks for
+ *     CC_MSG_OFFHOOK/CC_MSG_ONHOOK and handles the request.
+ *
+ *  Returns:
+ *     TRUE  - if it is hook event.
+ *     FALSE - if it is not hook event.
+ */
+static boolean
+ccsip_handle_cc_hook_event (sipSMEvent_t *sip_sm_event)
+{
+    static const char *fname = "ccsip_handle_cc_hook_event";
+    line_t         line_number = 0;
+    char          *sip_call_id = NULL;
+    char          *sip_local_tag;
+    cc_msg_t      *pCCMsg;
+    ccsipCCB_t    *ccb;
+    cc_msgs_t      event;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Entering with event %d", DEB_F_PREFIX_ARGS(SIP_EVT, fname),
+                     sip_sm_event->u.cc_msg->msg.setup.msg_id);
+
+    pCCMsg = sip_sm_event->u.cc_msg;
+    event = pCCMsg->msg.setup.msg_id;
+
+    if ((event != CC_MSG_OFFHOOK) && (event != CC_MSG_ONHOOK)) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Exiting because event is not hook state\n",
+                         DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+        return FALSE;
+    }
+
+    if (event == CC_MSG_OFFHOOK) {
+        line_number = pCCMsg->msg.offhook.line;
+    } else {
+        line_number = pCCMsg->msg.onhook.line;
+    }
+    if (sip_regmgr_get_cc_mode(line_number) != REG_MODE_CCM) {
+        /* this is not CCM environment */
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Exiting Not CCM mode\n",
+            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+        return TRUE;
+    }
+
+    /*
+     * Allocate SIP Call-ID and local tag, if they are not already allocated.
+     */
+    if (event == CC_MSG_OFFHOOK) {
+        ccb = sip_sm_get_ccb_by_gsm_id(pCCMsg->msg.offhook.call_id);
+        line_number = pCCMsg->msg.offhook.line;
+    } else {
+        ccb = sip_sm_get_ccb_by_gsm_id(pCCMsg->msg.onhook.call_id);
+        line_number = pCCMsg->msg.onhook.line;
+    }
+    if (ccb == NULL) {
+        /*
+         * No CCB. So user must be just trying to make a call.
+         * We will pre allocate SIP Call-ID and local tag for the potential
+         * dialog initiated by INVITE. These values are needed for sending
+         * hook event notification.
+         */
+        sip_call_id = getPreallocatedSipCallID(line_number);
+        sip_local_tag = getPreallocatedSipLocalTag(line_number);
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"preallocated callid: %s & local-tag: %s\n",
+                         DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname), sip_call_id, sip_local_tag);
+    } else {
+        sip_call_id = ccb->sipCallID;
+        sip_local_tag = (char *) (ccb->sip_from_tag);
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"callid: %s & local-tag: %s\n",
+                         DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname), sip_call_id, sip_local_tag);
+    }
+
+    /*
+     * if CCB is NULL and we are sending ONKOOK notification,
+     * free up the preallocated call-id and local-tag.
+     */
+    if ((ccb == NULL) && (event == CC_MSG_ONHOOK)) {
+        ccsip_free_preallocated_sip_call_id(line_number);
+        ccsip_free_preallocated_sip_local_tag(line_number);
+    }
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Exiting after sending hook state "
+                     "notification\n",
+                     DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+    return TRUE;
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_debug.c b/libs/sipcc/core/sipstack/ccsip_debug.c
new file mode 100644 (file)
index 0000000..b7d571e
--- /dev/null
@@ -0,0 +1,392 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "debug.h"
+#include "phone_debug.h"
+#include "ccsip_publish.h"
+#include "util_string.h"
+#include "sip_interface_regmgr.h"
+
+extern boolean dump_reg_msg;
+
+/* SIP flags */
+cc_int32_t SipDebugMessage          = 1; /* SIP messaging output */
+cc_int32_t SipDebugState            = 0; /* SIP State Machine output */
+cc_int32_t SipDebugTask             = 1; /* SIP Task output */
+cc_int32_t SipDebugRegState         = 0; /* SIP Registration State Mach. output */
+
+/**
+ * SipRelDevEnabled flag is not a debug print flag. It actually turns
+ * on and off reliable delivery module in sip layer. We will keep
+ * this on as retransmissions are enabled on CUCM by default even for TCP transport
+ */
+int32_t SipRelDevEnabled           = 1; /* Is reliable delivery on? */
+
+int32_t SipDebugGenContactHeader = 0; /* Generate Contact header */
+cc_int32_t SipDebugTrx              = 0; /* SIP Transaction Layer output */
+
+cc_int32_t GSMDebug                 = 0;
+cc_int32_t FIMDebug                 = 0;
+cc_int32_t LSMDebug                 = 0;
+cc_int32_t FSMDebugSM               = 0;
+int32_t CSMDebugSM               = 0;
+cc_int32_t CCDebug                  = 0;
+cc_int32_t CCDebugMsg               = 0;
+cc_int32_t AuthDebug                = 0;
+cc_int32_t g_DEFDebug               = 1;
+cc_int32_t TNPDebug                 = 1;
+cc_int32_t VCMDebug                 = 0;
+cc_int32_t CCEVENTDebug             = 0;
+cc_int32_t PLATDebug                = 0;
+cc_int32_t TMRDebug                 = 0;
+cc_int32_t g_dcsmDebug              = 0;
+
+/**
+ *
+ * Dump sip messages
+ *
+ * @param msg - message buffer
+ *        pSIPMessage - parsed message structure
+ *        cc_remote_ipaddr - remote ip address
+ *        cc_remote_port - remote port number
+ *
+ * @return  none
+ *
+ * @pre     (valid pSIPMessage AND
+ *           valid cc_remote_ipaddr)
+ */
+
+void ccsip_dump_send_msg_info (char *msg, sipMessage_t *pSIPMessage,
+                               cpr_ip_addr_t *cc_remote_ipaddr,
+                               uint16_t cc_remote_port)
+{
+    char *disp_buf;
+    const char *req_uri;
+    const char *cseq;
+    const char *callid;
+    char ipaddr_str[MAX_IPADDR_STR_LEN];
+    static const char fname[] = "ccsip_dump_send_msg_info";
+
+    ipaddr2dotted(ipaddr_str, cc_remote_ipaddr);
+
+    req_uri = sippmh_get_header_val(pSIPMessage, SIP_HEADER_TO, NULL);
+    if (req_uri == NULL) {
+        /* No REQ URI, fill with blank */
+        req_uri = "";
+    }
+    cseq = sippmh_get_header_val(pSIPMessage, SIP_HEADER_CSEQ, NULL);
+    if (cseq == NULL) {
+        /* No REQ CSEQ, fill with blank */
+        cseq = "";
+    }
+    callid = sippmh_get_header_val(pSIPMessage, SIP_HEADER_CALLID, NULL);
+    if (callid == NULL) {
+        /* No REQ CSEQ, fill with blank */
+        callid = "";
+    }
+
+    /* For messages starting with SIP add 8 byte. default
+     * debugs do not show all the SIP message information
+     * rather show initial msg.
+     */
+    if (msg != NULL) {
+        if (msg[0] == 'S' &&
+            msg[1] == 'I' &&
+            msg[2] == 'P') {
+            disp_buf = &msg[8];
+        } else {
+            disp_buf = msg;
+        }
+        if ((strncmp(disp_buf, SIP_METHOD_REGISTER, sizeof(SIP_METHOD_REGISTER)-1) == 0) &&
+            (!dump_reg_msg)) {
+            return;
+        }
+    } else {
+        /* No msg. buffer */
+        disp_buf = NULL;
+    }
+
+
+    if (disp_buf != NULL) {
+        DEF_DEBUG(DEB_F_PREFIX"<%s:%-4d>:%c%c%c%c%c%c%c: %-10s :%-6s::%s\n",
+                    DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname),
+                    ipaddr_str, cc_remote_port,
+                    disp_buf[0],
+                    disp_buf[1],
+                    disp_buf[2],
+                    disp_buf[3],
+                    disp_buf[4],
+                    disp_buf[5],
+                    disp_buf[6],
+                    req_uri,
+                    cseq, callid);
+    } else {
+        /* No msg to send */
+        DEF_DEBUG(DEB_F_PREFIX"<%s:%-4d>: empty message\n",
+                  DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname),
+                  ipaddr_str, cc_remote_port);
+    }
+}
+
+/**
+ *
+ * Dump sip messages
+ *
+ * @param msg - message buffer
+ *        pSIPMessage - parsed message structure
+ *        cc_remote_ipaddr - remote ip address
+ *        cc_remote_port - remote port number
+ *
+ * @return  none
+ *
+ * @pre     (valid pSIPMessage AND
+ *           valid cc_remote_ipaddr)
+ */
+
+void ccsip_dump_recv_msg_info (sipMessage_t *pSIPMessage,
+                               cpr_ip_addr_t *cc_remote_ipaddr,
+                               uint16_t cc_remote_port)
+{
+    char *disp_buf;
+    const char *req_uri;
+    const char *cseq;
+    const char *callid;
+    char ipaddr_str[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t cc_ipaddr;
+    static const char fname[] = "ccsip_dump_recv_msg_info";
+
+    util_ntohl(&cc_ipaddr, cc_remote_ipaddr);
+    ipaddr2dotted(ipaddr_str, &cc_ipaddr);
+
+    req_uri = sippmh_get_cached_header_val(pSIPMessage, FROM);
+    if (req_uri == NULL) {
+        /* No REQ URI, fill with blank */
+        req_uri = "";
+    }
+    cseq = sippmh_get_cached_header_val(pSIPMessage, CSEQ);
+    if (cseq == NULL) {
+        /* No REQ CSEQ, fill with blank */
+        cseq = "";
+    }
+    callid = sippmh_get_cached_header_val(pSIPMessage, CALLID);
+    if (callid == NULL) {
+        /* No REQ CSEQ, fill with blank */
+        callid = "";
+    }
+
+    if (!dump_reg_msg) {
+       if (strstr(cseq, SIP_METHOD_REGISTER) != NULL) {
+          return;
+       }
+    }
+
+    /* For messages starting with SIP add 8 byte. default
+     * debugs do not show all the SIP message information
+     * rather show initial msg.
+     */
+    if (pSIPMessage->mesg_line != NULL) {
+        if (pSIPMessage->mesg_line[0] == 'S' &&
+            pSIPMessage->mesg_line[1] == 'I' &&
+            pSIPMessage->mesg_line[2] == 'P') {
+            disp_buf = &(pSIPMessage->mesg_line[8]);
+        } else {
+            disp_buf = pSIPMessage->mesg_line;
+        }
+    } else {
+        /*
+         * It is possible that this function is called with
+         * invalid message or partially received.
+         */
+        disp_buf = NULL;
+    }
+
+    if (disp_buf != NULL) {
+        DEF_DEBUG(DEB_F_PREFIX"<%s:%-4d>:%c%c%c%c%c%c%c: %-10s :%-6s::%s\n",
+                    DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname),
+                    ipaddr_str, cc_remote_port,
+                    disp_buf[0],
+                    disp_buf[1],
+                    disp_buf[2],
+                    disp_buf[3],
+                    disp_buf[4],
+                    disp_buf[5],
+                    disp_buf[6],
+                    req_uri,
+                    cseq, callid);
+    } else {
+        /* No line received */
+        DEF_DEBUG(DEB_F_PREFIX"<%s:%-4d>: empty message\n",
+                  DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname),
+                  ipaddr_str, cc_remote_port);
+    }
+}
+
+/**
+ *
+ * platform_print_sip_msg
+ *
+ * The function perform printing SIP msg.
+ *
+ * Parameters:   msg - pointer to character strings of the SIP messages.
+ *
+ * Return Value: None
+ *
+ */
+void
+platform_print_sip_msg (const char *msg)
+{
+    char *buf;
+    int msg_to_crypto_line_len, msg_to_digits_tag_len, buf_len;
+    const char *c_line_begin, *c_line_end;
+    static const char crypto_line_tag[] = "a=crypto:";
+    static const char crypto_mask[] = "...";
+    static const char digits_tag[] = "digits=";
+
+    if (msg == NULL) {
+        return;
+    }
+
+    buginf_msg("\n");
+    /* replace digits for security reasons */
+    if (strstr(msg, "kpml-response")) {
+        /* This is kpml response. so supress printing digits. */
+        c_line_begin = strstr(msg, digits_tag);
+        if (c_line_begin == NULL) {
+            /* No digits, print everything */
+            buginf_msg(msg);
+            return;
+        }
+        /*
+         * Calculate the length of the msg. to print from
+         * from the beginning to the end of the "digits=" i.e.
+         */
+        msg_to_digits_tag_len = c_line_begin - msg + sizeof(digits_tag);
+        buf_len = msg_to_digits_tag_len + sizeof(crypto_mask);
+        buf = (char *) cpr_malloc(buf_len);
+        if (buf == NULL) {
+            /* No memory */
+            return;
+        }
+
+        /* Copy the message upto "digits=" */
+        memcpy(buf, msg, msg_to_digits_tag_len);
+
+        /* Copy mask and the end NULL terminating char to the buffer */
+        memcpy(&buf[msg_to_digits_tag_len], crypto_mask, sizeof(crypto_mask));
+        /* Print it out now */
+        buginf_msg(buf);
+        cpr_free(buf);
+
+        c_line_end = c_line_begin + sizeof(digits_tag) + 3; /* 3 beacuse " + digit + " */
+        msg = c_line_end;
+        buginf_msg(msg);
+    } else if (sip_regmgr_get_sec_level(1) == ENCRYPTED) {
+        /* The 1st. line is using encrypted signaling */
+        while (TRUE) {
+            c_line_begin = strstr(msg, crypto_line_tag);
+            if (c_line_begin == NULL) {
+                /* No more crypto line, print whatever left and done */
+                buginf_msg(msg);
+                return;
+            } else {
+                /*
+                 * Found a crypto line:
+                 *
+                 * Allocate buffer to print the msg. including this
+                 * instance of crypto line but no crypto parameter.
+                 * For example:
+                 *
+                 * v=0
+                 * o=Cisco-SIPUA 3052 7 IN IP4 10.80.35.217
+                 * s=SIP Call
+                 * t=0 0
+                 * m=audio 28926 RTP/SAVP 0 8 18 101
+                 * c=IN IP4 10.80.35.217
+                 * a=crypto:1 AES_CM_128_HMAC_SHA1_32 inline:GqT...
+                 * a=rtpmap:0 PCMU/8000
+                 *
+                 *
+                 * The output of printing will be
+                 *
+                 * v=0
+                 * o=Cisco-SIPUA 3052 7 IN IP4 10.80.35.217
+                 * s=SIP Call
+                 * t=0 0
+                 * m=audio 28926 RTP/SAVP 0 8 18 101
+                 * c=IN IP4 10.80.35.217
+                 * a=crypto:1...
+                 * a=rtpmap:0 PCMU/8000
+                 *
+                 */
+
+                /*
+                 * Calculate the length of the msg. to print from
+                 * current position to the end of the "a=crypto:n" i.e.
+                 * from the current of msg passes the ":" by one character
+                 * after of the "a=crypto:". This allows the first character
+                 * of the crypto tag field character to be included in the
+                 * output. Note that there is no explicit adding this
+                 * extra character in the code below because of the
+                 * sizeof(crypto_line_tag) includes extra 1 byte of string
+                 * termination character already. It will be the extra
+                 * character to print after the ":".
+                 */
+                msg_to_crypto_line_len = c_line_begin - msg +
+                                         sizeof(crypto_line_tag);
+                /*
+                 * The buffer allocated to print includes the length from
+                 * current msg pointer to this instance of crypto line +
+                 * the mask length plus 1 byte for NULL terminating character.
+                 * The NULL terminating character is included in the
+                 * sizeof(crypto_mask) below.
+                 */
+                buf_len = msg_to_crypto_line_len + sizeof(crypto_mask);
+                buf = (char *) cpr_malloc(buf_len);
+                if (buf == NULL) {
+                    /* No memory */
+                    return;
+                }
+
+                /* Copy the message including this instance of crypto line */
+                memcpy(buf, msg, msg_to_crypto_line_len);
+
+                /* Copy mask and the end NULL terminating char to the buffer */
+                memcpy(&buf[msg_to_crypto_line_len], crypto_mask,
+                       sizeof(crypto_mask));
+                /* Print it out now */
+                buginf_msg(buf);
+                cpr_free(buf);
+
+                /* Find the end of the crypto line */
+                c_line_end = strpbrk(c_line_begin, "\n");
+                if (c_line_end != NULL) {
+                    /* Skip pass the "\n" character of the crypto line */
+                    msg = c_line_end + 1;
+                } else {
+                    /* Some thing is wrong, no end line character in SDP */
+                    return;
+                }
+            }
+        }
+    } else {
+        /* print full content */
+        buginf_msg(msg);
+    }
+}
+
+/*
+ *  Function: ccsip_debug_init()
+ *
+ *  Parameters: None
+ *
+ *  Description: Initialize the Debug keywords used by the SIP protocol stack.
+ *
+ *  Returns: None
+ *
+ */
+void
+ccsip_debug_init (void)
+{
+    /* Placeholder for doing any initialization related tasks */
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_info.c b/libs/sipcc/core/sipstack/ccsip_info.c
new file mode 100644 (file)
index 0000000..d58b022
--- /dev/null
@@ -0,0 +1,898 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+
+#include "ccsip_core.h"
+#include "ccsip_callinfo.h"
+#include "ccsip_messaging.h"
+#include "uiapi.h"
+#include "lsm.h"
+#include "fsm.h"
+#include "vcm.h"
+#include "phone_debug.h"
+#include "singly_link_list.h"
+#include "ccapi.h"
+
+extern int httpish_strncasecmp(const char *s1, const char *s2, size_t len);
+
+typedef unsigned int info_index_t;
+typedef unsigned int type_index_t;
+
+typedef struct {
+    info_package_handler_t handler;
+    info_index_t info_index;    // the index of the info_package in g_registered_info array
+                                // i.e., the value returned by find_info_index()
+    type_index_t type_index;    // the index of the content_type in g_registered_type array
+                                // i.e., the value returned by find_type_index()
+} handler_record_t;
+
+/* Info Package handler registry */
+static sll_handle_t s_handler_registry = NULL;
+
+#define INDEX_NOT_FOUND     ((unsigned int)-1)
+
+/*
+ * g_registered_info[] contains the Info Package strings (such as
+ * "conference") for the registered handlers.
+ */
+char *g_registered_info[MAX_INFO_HANDLER];
+
+static char *s_registered_type[MAX_INFO_HANDLER];
+
+static sll_match_e is_matching_type(void *find_by_p, void *data_p);
+static info_index_t find_info_index(const char *info_package);
+static info_index_t find_next_available_info_index(void);
+static type_index_t find_type_index(const char *type_package);
+static type_index_t find_next_available_type_index(void);
+static handler_record_t *find_handler_record(info_index_t info_index,
+                                             type_index_t type_index);
+static boolean is_info_package_registered(info_index_t info_index);
+static boolean is_content_type_registered(type_index_t type_index);
+static void update_recv_info_list(const char *header_field_value,
+                                  string_t *info_packages);
+static void media_control_info_package_handler(line_t line, callid_t call_id,
+                                               const char *info_package,
+                                               const char *content_type,
+                                               const char *message_body);
+#ifdef _CONF_ROSTER_
+static void conf_info_package_handler(line_t line, callid_t call_id,
+                                           const char *info_package,
+                                           const char *content_type,
+                                           const char *message_body);
+#endif
+
+
+
+/*
+ *  Function: find_info_index
+ *
+ *  Parameters:
+ *      info_package - the Info Package to find
+ *
+ *  Description:
+ *      Finds the Info Package in g_registered_info array.
+ *
+ *  Return:
+ *      info_index_t - the index of the Info Package in the array
+ *      INDEX_NOT_FOUND - the Info Package was not found in the array
+ */
+static info_index_t
+find_info_index(const char *info_package)
+{
+    info_index_t info_index;
+
+    for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) {
+        if (g_registered_info[info_index] &&
+            httpish_strncasecmp(info_package,
+                                g_registered_info[info_index],
+                                strlen(g_registered_info[info_index])) == 0) {
+            return info_index;
+        }
+    }
+
+    return INDEX_NOT_FOUND;
+}
+
+/*
+ *  Function: find_next_available_info_index
+ *
+ *  Parameters:
+ *      None
+ *
+ *  Description:
+ *      Finds an empty slot in g_registered_info array.
+ *
+ *  Return:
+ *      info_index_t - the index of the empty slot in the array
+ *      INDEX_NOT_FOUND - the array is full
+ */
+static info_index_t
+find_next_available_info_index(void)
+{
+    info_index_t info_index;
+
+    for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) {
+        if (g_registered_info[info_index] == NULL) {
+            return info_index;
+        }
+    }
+
+    return INDEX_NOT_FOUND;
+}
+
+/*
+ *  Function: find_type_index
+ *
+ *  Parameters:
+ *      content_type - the Content Type to find
+ *
+ *  Description:
+ *      Finds the Content Type in s_registered_type array.
+ *
+ *  Return:
+ *      type_index_t - the index of the Content Type in the array
+ *      INDEX_NOT_FOUND - the Content Type was not found in the array
+ */
+static type_index_t
+find_type_index(const char *content_type)
+{
+    type_index_t type_index;
+
+    for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) {
+        if (s_registered_type[type_index] &&
+            httpish_strncasecmp(content_type,
+                                s_registered_type[type_index],
+                                strlen(s_registered_type[type_index])) == 0) {
+            return type_index;
+        }
+    }
+
+    return INDEX_NOT_FOUND;
+}
+
+/*
+ *  Function: find_next_available_type_index
+ *
+ *  Parameters:
+ *      None
+ *
+ *  Description:
+ *      Finds an empty slot in s_registered_type array.
+ *
+ *  Return:
+ *      type_index_t - the index of the empty slot in the array
+ *      INDEX_NOT_FOUND - the array is full
+ */
+static type_index_t
+find_next_available_type_index(void)
+{
+    type_index_t type_index;
+
+    for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) {
+        if (s_registered_type[type_index] == NULL) {
+            return type_index;
+        }
+    }
+
+    return INDEX_NOT_FOUND;
+}
+
+/*
+ *  Function: is_matching_type
+ *
+ *  Parameters:
+ *      find_by_p - searching criteria
+ *      data_p - a node
+ *
+ *  Description:
+ *      Checks to see if the node matches the searching criteria.
+ *
+ *  Return:
+ *      SLL_MATCH_FOUND - the node is a match
+ *      SLL_MATCH_NOT_FOUND - the node is not a match
+ */
+static sll_match_e
+is_matching_type(void *find_by_p, void *data_p)
+{
+    handler_record_t *tuple = (handler_record_t *)find_by_p;
+    handler_record_t *node = (handler_record_t *)data_p;
+
+    if ((node->info_index == tuple->info_index) &&
+        (node->type_index == tuple->type_index)) {
+        return SLL_MATCH_FOUND;
+    }
+    return SLL_MATCH_NOT_FOUND;
+}
+
+/*
+ *  Function: find_handler_record
+ *
+ *  Parameters:
+ *      info_index - the index of the Info Package in g_registered_info array
+ *      type_index - the index of the Content Type in s_registered_type array
+ *
+ *  Description:
+ *      Finds the Info Package handler registered for the Info Package/Content
+ *      Type pair.
+ *
+ *  Return:
+ *      handler_record_t * - the registered handler record
+ *      NULL - otherwise
+ */
+static handler_record_t *
+find_handler_record(info_index_t info_index, type_index_t type_index)
+{
+    handler_record_t tuple;
+
+    tuple.info_index = info_index;
+    tuple.type_index = type_index;
+
+    return (handler_record_t *)sll_find(s_handler_registry, &tuple);
+}
+
+/*
+ *  Function: is_info_package_registered
+ *
+ *  Parameters:
+ *      info_index - the index of the Info Package in g_registered_info array
+ *
+ *  Description:
+ *      Checks to see if a handler was registered for the Info Package.
+ *
+ *  Return:
+ *      TRUE - a handler was registered for the Info Package
+ *      FALSE - otherwise
+ */
+static boolean
+is_info_package_registered(info_index_t info_index)
+{
+    handler_record_t *record;
+
+    for (record = (handler_record_t *)sll_next(s_handler_registry, NULL);
+         record != NULL;
+         record = (handler_record_t *)sll_next(s_handler_registry, record)) {
+        if (record->info_index == info_index) {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+/*
+ *  Function: is_content_type_registered
+ *
+ *  Parameters:
+ *      type_index - the index of the Content Type in s_registered_type array
+ *
+ *  Description:
+ *      Checks to see if a handler was registered for the Content Type.
+ *
+ *  Return:
+ *      TRUE - a handler was registered for the Content Type
+ *      FALSE - otherwise
+ */
+static boolean
+is_content_type_registered(type_index_t type_index)
+{
+    handler_record_t *record;
+
+    for (record = (handler_record_t *)sll_next(s_handler_registry, NULL);
+         record != NULL;
+         record = (handler_record_t *)sll_next(s_handler_registry, record)) {
+        if (record->type_index == type_index) {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+/*
+ *  Function: ccsip_register_info_package_handler
+ *
+ *  Parameters:
+ *      info_package - the Info Package for the handler
+ *      content_type - the Content Type for the handler
+ *      handler - the handler
+ *
+ *  Description:
+ *      Registers the handler for the Info Package/Content Type pair.
+ *
+ *  Return:
+ *      SIP_OK - the handler was registered successfully
+ *      SIP_ERROR - otherwise
+ */
+int
+ccsip_register_info_package_handler(const char *info_package,
+                                    const char *content_type,
+                                    info_package_handler_t handler)
+{
+    static const char *fname = "ccsip_register_info_package_handler";
+    info_index_t info_index;
+    type_index_t type_index;
+    char *tmp_info = NULL;
+    char *tmp_type = NULL;
+    handler_record_t *record;
+
+    if (s_handler_registry == NULL) {
+        CCSIP_DEBUG_TASK("%s: Info Package handler was not initialized\n", fname);
+        return SIP_ERROR;
+    }
+
+    if ((info_package == NULL) || (content_type == NULL) || (handler == NULL)) {
+        CCSIP_DEBUG_ERROR("%s: invalid parameter\n", fname);
+        return SIP_ERROR;
+    }
+
+    /* Find the info_index for the info_package */
+    info_index = find_info_index(info_package);
+
+    if (info_index == INDEX_NOT_FOUND) {
+        /* Find the first available slot */
+        info_index = find_next_available_info_index();
+
+        if (info_index == INDEX_NOT_FOUND) {
+            CCSIP_DEBUG_ERROR("%s: maximum reached\n", fname);
+            return SIP_ERROR;
+        }
+
+        tmp_info = cpr_strdup(info_package);
+        if (tmp_info == NULL) {
+            CCSIP_DEBUG_ERROR("%s: failed to duplicate info_package string\n", fname);
+            return SIP_ERROR;
+        }
+    }
+
+    /* Find the type_index for the content_type */
+    type_index = find_type_index(content_type);
+
+    if (type_index == INDEX_NOT_FOUND) {
+        /* Find the first available slot */
+        type_index = find_next_available_type_index();
+
+        if (type_index == INDEX_NOT_FOUND) {
+            CCSIP_DEBUG_ERROR("%s: maximum reached\n", fname);
+            if (tmp_info != NULL) {
+                cpr_free(tmp_info);
+            }
+            return SIP_ERROR;
+        }
+
+        tmp_type = cpr_strdup(content_type);
+        if (tmp_type == NULL) {
+            CCSIP_DEBUG_ERROR("%s: failed to duplicate info_package string\n", fname);
+            if (tmp_info != NULL) {
+                cpr_free(tmp_info);
+            }
+            return SIP_ERROR;
+        }
+    }
+
+    /* Check to see if the info/type tuple has been registered before */
+    if (find_handler_record(info_index, type_index) != NULL) {
+        CCSIP_DEBUG_ERROR("%s: Info Package handler already registered\n", fname);
+        return SIP_ERROR;
+    }
+
+    /*
+     * At this point, info_index points to the slot in g_registered_info where
+     * either:
+     *
+     * 1) the info_package is residing, or
+     * 2) the info_package (a copy of which is pointed to by *tmp_info) will be
+     *    copied to before the function returns
+     *
+     * type_index is similar.
+     */
+
+    record = (handler_record_t *)cpr_malloc(sizeof(handler_record_t));
+    if (record == NULL) {
+        if (tmp_type != NULL) {
+            cpr_free(tmp_type);
+        }
+        if (tmp_info != NULL) {
+            cpr_free(tmp_info);
+        }
+        CCSIP_DEBUG_ERROR("%s: failed to allocate info handler record\n", fname);
+        return SIP_ERROR;
+    }
+
+    record->handler = handler;
+    record->info_index = info_index;
+    record->type_index = type_index;
+
+    if (sll_append(s_handler_registry, record) != SLL_RET_SUCCESS) {
+        cpr_free(record);
+        if (tmp_type != NULL) {
+            cpr_free(tmp_type);
+        }
+        if (tmp_info != NULL) {
+            cpr_free(tmp_info);
+        }
+        CCSIP_DEBUG_ERROR("%s: failed to insert to the registry\n", fname);
+        return SIP_ERROR;
+    }
+
+    if (tmp_info != NULL) {
+        g_registered_info[info_index] = tmp_info;
+    }
+    if (tmp_type != NULL) {
+        s_registered_type[type_index] = tmp_type;
+    }
+
+    return SIP_OK;
+}
+
+/*
+ *  Function: ccsip_deregister_info_package_handler
+ *
+ *  Parameters:
+ *      info_package - the Info Package for the handler
+ *      content_type - the Content Type for the handler
+ *      handler - the handler
+ *
+ *  Description:
+ *      Deregisters the handler for the Info Package/Content Type pair.
+ *
+ *  Return:
+ *      SIP_OK - the handler was registered successfully
+ *      SIP_ERROR - otherwise
+ */
+int
+ccsip_deregister_info_package_handler(const char *info_package,
+                                      const char *content_type,
+                                      info_package_handler_t handler)
+{
+    static const char *fname = "ccsip_deregister_info_package_handler";
+    info_index_t info_index;
+    type_index_t type_index;
+    handler_record_t *record;
+
+    if (s_handler_registry == NULL) {
+        CCSIP_DEBUG_TASK("%s: Info Package handler was not initialized\n", fname);
+        return SIP_ERROR;
+    }
+
+    /* Find the info_index for the info_package */
+    info_index = find_info_index(info_package);
+    if (info_index == INDEX_NOT_FOUND) {
+        CCSIP_DEBUG_ERROR("%s: handler was not registered (%s)\n",
+                          fname, info_package);
+        return SIP_ERROR;
+    }
+
+    /* Find the type_index for the content_type */
+    type_index = find_type_index(content_type);
+    if (type_index == INDEX_NOT_FOUND) {
+        CCSIP_DEBUG_ERROR("%s: handler was not registered (%s)\n",
+                          fname, content_type);
+        return SIP_ERROR;
+    }
+
+    /* Find the handler record */
+    record = find_handler_record(info_index, type_index);
+    if ((record == NULL) || (record->handler != handler)) {
+        CCSIP_DEBUG_ERROR("%s: handler was not registered (%p)\n",
+                          fname, handler);
+        return SIP_ERROR;
+    }
+
+    (void)sll_remove(s_handler_registry, record);
+
+    cpr_free(record);
+
+    if (!is_info_package_registered(info_index)) {
+        /* The info_package was not found in the registry, meaning we're
+         * the last one who registered for this particular info_package */
+        cpr_free(g_registered_info[info_index]);
+        g_registered_info[info_index] = NULL;
+    }
+
+    if (!is_content_type_registered(type_index)) {
+        /* The content_type was not found in the registry, meaning we're
+         * the last one who registered for this particular content_type */
+        cpr_free(s_registered_type[type_index]);
+        s_registered_type[type_index] = NULL;
+    }
+
+    return SIP_OK;
+}
+
+/*
+ *  Function:  update_recv_info_list
+ *
+ *  Parameters:
+ *      header_field_value - the header field value to match (e.g.,
+ *      "conference")
+ *      info_packages - the Info Packages string to append the header
+ *      field value to
+ *
+ *  Description:
+ *      Checks to see if a handler is registered for the header field value
+ *      (e.g., "conference"), if so, append the header field value to
+ *      the end of info_packages.
+ *
+ *  Returns:
+ *      None
+ */
+static void
+update_recv_info_list(const char *header_field_value, string_t *info_packages)
+{
+    static const char *fname = "update_recv_info_list";
+    info_index_t info_index;
+
+    if ((header_field_value == NULL) || (info_packages == NULL) ||
+        (*info_packages == NULL)) {
+        CCSIP_DEBUG_ERROR("%s: invalid parameter\n", fname);
+        return;
+    }
+
+    info_index = find_info_index(header_field_value);
+    if (info_index != INDEX_NOT_FOUND) {
+        /* Info-Package is supported */
+        if (**info_packages == '\0') {
+            *info_packages = strlib_update(*info_packages,
+                                           g_registered_info[info_index]);
+        } else {
+            *info_packages = strlib_append(*info_packages, ", ");
+            *info_packages = strlib_append(*info_packages,
+                                           g_registered_info[info_index]);
+        }
+    }
+}
+
+/*
+ *  Function:  ccsip_parse_send_info_header
+ *
+ *  Parameters:
+ *      ccb         - the SIP CCB
+ *      pSipMessage - the SIP message
+ *
+ *  Description:
+ *      Checks the Send-Info header (if exists) to see if a handler was
+ *      registered for the Info Package.  If so, append the Info Package
+ *      to the end of recv_info_list.
+ *
+ *  Returns:
+ *      None
+ */
+void
+ccsip_parse_send_info_header(sipMessage_t *pSipMessage, string_t *recv_info_list)
+{
+    char *send_info[MAX_INFO_HANDLER];
+    int count;
+    int i;
+    char *header_field_values;
+    char *header_field_value;
+    char *separator;
+
+    // leading white spaces are trimmed, but not trailing ones
+    count = sippmh_get_num_particular_headers(pSipMessage,
+                                              SIP_HEADER_SEND_INFO,
+                                              NULL,
+                                              send_info,
+                                              MAX_INFO_HANDLER);
+
+    if (count == 0) {
+        return;
+    }
+
+    for (i = 0; (i < count) && (i < MAX_INFO_HANDLER); i++) {
+        header_field_values = cpr_strdup(send_info[i]);
+        if (header_field_values == NULL) {
+            return;
+        }
+        header_field_value = header_field_values;
+
+        while ((separator = strchr(header_field_value, COMMA)) != NULL) {
+            *separator++ = '\0';
+            update_recv_info_list(header_field_value, recv_info_list);
+            header_field_value = separator;
+            SKIP_WHITE_SPACE(header_field_value);
+        }
+        update_recv_info_list(header_field_value, recv_info_list);
+
+        cpr_free(header_field_values);
+    }
+}
+
+/*
+ *  Function: ccsip_handle_info_package
+ *
+ *  Parameters:
+ *      ccb         - the SIP CCB
+ *      pSipMessage - the SIP message
+ *
+ *  Description:
+ *      Handles an incoming unsolicited Info Package message.
+ *      XXX Currently this function only handles the first part in a
+ *          multi-part Info Package message.
+ *
+ *  Return:
+ *      SIP_OK - if request processed.
+ *      SIP_ERROR - if there is error
+ */
+int
+ccsip_handle_info_package(ccsipCCB_t *ccb, sipMessage_t *pSipMessage)
+{
+    static const char *fname = "ccsip_handle_info_package";
+    const char  *info_package;
+    const char  *content_type;
+    info_index_t info_index;
+    type_index_t type_index;
+    handler_record_t *record;
+    uint16_t     status_code;
+    const char  *reason_phrase;
+    int          return_code = SIP_ERROR;
+
+    /* FIXME Media Control currently does not follow the IETF draft
+             draft-ietf-sip-info-events-01, so short-circuit here and
+             bypass all the Info Package related stuff below. */
+    // leading white spaces are trimmed, but not trailing ones
+    content_type = sippmh_get_cached_header_val(pSipMessage,
+                                                CONTENT_TYPE);
+    if (content_type &&
+        httpish_strncasecmp(content_type,
+                            SIP_CONTENT_TYPE_MEDIA_CONTROL,
+                            strlen(SIP_CONTENT_TYPE_MEDIA_CONTROL)) == 0) {
+
+        media_control_info_package_handler(ccb->dn_line, ccb->gsm_id,
+                                           "",  // legacy mode, no Info Package
+                                           SIP_CONTENT_TYPE_MEDIA_CONTROL,
+                                           pSipMessage->mesg_body[0].msgBody);
+
+        if (sipSPISendErrorResponse(pSipMessage, 200, SIP_SUCCESS_SETUP_PHRASE,
+                                    0, NULL, NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_SUCCESS_SETUP_PHRASE);
+            return SIP_ERROR;
+        }
+
+        return SIP_OK;
+    }
+
+    /*
+     * Parse the Info-Package header
+     */
+    // leading white spaces are trimmed, but not trailing ones
+    info_package = sippmh_get_header_val(pSipMessage,
+                                         SIP_HEADER_INFO_PACKAGE,
+                                         NULL);
+
+    if (info_package == NULL) {
+        /* No Info-Package header */
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Missing Info-Package header\n",
+                            DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
+
+        if (pSipMessage->num_body_parts == 0) {
+            /* No Info-Package header, and no body poarts */
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Missing message body\n",
+                                DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
+            /* Send 200 OK for legacy UA support */
+            status_code = 200;
+            reason_phrase = SIP_SUCCESS_SETUP_PHRASE;
+            return_code = SIP_OK;
+        } else {
+            /* No Info-Package header, but with body part(s) */
+            if (pSipMessage->num_body_parts > 1) {
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Multipart Info Package"
+                                    DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
+            }
+
+            type_index = find_type_index(pSipMessage->mesg_body[0].msgContentType);
+            if (type_index == INDEX_NOT_FOUND) {
+                /* No Info-Package header, and Content-Type is not supported */
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unsupported Content Type\n",
+                                    DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
+                /* Send 415 Unsupported Media Type */
+                status_code = SIP_CLI_ERR_MEDIA;
+                reason_phrase = SIP_CLI_ERR_MEDIA_PHRASE;
+            } else {
+                /* No Info-Package header, but Conent-Type is supported */
+                /* Send 200 OK for legacy UA support */
+                status_code = 200;
+                reason_phrase = SIP_SUCCESS_SETUP_PHRASE;
+                return_code = SIP_OK;
+            }
+        }
+    } else {
+        /* With Info-Package header */
+        if (pSipMessage->num_body_parts == 0) {
+            /* With Info-Package header, but no body parts */
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Missing message body\n",
+                                DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
+
+            /* ? */
+            /* Send 489 Bad Event */
+            status_code = SIP_CLI_ERR_BAD_EVENT;
+            reason_phrase = SIP_CLI_ERR_BAD_EVENT_PHRASE;
+
+        } else {
+            /* With Info-Package header and body part(s) */
+            if (pSipMessage->num_body_parts > 1) {
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Multipart Info Package "
+                                    "(only the first part is processed)\n",
+                                    DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
+            }
+
+            info_index = find_info_index(info_package);
+            if (info_index == INDEX_NOT_FOUND) {
+                /* Info-Package is not supported */
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unsupported Info Package\n",
+                                    DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
+
+                /* Send 489 Bad Event */
+                status_code = SIP_CLI_ERR_BAD_EVENT;
+                reason_phrase = SIP_CLI_ERR_BAD_EVENT_PHRASE;
+
+            } else {
+                /* Info-Package is supported */
+                type_index = find_type_index(pSipMessage->mesg_body[0].msgContentType);
+                record = find_handler_record(info_index, type_index);
+                if (record == NULL) {
+                    /* Info-Package header is supported, but Content-Type is not */
+                    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unsupported Content Type\n",
+                                        DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
+                    /* Send 415 Unsupported Media Type */
+                    status_code = SIP_CLI_ERR_MEDIA;
+                    reason_phrase = SIP_CLI_ERR_MEDIA_PHRASE;
+                } else {
+                    /* a handler is registered */
+                    (*record->handler)(ccb->dn_line, ccb->gsm_id,
+                                       g_registered_info[record->info_index],
+                                       s_registered_type[record->type_index],
+                                       pSipMessage->mesg_body[0].msgBody);
+                    /* Send 200 OK */
+                    status_code = 200;
+                    reason_phrase = SIP_SUCCESS_SETUP_PHRASE;
+                    return_code = SIP_OK;
+                }
+            }
+        }
+    }
+
+    if (sipSPISendErrorResponse(pSipMessage, status_code, reason_phrase,
+                                0, NULL, NULL) != TRUE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                          fname, reason_phrase);
+        return SIP_ERROR;
+    }
+
+    return return_code;
+}
+
+static void
+media_control_info_package_handler(line_t line, callid_t call_id,
+                                   const char *info_package,
+                                   const char *content_type,
+                                   const char *message_body)
+{
+}
+
+#ifdef _CONF_ROSTER_
+static void
+conf_info_package_handler(line_t line, callid_t call_id,
+                               const char *info_package,
+                               const char *content_type,
+                               const char *message_body)
+{
+    static const char *fname = "conf_info_package_handler";
+
+    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"info_package: %s content_type: %s\n",
+                        DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname),
+                        info_package, content_type);
+
+    ui_info_received(line, lsm_get_ui_id(call_id), info_package, content_type,
+                     message_body);
+}
+#endif
+
+/*
+ *  Function: ccsip_info_package_handler_init
+ *
+ *  Parameters:
+ *      None
+ *
+ *  Description:
+ *      Initializes the Info Package handler framework.
+ *
+ *  Return:
+ *      SIP_OK - Info Package handler framework initialized successfully
+ *      SIP_ERROR - otherwise
+ */
+int
+ccsip_info_package_handler_init(void)
+{
+    static const char *fname = "ccsip_info_package_handler_init";
+    info_index_t info_index;
+    type_index_t type_index;
+
+    if (s_handler_registry != NULL) {
+        // Is this considered an error?
+        CCSIP_DEBUG_TASK("%s: Info Package handler already initialized\n", fname);
+        return SIP_OK;
+    }
+
+    /* Create the SLL */
+    s_handler_registry = sll_create(is_matching_type);
+    if (s_handler_registry == NULL) {
+        CCSIP_DEBUG_ERROR("%s: failed to create the registry\n", fname);
+        return SIP_ERROR;
+    }
+
+    for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) {
+        g_registered_info[info_index] = NULL;
+    }
+
+    for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) {
+        s_registered_type[type_index] = NULL;
+    }
+
+    // XXX Where is the best place to register the application-specific handler?
+#ifdef _CONF_ROSTER_
+    /* Register the handler for conference & x-cisco-conference Info Packages */
+    ccsip_register_info_package_handler(INFO_PACKAGE_CONFERENCE,
+                                        CONTENT_TYPE_CONFERENCE_INFO,
+                                        conf_info_package_handler);
+    ccsip_register_info_package_handler(INFO_PACKAGE_CISCO_CONFERENCE,
+                                        CONTENT_TYPE_CONFERENCE_INFO,
+                                        conf_info_package_handler);
+#endif
+    return SIP_OK;
+}
+
+/*
+ *  Function: ccsip_info_package_handler_shutdown
+ *
+ *  Parameters:
+ *      None
+ *
+ *  Description:
+ *      Shuts down the Info Package handler framework.
+ *
+ *  Return:
+ *      None
+ */
+void
+ccsip_info_package_handler_shutdown(void)
+{
+    static const char *fname = "ccsip_info_package_handler_shutdown";
+    info_index_t info_index;
+    type_index_t type_index;
+    handler_record_t *record;
+
+    if (s_handler_registry == NULL) {
+        // Is this considered an error?
+        CCSIP_DEBUG_TASK("%s: Info Package handler was not initialized\n", fname);
+        return;
+    }
+
+    for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) {
+        if (s_registered_type[type_index] != NULL) {
+            cpr_free(s_registered_type[type_index]);
+            s_registered_type[type_index] = NULL;
+        }
+    }
+
+    for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) {
+        if (g_registered_info[info_index] != NULL) {
+            cpr_free(g_registered_info[info_index]);
+            g_registered_info[info_index] = NULL;
+        }
+    }
+
+    /* Deregister each Info Package handler */
+    for (record = (handler_record_t *)sll_next(s_handler_registry, NULL);
+         record != NULL;
+         record = (handler_record_t *)sll_next(s_handler_registry, record)) {
+        cpr_free(record);
+    }
+
+    /* Destroy the SLL */
+    sll_destroy(s_handler_registry);
+    s_handler_registry = NULL;
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_messaging.c b/libs/sipcc/core/sipstack/ccsip_messaging.c
new file mode 100644 (file)
index 0000000..d9a7965
--- /dev/null
@@ -0,0 +1,7639 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "plstr.h"
+#include "cpr_types.h"
+#include "cpr_time.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_in.h"
+#include "cpr_rand.h"
+#include "phntask.h"
+#include "text_strings.h"
+#include "util_string.h"
+#include "ccsip_core.h"
+#include "ccsip_macros.h"
+#include "ccsip_messaging.h"
+#include "ccsip_platform.h"
+#include "ccsip_task.h"
+#include "prot_configmgr.h"
+#include "phone_debug.h"
+#include "ccsip_reldev.h"
+#include "digcalc.h"
+#include "ccsip_register.h"
+#include "ccsip_credentials.h"
+#include "dns_utils.h"
+#include "config.h"
+#include "string_lib.h"
+#include "dialplan.h"
+#include "rtp_defs.h"
+#include "ccapi.h"
+#include "ccsip_platform_udp.h"
+#include "ccsip_task.h"
+#include "sdp.h"
+#include "sip_common_transport.h"
+#include "sip_common_regmgr.h"
+#include "uiapi.h"
+#include "ccsip_callinfo.h"
+#include "sip_interface_regmgr.h"
+#include "ccsip_spi_utils.h"
+#include "ccsip_subsmanager.h"
+#include "subapi.h"
+#include "platform_api.h"
+
+#define SIPS_URL_LEN 8
+#define NONCE_LEN    9
+#define SUBS_STATE_HDR_LEN 80
+#define MAX_EXPIRES_LEN      12
+#define MAX_ESCAPED_USER_LEN 94 // Worst case all 31 chars require escaping (3 chars) + a NULL
+#define MAX_PHONE_NAME_LEN 20
+#define MAX_UNREG_REASON_STR_LEN    256
+
+#define MAX_ESCAPED_USER_LEN 94 // Worst case all 31 chars require escaping (3 chars) + a NULL
+#define INITIAL_BUFFER_SIZE 2048
+
+
+/* External declarations */
+extern int dns_error_code; // DNS error code global
+extern sipPlatformUITimer_t sipPlatformUISMTimers[];
+extern sipCallHistory_t gCallHistory[];
+extern ccsipGlobInfo_t gGlobInfo;
+extern int16_t clockIsSetup;
+extern struct tm *gmtime_r(const time_t *, struct tm *);
+extern char *Basic_is_phone_forwarded(line_t line);
+extern uint16_t server_caps;
+extern char sipPhoneModelNumber[];
+extern char phone_load_name[];
+extern sipGlobal_t sip;
+extern ccm_act_stdby_table_t CCM_Active_Standby_Table;
+
+/* Forward declarations */
+boolean sipSPIAddRequestRecordRoute(sipMessage_t *, sipMessage_t *);
+static boolean sendResponse(ccsipCCB_t *ccb, sipMessage_t *response,
+                            sipMessage_t *refrequest, boolean retx,
+                            sipMethod_t method);
+static sipRet_t CopyLocalSDPintoResponse(sipMessage_t *request,
+                                         cc_msgbody_info_t *local_msg_body);
+
+#if defined SIP_OS_WINDOWS
+#define debugif_printf printf
+#endif
+/*
+ * Functions to manipulate transaction blocks
+ */
+
+/*
+ * This function gets the index of the last request sent or received
+ * It determines this by choosing the last one that has a
+ * non CCSIP_START_CSEQ cseq value
+ */
+int16_t
+get_last_request_trx_index (ccsipCCB_t *ccb, boolean sent)
+{
+    const char *fname = "get_last_request_trx_index";
+    int16_t i;
+
+    if (ccb == NULL) {
+        return -1;
+    }
+
+    CCSIP_DEBUG_TRX(DEB_F_PREFIX"Getting last TRX index, sent = %d\n", DEB_F_PREFIX_ARGS(SIP_TRX, fname), sent);
+
+    if (sent) {
+        for (i = MAX_REQ_OUTSTANDING - 1; i >= 0; i--) {
+            if (ccb->sent_request[i].cseq_number != CCSIP_START_CSEQ) {
+                CCSIP_DEBUG_TRX(DEB_F_PREFIX"Got TRX(%d) for sent req\n", DEB_F_PREFIX_ARGS(SIP_TRX, fname), i);
+                return i;
+            }
+        }
+    } else {
+        for (i = MAX_REQ_OUTSTANDING - 1; i >= 0; i--) {
+            if (ccb->recv_request[i].cseq_number != CCSIP_START_CSEQ) {
+                CCSIP_DEBUG_TRX(DEB_F_PREFIX"Got TRX(%d) for recv req\n", DEB_F_PREFIX_ARGS(SIP_TRX, fname), i);
+                return i;
+            }
+        }
+    }
+    return -1;
+}
+
+/*
+ * This function gets the next cseq index that can be used to send
+ * a request. It determines this by choosing the next available
+ * cseq index
+ */
+int16_t
+get_next_request_trx_index (ccsipCCB_t *ccb, boolean sent)
+{
+    const char *fname = "get_next_request_trx_index";
+    int16_t i;
+
+    if (ccb == NULL) {
+        return -1;
+    }
+
+    CCSIP_DEBUG_TRX(DEB_F_PREFIX"Getting next TRX index, sent = %d\n", DEB_F_PREFIX_ARGS(SIP_TRX, fname), sent);
+    if (sent) {
+        for (i = 0; i < MAX_REQ_OUTSTANDING; i++) {
+            if (ccb->sent_request[i].cseq_number == CCSIP_START_CSEQ) {
+                CCSIP_DEBUG_TRX(DEB_F_PREFIX"Got TRX(%d) for sent req\n", DEB_F_PREFIX_ARGS(SIP_TRX, fname), i);
+                return i;
+            }
+        }
+    } else {
+        for (i = 0; i < MAX_REQ_OUTSTANDING; i++) {
+            if (ccb->recv_request[i].cseq_number == CCSIP_START_CSEQ) {
+                CCSIP_DEBUG_TRX(DEB_F_PREFIX"Got TRX(%d) for recv req\n", DEB_F_PREFIX_ARGS(SIP_TRX, fname), i);
+                return i;
+            }
+        }
+    }
+    CCSIP_DEBUG_TRX(DEB_F_PREFIX"Unable to get any open TRX!!\n", DEB_F_PREFIX_ARGS(SIP_TRX, fname));
+    return -1;
+}
+
+/*
+ * This function gets the index corresponding to a specific method
+ */
+int16_t
+get_method_request_trx_index (ccsipCCB_t *ccb, sipMethod_t method, boolean sent)
+{
+    const char *fname = "get_method_request_trx_index";
+    int16_t i;
+
+    if (ccb == NULL) {
+        return -1;
+    }
+
+    CCSIP_DEBUG_TRX(DEB_F_PREFIX"Getting TRX for method(%s), sent = %d\n",
+                    DEB_F_PREFIX_ARGS(SIP_TRX, fname), sipGetMethodString(method), sent);
+
+    if (sent) {
+        for (i = 0; i < MAX_REQ_OUTSTANDING; i++) {
+            if (ccb->sent_request[i].cseq_method == method) {
+                CCSIP_DEBUG_TRX(DEB_F_PREFIX"Got TRX(%d) for sent method(%s)\n",
+                                DEB_F_PREFIX_ARGS(SIP_TRX, fname), i, sipGetMethodString(method));
+                return i;
+            }
+        }
+    } else {
+        for (i = 0; i < MAX_REQ_OUTSTANDING; i++) {
+            if (ccb->recv_request[i].cseq_method == method) {
+                CCSIP_DEBUG_TRX(DEB_F_PREFIX"Got TRX(%d) for recv method(%s)\n",
+                                DEB_F_PREFIX_ARGS(SIP_TRX, fname), i, sipGetMethodString(method));
+                return i;
+            }
+        }
+    }
+    CCSIP_DEBUG_TRX(DEB_F_PREFIX"Unable to find any TRX for method!!\n", DEB_F_PREFIX_ARGS(SIP_TRX, fname));
+    return -1;
+}
+
+/*
+ * This function cleans the CSeq array and removes the entry
+ * corresponding to the method and compacts the array
+ */
+void
+clean_method_request_trx (ccsipCCB_t *ccb, sipMethod_t method, boolean sent)
+{
+    const char *fname = "clean_method_request_trx";
+    uint8_t     i, j, k;
+    boolean     found = FALSE;
+    sipTransaction_t *transactionp = NULL;
+
+    if (ccb == NULL) {
+        return;
+    }
+
+    CCSIP_DEBUG_TRX(DEB_F_PREFIX"Removing TRX for method(%s), sent = %d\n",
+                    DEB_F_PREFIX_ARGS(SIP_TRX, fname), sipGetMethodString(method), sent);
+
+    if (sent) {
+        transactionp = &(ccb->sent_request[0]);
+    } else {
+        transactionp = &(ccb->recv_request[0]);
+    }
+
+    for (i = 0; i < MAX_REQ_OUTSTANDING && !found; i++) {
+        if (transactionp[i].cseq_method == method) {
+            transactionp[i].cseq_method = sipMethodInvalid;
+            transactionp[i].cseq_number = CCSIP_START_CSEQ;
+            strlib_free(transactionp[i].u.sip_via_header);
+            strlib_free(transactionp[i].sip_via_sentby);
+            CCSIP_DEBUG_TRX(DEB_F_PREFIX"Removed TRX(%d) for method(%s)\n",
+                            DEB_F_PREFIX_ARGS(SIP_TRX, fname), i, sipGetMethodString(method));
+            found = TRUE;
+        }
+        if (found) {
+            k = i;
+            for (j = k + 1; j < MAX_REQ_OUTSTANDING; j++, k++) {
+                memcpy(&(transactionp[k]), &(transactionp[j]),
+                       sizeof(sipTransaction_t));
+            }
+            // re-init the last transaction
+            transactionp[MAX_REQ_OUTSTANDING - 1].cseq_method =
+                sipMethodInvalid;
+            transactionp[MAX_REQ_OUTSTANDING - 1].cseq_number =
+                CCSIP_START_CSEQ;
+            transactionp[MAX_REQ_OUTSTANDING - 1].u.sip_via_header =
+                strlib_empty();
+            transactionp[MAX_REQ_OUTSTANDING - 1].sip_via_sentby =
+                strlib_empty();
+        }
+    }
+}
+
+line_t
+get_dn_line_from_dn (const char *watcher)
+{
+    line_t dn_line;
+    char   line_name[CC_MAX_DIALSTRING_LEN];
+
+    for (dn_line = 1; dn_line <= MAX_REG_LINES; dn_line++) {
+        config_get_line_string(CFGID_LINE_NAME, line_name, (int) dn_line,
+                               sizeof(line_name));
+        if (!cpr_strcasecmp(watcher, line_name)) {
+            break;
+        }
+    }
+    return dn_line;
+}
+
+boolean
+validateHostName (char *str, char *dn)
+{
+    line_t dn_line = (line_t) -1;
+    char   buffer[MAX_SIP_URL_LENGTH];
+    char   ccm1_addr[MAX_SIP_URL_LENGTH];
+    char   ccm2_addr[MAX_SIP_URL_LENGTH];
+    char   ccm3_addr[MAX_SIP_URL_LENGTH];
+
+    dn_line = get_dn_line_from_dn(dn);
+    if (dn_line >= 1 && dn_line <= MAX_REG_LINES) {
+        if (sip_regmgr_get_cc_mode(dn_line) == REG_MODE_NON_CCM) {
+            config_get_line_string(CFGID_PROXY_ADDRESS, buffer, dn_line,
+                                   MAX_SIP_URL_LENGTH);
+            if (!strncmp(buffer, str, MAX_SIP_URL_LENGTH)) {
+                return (TRUE);
+            } else {
+                return (FALSE);
+            }
+        } else {
+            config_get_string(CFGID_CCM1_ADDRESS, ccm1_addr,
+                              MAX_SIP_URL_LENGTH);
+            config_get_string(CFGID_CCM2_ADDRESS, ccm2_addr,
+                              MAX_SIP_URL_LENGTH);
+            config_get_string(CFGID_CCM3_ADDRESS, ccm3_addr,
+                              MAX_SIP_URL_LENGTH);
+
+            if (!strncmp(ccm1_addr, str, MAX_SIP_URL_LENGTH) ||
+                !strncmp(ccm2_addr, str, MAX_SIP_URL_LENGTH) ||
+                !strncmp(ccm3_addr, str, MAX_SIP_URL_LENGTH)) {
+                return (TRUE);
+            } else {
+                return (FALSE);
+            }
+        }
+    }
+
+    return (FALSE);
+}
+
+/*************************************************************
+ * Function: sipGetSupportedOptionList
+ * This function returns the list of the supported option tags.
+ * The function may returns NULL pointer for the case that
+ * there is no tag to add.
+ **************************************************************/
+static const char *
+sipGetSupportedOptionList (ccsipCCB_t *ccb, sipMethod_t sipmethod)
+{
+       return (SIP_CISCO_SUPPORTED_REG_TAGS);
+}
+
+/*
+ * Send REGISTER
+ *
+ * Send a SIP REGISTER request.
+ * Assumes that
+ * - connection has been setup beforehand.
+ */
+boolean
+sipSPISendRegister (ccsipCCB_t *ccb,
+                    boolean no_dns_lookup,
+                    const char *user,
+                    int expires_int)
+{
+    const char       fname[] = "SIPSPISendRegister";
+    sipMessage_t    *request = NULL;
+    char             obp_address[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t    ipaddr;
+    boolean          obp_present = FALSE;
+    boolean          send_result = FALSE;
+
+    CPR_IP_ADDR_INIT(ipaddr);
+
+    if (!(request = sipSPIBuildRegisterHeaders(ccb, user, expires_int))) {
+        CCSIP_DEBUG_ERROR("%s: Error: Building Register Headers.\n",
+                          fname);
+        return (send_result);
+    }
+
+    /*
+     * If we are being called as a result of a 4xx message we need to
+     * respond to the same proxy which sent us the 4xx message so use
+     * the previous ccb->reg.addr to send to.
+     */
+    config_get_string(CFGID_OUTBOUND_PROXY, obp_address, sizeof(obp_address));
+    if ((cpr_strcasecmp(obp_address, UNPROVISIONED) != 0) &&
+        (obp_address[0] != 0) &&
+        (obp_address[0] != '0')) {
+        obp_present = TRUE;
+    }
+    if ((!no_dns_lookup) &&
+        ((obp_present == FALSE) || ((ccb->index == REG_BACKUP_CCB)))) {
+        /* See if DNS SRV record is available     */
+        dns_error_code = sipTransportGetServerAddrPort(ccb->reg.proxy, &ipaddr,
+                                                       (uint16_t *)&ccb->reg.port,
+                                                       &ccb->SRVhandle, FALSE);
+        if (dns_error_code == 0) {
+            /*
+             * Found an SRV record. Use that address. If there is more
+             * than one record in SRV response, setup to try all the
+             * servers in the list
+             */
+            util_ntohl(&(ccb->reg.addr), &ipaddr);
+        } else {
+            /* Do a DNS A  record lookup on the proxy */
+            dns_error_code = dnsGetHostByName(ccb->reg.proxy, &ipaddr, 100, 1);
+            if (dns_error_code == 0) {
+                util_ntohl(&ipaddr, &ipaddr);
+                ccb->reg.addr = ipaddr;
+            } else {
+                ccb->reg.addr = ip_addr_invalid;
+            }
+
+        }
+    }
+
+    /* If we failed to get a valid IP address for the proxy
+     * do not broadcast the REGISTER message, but bail instead.
+     */
+    if ((util_check_if_ip_valid(&(ccb->reg.addr))) || obp_present) {
+        send_result = SendRequest(ccb, request, sipMethodRegister,
+                                  FALSE, TRUE, FALSE);
+    } else {
+        err_msg("%s: Unable to retrieve address of proxy.\n", fname);
+        free_sip_message(request);
+    }
+
+    if (!send_result) {
+        clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+    }
+    return (send_result);
+}
+
+char *
+cc2siptype (cc_content_type_t type)
+{
+    switch (type) {
+    default:
+    case cc_content_type_unknown:
+        return SIP_CONTENT_TYPE_UNKNOWN;
+    case cc_content_type_SDP:
+        return SIP_CONTENT_TYPE_SDP;
+    case cc_content_type_CMXML:
+        return SIP_CONTENT_TYPE_CMXML;
+    case cc_content_type_sipfrag:
+        return SIP_CONTENT_TYPE_SIPFRAG;
+    }
+}
+
+cc_content_type_t
+sip2cctype (uint8_t type)
+{
+    switch (type) {
+    default:
+    case SIP_CONTENT_TYPE_UNKNOWN_VALUE:
+        return cc_content_type_unknown;
+    case SIP_CONTENT_TYPE_SDP_VALUE:
+        return cc_content_type_SDP;
+    case SIP_CONTENT_TYPE_CMXML_VALUE:
+        return cc_content_type_CMXML;
+    case SIP_CONTENT_TYPE_SIPFRAG_VALUE:
+        return cc_content_type_sipfrag;
+    }
+}
+
+uint8_t
+cc2sipdisp (cc_disposition_type_t type)
+{
+    switch (type) {
+    default:
+    case cc_disposition_unknown:
+        return SIP_CONTENT_DISPOSITION_UNKNOWN_VALUE;
+    case cc_disposition_render:
+        return SIP_CONTENT_DISPOSITION_RENDER_VALUE;
+    case cc_disposition_session:
+        return SIP_CONTENT_DISPOSITION_SESSION_VALUE;
+    case cc_dispostion_icon:
+        return SIP_CONTENT_DISPOSITION_ICON_VALUE;
+    case cc_disposition_alert:
+        return SIP_CONTENT_DISPOSITION_ALERT_VALUE;
+    case cc_disposition_precondition:
+        return SIP_CONTENT_DISPOSITION_PRECONDITION_VALUE;
+    }
+}
+
+cc_disposition_type_t
+sip2ccdisp (uint8_t type)
+{
+    switch (type) {
+    default:
+    case SIP_CONTENT_DISPOSITION_UNKNOWN_VALUE:
+        return cc_disposition_unknown;
+    case SIP_CONTENT_DISPOSITION_RENDER_VALUE:
+        return cc_disposition_render;
+    case SIP_CONTENT_DISPOSITION_SESSION_VALUE:
+        return cc_disposition_session;
+    case SIP_CONTENT_DISPOSITION_ICON_VALUE:
+        return cc_dispostion_icon;
+    case SIP_CONTENT_DISPOSITION_ALERT_VALUE:
+        return cc_disposition_alert;
+    case SIP_CONTENT_DISPOSITION_PRECONDITION_VALUE:
+        return cc_disposition_precondition;
+    }
+}
+
+
+static boolean
+sipSPIIsPrivate (ccsipCCB_t *ccb)
+{
+    int     blocking;
+    boolean private_flag = FALSE;
+
+    /*
+     * If Caller ID Blocking is OFF or emergency route is ON,
+     * display the actual name.  Otherwise, display "Anonymous".
+     */
+    config_get_value(CFGID_CALLERID_BLOCKING, &blocking, sizeof(blocking));
+    if ((blocking & 1) && (ccb->routeMode != RouteEmergency)) {
+        private_flag = TRUE;
+    }
+    return private_flag;
+}
+
+/*
+ * sipSPISetRPID
+ *
+ * Set the RPID header string sent in either a request or response.
+ */
+
+static int
+sipSPISetRPID (ccsipCCB_t *ccb, boolean request)
+{
+    const char *fname = "sipSPISetRPID";
+    int         rpid_flag = RPID_DISABLED;
+    boolean     private_flag;
+    size_t      escaped_url_len;
+    char        remote_party_id_buf[MAX_SIP_URL_LENGTH];
+    char        line_name[MAX_LINE_NAME_SIZE];
+    char        display_name[MAX_LINE_NAME_SIZE];
+    char        src_addr_str[MAX_IPADDR_STR_LEN];
+    cpr_ip_type ip_type;
+
+    src_addr_str[0] = '\0';
+    config_get_value(CFGID_REMOTE_PARTY_ID, &rpid_flag, sizeof(rpid_flag));
+
+    if (rpid_flag != RPID_ENABLED) {
+        return RPID_DISABLED;
+    }
+
+    if (!ccb) {
+        CCSIP_DEBUG_ERROR("%s: Error: NULL ccb.\n", fname);
+        return rpid_flag;
+    }
+
+    /* If RPID string is already set, just return */
+    if (ccb->sip_remote_party_id[0]) {
+        return RPID_ENABLED;
+    }
+
+    private_flag = sipSPIIsPrivate(ccb);
+
+    config_get_string((CFGID_LINE_NAME + ccb->dn_line - 1), line_name,
+                      sizeof(line_name));
+    sip_config_get_display_name(ccb->dn_line, display_name,
+                                sizeof(display_name));
+
+    ip_type = sipTransportGetPrimServerAddress(ccb->dn_line, src_addr_str);
+
+    sstrncpy(remote_party_id_buf, "\"", MAX_SIP_URL_LENGTH);
+    escaped_url_len = 1;
+    escaped_url_len +=
+        sippmh_converQuotedStrToEscStr(display_name, strlen(display_name),
+                                          remote_party_id_buf + escaped_url_len,
+                                           MAX_SIP_URL_LENGTH - escaped_url_len,
+                                           TRUE) - 1;
+    sstrncat(remote_party_id_buf,"\" <sip:",MAX_SIP_URL_LENGTH - escaped_url_len );
+    escaped_url_len = strlen(remote_party_id_buf);
+
+    escaped_url_len +=
+        sippmh_convertURLCharToEscChar(line_name, strlen(line_name),
+                                       remote_party_id_buf + escaped_url_len,
+                                       MAX_SIP_URL_LENGTH - escaped_url_len,
+                                       FALSE);
+    if (ip_type == CPR_IP_ADDR_IPV6) {
+        snprintf(remote_party_id_buf + escaped_url_len,
+                 MAX_SIP_URL_LENGTH - escaped_url_len,
+                 "@[%s]>;party=%s;id-type=subscriber;privacy=%s;screen=yes",
+                src_addr_str, (request ? "calling" : "called"),
+                (private_flag ? "full" : "off"));
+    } else {
+        snprintf(remote_party_id_buf + escaped_url_len,
+                 MAX_SIP_URL_LENGTH - escaped_url_len,
+                 "@%s>;party=%s;id-type=subscriber;privacy=%s;screen=yes",
+                src_addr_str, (request ? "calling" : "called"),
+                (private_flag ? "full" : "off"));
+    }
+
+    ccb->sip_remote_party_id = strlib_update(ccb->sip_remote_party_id,
+                                             remote_party_id_buf);
+
+    return RPID_ENABLED;
+}
+
+static void
+sipSPISetFrom (ccsipCCB_t *ccb)
+{
+    const char *fname = "sipSPISetFrom";
+    boolean     private_flag;
+    size_t      escaped_url_len;
+    char       *sip_from_tag;
+    char       *temp_from_tag;
+    char       *sip_from_temp;
+    char        line_name[MAX_LINE_NAME_SIZE];
+    char        display_name[MAX_LINE_NAME_SIZE];
+    char        dest_sip_addr_str[MAX_IPADDR_STR_LEN];
+    char       *addr_str = 0;
+    char        addr[MAX_IPADDR_STR_LEN];
+    cpr_ip_type ip_type = CPR_IP_ADDR_INVALID;
+
+    if (!ccb) {
+        CCSIP_DEBUG_ERROR("%s: Error: NULL ccb.\n", fname);
+        return;
+    }
+
+    ipaddr2dotted(dest_sip_addr_str, &ccb->dest_sip_addr);
+
+    if ((ccb->routeMode == RouteEmergency) ||
+        (ccb->proxySelection == SIP_PROXY_BACKUP)) {
+        addr_str = dest_sip_addr_str;
+    } else {
+        ip_type = sipTransportGetPrimServerAddress(ccb->dn_line, addr);
+        addr_str = addr;
+    }
+
+    sip_from_temp = strlib_open(ccb->sip_from, MAX_SIP_URL_LENGTH);
+
+    if (sip_from_temp == NULL) {
+        CCSIP_DEBUG_ERROR("%s: Error: sip_from_temp is NULL.\n", fname);
+        return;
+    }
+
+    private_flag = sipSPIIsPrivate(ccb);
+
+    if (private_flag == TRUE) {
+        if (ip_type == CPR_IP_ADDR_IPV6) {
+            snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, "\"%s\" <sip:%s@[%s]>",
+                 SIP_HEADER_ANONYMOUS_STR, SIP_HEADER_ANONYMOUS_STR,
+                 addr_str);
+        } else {
+            snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, "\"%s\" <sip:%s@%s>",
+                 SIP_HEADER_ANONYMOUS_STR, SIP_HEADER_ANONYMOUS_STR,
+                 addr_str);
+        }
+    } else {
+        /* From header needs to have global address in it */
+        config_get_string((CFGID_LINE_NAME + ccb->dn_line - 1), line_name,
+                          sizeof(line_name));
+        sip_config_get_display_name(ccb->dn_line, display_name,
+                                    sizeof(display_name));
+        sstrncpy(sip_from_temp, "\"", MAX_SIP_URL_LENGTH);
+        escaped_url_len = 1;
+        escaped_url_len +=
+               sippmh_converQuotedStrToEscStr(display_name, strlen(display_name),
+                                           sip_from_temp + escaped_url_len,
+                                           MAX_SIP_URL_LENGTH - escaped_url_len,
+                                           TRUE) - 1;
+        sstrncat(sip_from_temp,"\" <sip:",MAX_SIP_URL_LENGTH - escaped_url_len );
+        escaped_url_len = strlen(sip_from_temp);
+        escaped_url_len +=
+            sippmh_convertURLCharToEscChar(line_name, strlen(line_name),
+                                           sip_from_temp + escaped_url_len,
+                                           MAX_SIP_URL_LENGTH - escaped_url_len,
+                                           TRUE) - 1;
+
+        /* construct From header's URI */
+        if (ip_type == CPR_IP_ADDR_IPV6) {
+
+            snprintf(sip_from_temp + escaped_url_len,
+                     MAX_SIP_URL_LENGTH - escaped_url_len, "@[%s]>",
+                     addr_str);
+        } else {
+            snprintf(sip_from_temp + escaped_url_len,
+                     MAX_SIP_URL_LENGTH - escaped_url_len, "@%s>",
+                     addr_str);
+        }
+    }
+
+    /* Now add tag to the From header */
+    sstrncat(sip_from_temp, ";tag=",
+            MAX_SIP_URL_LENGTH - strlen(sip_from_temp));
+    temp_from_tag = ccsip_find_preallocated_sip_local_tag(ccb->dn_line);
+    sip_from_tag = strlib_open(ccb->sip_from_tag, MAX_SIP_URL_LENGTH);
+    if (temp_from_tag == NULL) {
+        if (sip_from_tag) {
+            sip_util_make_tag(sip_from_tag);
+            sstrncat(sip_from_temp, sip_from_tag,
+                    MAX_SIP_URL_LENGTH - strlen(sip_from_temp));
+        }
+    } else {
+        if (sip_from_tag) {
+            sstrncpy(sip_from_tag, temp_from_tag, MAX_SIP_URL_LENGTH);
+            sstrncat(sip_from_temp, temp_from_tag,
+                    MAX_SIP_URL_LENGTH - strlen(sip_from_temp));
+        }
+        ccsip_free_preallocated_sip_local_tag(ccb->dn_line);
+    }
+    ccb->sip_from_tag = strlib_close(sip_from_tag);
+    ccb->sip_from = strlib_close(sip_from_temp);
+}
+
+/*
+ * Send INVITE
+ *
+ * As the name suggests, called to send a SIP INVITE request.
+ * Assumes that
+ * - connection has been setup beforehand.
+ * - SDP description has been setup.
+ * Does not affect call state.
+ */
+boolean
+sipSPISendInvite (ccsipCCB_t *ccb, sipInviteType_t inviteType,
+                  boolean initInvite)
+{
+    const char      *fname = "SIPSPISendInvite";
+    sipMessage_t    *request = NULL;
+    sipRet_t         flag = STATUS_SUCCESS;
+    sipRet_t         tflag = STATUS_SUCCESS;
+    char             called_number[MAX_SIP_URL_LENGTH];
+    ccsipCCB_t      *referccb = NULL;
+    boolean          inviterefer = FALSE;
+    sipMessageFlag_t messageflag;
+    int              i;
+    int              rpid_flag;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "INVITE");
+
+    if (ccb->wastransferred) {
+        inviterefer = TRUE;
+    }
+    // This routine can be called in three different contexts. These are the
+    // important differences between them:
+    // CONTEXT         inviteType                       referccb
+    // -----------------------------------------------------------
+    // Normal          SIP_INVITE_TYPE_NORMAL           NULL
+    // Blind Xfer      SIP_INVITE_TYPE_NORMAL           valid
+    // Att Xfer        SIP_INVITE_TYPE_TRANSFER         valid
+
+    referccb = sip_sm_get_target_call_by_gsm_id(ccb->gsm_id);
+
+    // If there is a referccb, try to get authentication stuff from it
+    if (referccb != NULL) {
+        if (referccb->refer_proxy_auth != NULL) {
+            ccb->refer_proxy_auth = cpr_strdup(referccb->refer_proxy_auth);
+        }
+    }
+
+    if (inviteType == SIP_INVITE_TYPE_TRANSFER) {
+        // For attended transfers we need to make a local copy of the
+        // referccb if it exists
+        if (NULL != referccb) {
+            ccb->sip_referredBy = strlib_update(ccb->sip_referredBy,
+                                                referccb->sip_referredBy);
+            if (referccb->featuretype == CC_FEATURE_XFER) {
+                ccb->sipxfercallid = strlib_update(ccb->sipxfercallid,
+                                                   referccb->sipxfercallid);
+                if (ccb->sipxfercallid) {
+                    if (ccb->sipxfercallid[0] != '\0') {
+                        ccb->wastransferred = TRUE;
+                        inviterefer = TRUE;
+                    }
+                }
+            }
+        }
+
+        if (inviterefer == FALSE) {
+            //Not enough information for starting attended transfer
+            CCSIP_DEBUG_ERROR("%s: Error:Replaces INVITE build unsuccessful.\n",
+                              fname);
+            return (FALSE);
+        }
+    }
+
+    if (inviteType != SIP_INVITE_TYPE_REDIRECTED) {
+        ccb->sip_to = strlib_update(ccb->sip_to, ccb->calledNumber);
+    }
+
+
+    /*
+     * Set from header
+     */
+    sipSPISetFrom(ccb);
+
+    /*
+     * Set RPID header.
+     */
+    rpid_flag = sipSPISetRPID(ccb, TRUE);
+
+    /*
+     * The calledNumber needs to be rewritten with the backup proxy
+     * ip address if the backup proxy is active
+     */
+    if (ccb->proxySelection == SIP_PROXY_BACKUP) {
+        sstrncpy(called_number, ccb->calledDisplayedName, MAX_SIP_URL_LENGTH);
+
+        /*
+         * NOTE: Need to replace with new routine ???
+         */
+        if (called_number[0] != '\0') {
+            sip_sm_util_normalize_name(ccb, called_number);
+        }
+    }
+
+    // Add Create Request Here
+    messageflag.flags = 0;
+    messageflag.flags |= SIP_HEADER_ACCEPT_BIT |
+                         SIP_HEADER_EXPIRES_BIT |
+                         SIP_HEADER_CONTACT_BIT |
+                         SIP_HEADER_DIVERSION_BIT |
+                         SIP_HEADER_SUPPORTED_BIT |
+                         SIP_HEADER_ALLOW_EVENTS_BIT |
+                         SIP_HEADER_ALLOW_BIT |
+                         SIP_HEADER_RECV_INFO_BIT |
+                         SIP_HEADER_REQUIRE_BIT;
+
+    if (ccb->authen.authorization != NULL) {
+        messageflag.flags |= SIP_HEADER_AUTHENTICATION_BIT;
+    }
+    if (ccb->refer_proxy_auth != NULL) {
+        messageflag.flags |= SIP_HEADER_PROXY_AUTH_BIT;
+    }
+    if (ccb->sip_referredBy[0] != '\0') {
+        messageflag.flags |= SIP_HEADER_REFERRED_BY_BIT;
+    }
+    if (((TRUE == inviterefer) || (ccb->flags & SENT_INVITE_REPLACE)) &&
+        ('\0' != ccb->sipxfercallid[0])) {
+        messageflag.flags |= SIP_HEADER_REPLACES_BIT;
+    }
+    if (ccb->sip_reqby[0]) {
+        messageflag.flags |= SIP_HEADER_REQUESTED_BY_BIT;
+    }
+    if (rpid_flag == RPID_ENABLED) {
+        messageflag.flags |= SIP_HEADER_REMOTE_PARTY_ID_BIT;
+    }
+    if (ccb->out_call_info != NULL) {
+        messageflag.flags |= SIP_HEADER_CALL_INFO_BIT;
+    }
+    if (ccb->join_info != NULL) {
+        messageflag.flags |= SIP_HEADER_JOIN_INFO_BIT;
+    }
+
+    /* Write SDP */
+    messageflag.flags |= SIP_HEADER_CONTENT_TYPE_BIT;
+    request = GET_SIP_MESSAGE();
+    if (request == NULL) {
+        CCSIP_DEBUG_ERROR("%s: Error: Unable to allocate INVITE request\n",
+                          fname);
+        return (FALSE);
+    }
+    messageflag.extflags = 0;
+    if (CreateRequest(ccb, messageflag, sipMethodInvite, request,
+                      initInvite, 0)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+    UPDATE_FLAGS(flag, tflag);
+
+    ccb->ReqURIOriginal = strlib_update(ccb->ReqURIOriginal, ccb->ReqURI);
+
+    /*
+     * If overloaded headers are present from the 302, add them to the
+     * Invite message
+     */
+    if ((ccb->redirect_info) &&
+        (ccb->redirect_info->sipContact->locations[0]->genUrl) &&
+        (ccb->redirect_info->sipContact->locations[0]->genUrl->u.sipUrl) &&
+        (ccb->redirect_info->sipContact->locations[0]->genUrl->u.sipUrl->headerp) &&
+        (inviteType == SIP_INVITE_TYPE_REDIRECTED)) {
+        for (i = 0;
+             i < ccb->redirect_info->sipContact->locations[0]->genUrl->u.sipUrl->num_headers;
+             i++) {
+            tflag = sippmh_add_text_header(request,
+                        ccb->redirect_info->sipContact->locations[0]->genUrl->u.
+                        sipUrl->headerp[i].attr,
+                        ccb->redirect_info->sipContact->locations[0]->genUrl->u.
+                        sipUrl->headerp[i].value);
+            UPDATE_FLAGS(flag, tflag);
+        }
+    }
+
+    if (flag != STATUS_SUCCESS) {
+        free_sip_message(request);
+        CCSIP_DEBUG_ERROR("%s: Error: INVITE message build unsuccessful.\n",
+                          fname);
+        clean_method_request_trx(ccb, sipMethodInvite, TRUE);
+        return (FALSE);
+    }
+
+    ccb->retx_counter = 0;
+
+    if (SendRequest(ccb, request, sipMethodInvite, FALSE, TRUE, TRUE)
+            == FALSE) {
+        clean_method_request_trx(ccb, sipMethodInvite, TRUE);
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+sipRet_t
+sipSPIAddCallStats (ccsipCCB_t *ccb, sipMessage_t *msg)
+{
+    int      call_stats_flag;
+    sipRet_t tflag = STATUS_SUCCESS;
+
+    config_get_value(CFGID_CALL_STATS, &call_stats_flag, sizeof(call_stats_flag));
+    if ((call_stats_flag) && (ccb->kfactor_ptr)) {
+        if (ccb->kfactor_ptr->rxstats[0] != NUL) {
+            tflag = sippmh_add_text_header(msg, SIP_RX_CALL_STATS, ccb->kfactor_ptr->rxstats);
+        }
+        if (ccb->kfactor_ptr->txstats[0] != NUL) {
+            tflag = sippmh_add_text_header(msg, SIP_TX_CALL_STATS, ccb->kfactor_ptr->txstats);
+        }
+    }
+    return tflag;
+}
+
+boolean
+sipSPISendInviteMidCall (ccsipCCB_t *ccb, boolean expires)
+{
+    const char      *fname = "sipSPISendInviteMidCall";
+    sipMessage_t    *request = NULL;
+    sipRet_t         flag = STATUS_SUCCESS;
+    sipRet_t         tflag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    int              rpid_flag;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "INVITE");
+
+    /*
+     * Build the request Here
+     */
+    messageflag.flags = 0;
+    messageflag.flags |= SIP_HEADER_CONTACT_BIT |
+                         SIP_HEADER_ROUTE_BIT |
+                         SIP_HEADER_CONTENT_TYPE_BIT |
+                         SIP_HEADER_ACCEPT_BIT |
+                         SIP_HEADER_SUPPORTED_BIT |
+                         SIP_HEADER_ALLOW_EVENTS_BIT |
+                         SIP_HEADER_ALLOW_BIT |
+                         SIP_HEADER_RECV_INFO_BIT |
+                         SIP_HEADER_REQUIRE_BIT;
+
+    if ((ccb->authen.authorization != NULL) &&
+        ((ccb->state == SIP_STATE_SENT_INVITE) ||
+         (ccb->state == SIP_STATE_SENT_MIDCALL_INVITE))) {
+        messageflag.flags |= SIP_HEADER_AUTHENTICATION_BIT;
+    }
+
+    if ((ccb->authen.authorization != NULL) &&
+        (ccb->state == SIP_STATE_SENT_INVITE)) {
+        if (ccb->join_info != NULL) {
+            messageflag.flags |= SIP_HEADER_JOIN_INFO_BIT;
+        }
+    }
+
+    if ('\0' != ccb->sipxfercallid[0]) {
+        messageflag.flags |= SIP_HEADER_REPLACES_BIT;
+    }
+
+    if (expires > 0) {
+        messageflag.flags |= SIP_HEADER_EXPIRES_BIT;
+    }
+
+    if (ccb->sip_referredBy[0]) {
+        messageflag.flags |= SIP_HEADER_REFERRED_BY_BIT;
+    }
+
+    /*
+     * Set RPID header.
+     */
+    rpid_flag = sipSPISetRPID(ccb, TRUE);
+    if (rpid_flag == RPID_ENABLED) {
+        messageflag.flags |= SIP_HEADER_REMOTE_PARTY_ID_BIT;
+    }
+
+    if (ccb->out_call_info) {
+        messageflag.flags |= SIP_HEADER_CALL_INFO_BIT;
+    }
+
+    request = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateRequest(ccb, messageflag, sipMethodInvite, request, FALSE, 0)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+
+    UPDATE_FLAGS(flag, tflag);
+
+
+    tflag = sipSPIAddCallStats(ccb, request);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Recalc and add Authorization header if needed */
+    if ((ccb->state != SIP_STATE_SENT_INVITE) &&
+        (ccb->state != SIP_STATE_SENT_MIDCALL_INVITE)) {
+        sipSPIGenerateGenAuthorizationResponse(ccb, request, &flag,
+                                               SIP_METHOD_INVITE);
+    }
+
+    /* Write SDP */
+    if (flag != STATUS_SUCCESS) {
+        free_sip_message(request);
+        CCSIP_DEBUG_ERROR("%s: Error: INVITE message build unsuccessful.\n",
+                          fname);
+        clean_method_request_trx(ccb, sipMethodInvite, TRUE);
+        return (FALSE);
+    }
+    /*
+     * Currently the following field is being set to zero when the
+     * midcall invite is not being done as a response to an authorization
+     * challenge. So utilize this fact to prevent proxy backup selection
+     * on a mid-call invite. The end result is to stick with the same
+     * proxy as the original invite.
+     */
+    if (ccb->authen.cred_type == 0) {
+        ccb->proxySelection = SIP_PROXY_DO_NOT_CHANGE_MIDCALL;
+    }
+
+    /* Successfully constructed msg with new URI for this INVITE to send out.
+     * Update URIOriginal before sending out.
+     */
+    ccb->ReqURIOriginal = strlib_update(ccb->ReqURIOriginal, ccb->ReqURI);
+
+    /* Enable reTx and send */
+    ccb->retx_counter = 0;
+    if (SendRequest(ccb, request, sipMethodInvite, TRUE, TRUE, TRUE) == FALSE) {
+        clean_method_request_trx(ccb, sipMethodInvite, TRUE);
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+
+/*
+ * Sends the ACK request.
+ * Assumes that
+ * - the connection is setup.
+ * Appends session description if sd = TRUE. (This would
+ * be the case, if we want to change the codec that
+ * was sent on the INVITE.
+ * Does not affect call state.
+ */
+boolean
+sipSPISendAck (ccsipCCB_t *ccb, sipMessage_t *response)
+{
+    const char      *fname = "sipSPISendAck";
+    sipMessage_t    *request = NULL;
+    sipRet_t        flag = STATUS_SUCCESS;
+    sipRet_t        tflag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    uint32_t         response_cseq_number = 0;
+    sipCseq_t       *response_cseq_structure;
+    const char      *response_cseq;
+    int16_t          trx_index = -1;
+    boolean          retval;
+    int              rpid_flag;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "ACK");
+
+    /*
+     * Build the request
+     */
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_ROUTE_BIT |
+                        SIP_HEADER_RECV_INFO_BIT;
+
+    /*
+     * Cseq number in the response could be different from that in ccb.
+     * If there was no response as in
+     * ccsip_handle_sentinviteconnected_ev_cc_connected_ack
+     * then use ccb for getting Cseq number
+     */
+    if (response) {
+        response_cseq = sippmh_get_cached_header_val(response, CSEQ);
+        if (!response_cseq) {
+            CCSIP_DEBUG_ERROR("%s: Error: Unable to obtain response CSeq "
+                              "header.\n", fname);
+            return (FALSE);
+        }
+        response_cseq_structure = sippmh_parse_cseq(response_cseq);
+        if (!response_cseq_structure) {
+            CCSIP_DEBUG_ERROR("%s: Error: Unable to parse response CSeq "
+                              "header.\n", fname);
+            return (FALSE);
+        }
+        response_cseq_number = response_cseq_structure->number;
+        cpr_free(response_cseq_structure);
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Cseq from response = %d \n",
+            DEB_F_PREFIX_ARGS(SIP_ACK, "sipSPISendAck"), response_cseq_number);
+    } else {
+        trx_index = get_method_request_trx_index(ccb, sipMethodInvite, TRUE);
+        if (trx_index < 0) {
+            return (FALSE);
+        }
+        response_cseq_number = ccb->sent_request[trx_index].cseq_number;
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Cseq from ccb = %d \n",
+            DEB_F_PREFIX_ARGS(SIP_ACK, "sipSPISendAck"), response_cseq_number);
+    }
+
+    messageflag.flags |= SIP_HEADER_CONTENT_LENGTH_BIT;
+    if (ccb->authen.authorization != NULL) {
+        messageflag.flags |= SIP_HEADER_AUTHENTICATION_BIT;
+    }
+
+    /*
+     * Set RPID header.
+     */
+    rpid_flag = sipSPISetRPID(ccb, TRUE);
+    if (rpid_flag == RPID_ENABLED) {
+        messageflag.flags |= SIP_HEADER_REMOTE_PARTY_ID_BIT;
+    }
+
+    request = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateRequest(ccb, messageflag, sipMethodAck, request, FALSE,
+                      response_cseq_number)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+
+    UPDATE_FLAGS(flag, tflag);
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request)
+            free_sip_message(request);
+
+        clean_method_request_trx(ccb, sipMethodInvite, TRUE);
+        return (FALSE);
+    }
+
+    /* Send message */
+    retval = SendRequest(ccb, request, sipMethodAck, FALSE, FALSE, FALSE);
+
+    // We are done with this INVITE request so lets clear and reorder our
+    // cseq list of outstanding requests. Assumes that ACK will only be
+    // sent for INVITE. Also note that no trx block is allocated for Ack
+    // and so there is no need to free it.
+    clean_method_request_trx(ccb, sipMethodInvite, TRUE);
+    return (retval);
+}
+
+
+/*
+ * Sends the BYE request for the call.
+ * Assumes that
+ * - the connection is setup.
+ * Does not change the state, but changes the disconnection flags.
+ */
+void
+sipSPISendBye (ccsipCCB_t *ccb, char *alsoString, sipMessage_t *pForked200)
+{
+    const char       *fname = "sipSPISendBye";
+    sipMessage_t     *request = NULL;
+    sipRet_t          flag = STATUS_SUCCESS;
+    sipRet_t          tflag = STATUS_SUCCESS;
+    sipContact_t     *stored_contact_info = NULL;
+    sipRecordRoute_t *stored_record_route_info = NULL;
+    static char       stored_sip_to[MAX_SIP_URL_LENGTH];
+    static char       stored_sip_from[MAX_SIP_URL_LENGTH];
+    static char       last_route[MAX_SIP_URL_LENGTH];
+    const char       *forked200_contact = NULL;
+    const char       *forked200_record_route = NULL;
+    const char       *forked200_to = NULL;
+    const char       *forked200_from = NULL;
+    sipMessageFlag_t  messageflag;
+
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "BYE");
+
+    /*
+     * If this BYE is in response to the secondary forked 200 OK message
+     * (from the callee being rejected), then we need to use the
+     * Contact and Record-Route of this 200 OK message, rather than the
+     * stored ccb->contact_info and ccb->record_route_info.
+     * So, if pForked200 exists, then we will:
+     * - save existing ccb->contact_info and ccb->record_route_info fields
+     * - parse Contact and Record-Route of the forked 200
+     * - use these values to form this BYE's Req-URI
+     * - restore the original ccb->contact_info and ccb->record_route_info
+     */
+    if (pForked200) {
+        stored_contact_info = ccb->contact_info;
+        stored_record_route_info = ccb->record_route_info;
+        sstrncpy(stored_sip_to, ccb->sip_to, MAX_SIP_URL_LENGTH);
+        sstrncpy(stored_sip_from, ccb->sip_from, MAX_SIP_URL_LENGTH);
+
+        forked200_contact = sippmh_get_cached_header_val(pForked200, CONTACT);
+        forked200_record_route =
+            sippmh_get_cached_header_val(pForked200, RECORD_ROUTE);
+        forked200_to = sippmh_get_cached_header_val(pForked200, TO);
+        forked200_from = sippmh_get_cached_header_val(pForked200, FROM);
+
+        if (forked200_contact) {
+            ccb->contact_info = sippmh_parse_contact(forked200_contact);
+        }
+        if (forked200_record_route) {
+            ccb->record_route_info =
+                sippmh_parse_record_route(forked200_record_route);
+        }
+        ccb->sip_to = strlib_update(ccb->sip_to, forked200_to);
+        ccb->sip_from = strlib_update(ccb->sip_from, forked200_from);
+    }
+
+    /*
+     * Build the request
+     */
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_CONTENT_LENGTH_BIT;
+
+    request = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateRequest(ccb, messageflag, sipMethodBye, request, FALSE, 0)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+
+    UPDATE_FLAGS(flag, tflag);
+
+
+    /* add in call stats header if needed */
+    tflag = sipSPIAddCallStats(ccb, request);
+    UPDATE_FLAGS(flag, tflag);
+
+    if (alsoString) {
+        tflag = sippmh_add_text_header(request, SIP_HEADER_ALSO, alsoString);
+        UPDATE_FLAGS(flag, tflag);
+    }
+
+    memset(last_route, 0, MAX_SIP_URL_LENGTH);
+    tflag = (sipSPIAddRouteHeaders(request, ccb, last_route, MAX_SIP_URL_LENGTH)) ?
+        STATUS_SUCCESS : STATUS_FAILURE;
+    UPDATE_FLAGS(flag, tflag);
+    sipSPIGenerateGenAuthorizationResponse(ccb, request, &flag, SIP_METHOD_BYE);
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request)
+            free_sip_message(request);
+        if (alsoString)
+            cpr_free(alsoString);
+        clean_method_request_trx(ccb, sipMethodBye, TRUE);
+        return;
+    }
+
+    ccb->retx_counter = 0;
+    /* Send message */
+    (void) SendRequest(ccb, request, sipMethodBye, FALSE, TRUE, FALSE);
+
+    /*
+     * Update history
+     */
+    /* Record Also header if any */
+    if (alsoString) {
+        if (alsoString[0]) {
+            sstrncpy(gCallHistory[ccb->index].last_bye_also_string, alsoString,
+                     MAX_SIP_URL_LENGTH);
+        }
+        cpr_free(alsoString);
+    } else {
+        memset(gCallHistory[ccb->index].last_bye_also_string, 0,
+               MAX_SIP_URL_LENGTH);
+    }
+
+    /* Record current Route */
+    if (last_route[0]) {
+        sstrncpy(gCallHistory[ccb->index].last_route, last_route,
+                 MAX_SIP_URL_LENGTH);
+    } else {
+        memset(gCallHistory[ccb->index].last_route, 0, MAX_SIP_URL_LENGTH);
+    }
+    /* Record current Request-URI */
+    if (ccb->ReqURI[0]) {
+        sstrncpy(gCallHistory[ccb->index].last_route_request_uri, ccb->ReqURI,
+                 MAX_SIP_URL_LENGTH);
+    } else {
+        memset(gCallHistory[ccb->index].last_route_request_uri, 0,
+               MAX_SIP_URL_LENGTH);
+    }
+
+// bugid: CSCsz34666
+//    /*
+//     * Store call history info
+//     */
+//    if ((int) (ccb->index) <= TEL_CCB_END) {
+//        memcpy(gCallHistory[ccb->index].last_call_id, ccb->sipCallID,
+//               MAX_SIP_CALL_ID);
+//    }
+
+    /*
+     * Restore the original ccb->contact and ccb->record_route fields
+     */
+    if (pForked200) {
+        if (ccb->contact_info) {
+            sippmh_free_contact(ccb->contact_info);
+        }
+        ccb->contact_info = stored_contact_info;
+        if (ccb->record_route_info) {
+            sippmh_free_record_route(ccb->record_route_info);
+        }
+        ccb->record_route_info = stored_record_route_info;
+
+        ccb->sip_to = strlib_update(ccb->sip_to, stored_sip_to);
+        ccb->sip_from = strlib_update(ccb->sip_from, stored_sip_from);
+    }
+
+    return;
+}
+
+
+
+/*
+ * Sends the SIP CANCEL request, to disconnect a call that
+ * is not in the active state.
+ */
+void
+sipSPISendCancel (ccsipCCB_t *ccb)
+{
+    const char      *fname   = "sipSPISendCancel";
+    sipMessage_t    *request = NULL;
+    sipRet_t         flag    = STATUS_SUCCESS;
+    sipRet_t         tflag   = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    char            *temp = NULL;
+    char             local_cpy[MAX_SIP_URL_LENGTH];
+    string_t         hold_to_tag = strlib_copy(ccb->sip_to);
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "CANCEL");
+
+    messageflag.flags = 0;
+    ccb->authen.cred_type = 0;
+
+    messageflag.flags = SIP_HEADER_CONTENT_LENGTH_BIT;
+
+    /* Remove the to_tag from the CANCEL message if present */
+    sstrncpy(local_cpy, ccb->sip_to, MAX_SIP_URL_LENGTH);
+    temp = strstr(local_cpy, ">");
+    if (temp != NULL) {
+        *(temp + 1) = '\0';
+    }
+    ccb->sip_to = strlib_update(ccb->sip_to, local_cpy);
+
+    request = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateRequest(ccb, messageflag, sipMethodCancel, request, FALSE, 0)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+
+    /* restore the to_tag if it was there */
+    if (hold_to_tag) {
+        ccb->sip_to = strlib_update(ccb->sip_to, hold_to_tag);
+        strlib_free(hold_to_tag);
+    }
+    hold_to_tag = strlib_empty();
+
+    UPDATE_FLAGS(flag, tflag);
+    /* Recalc and add Authorization header if needed */
+    sipSPIGenerateGenAuthorizationResponse(ccb, request, &flag,
+                                           SIP_METHOD_CANCEL);
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request)
+            free_sip_message(request);
+        clean_method_request_trx(ccb, sipMethodCancel, TRUE);
+        return;
+    }
+    /* Record current Request-URI */
+    if (ccb->ReqURI[0]) {
+        sstrncpy(gCallHistory[ccb->index].last_route_request_uri, ccb->ReqURI,
+                 MAX_SIP_URL_LENGTH);
+    } else {
+        memset(gCallHistory[ccb->index].last_route_request_uri, 0,
+               MAX_SIP_URL_LENGTH);
+    }
+    if (SendRequest(ccb, request, sipMethodCancel, FALSE, TRUE, FALSE)
+            == FALSE) {
+        clean_method_request_trx(ccb, sipMethodCancel, TRUE);
+        return;
+    } else {
+        return;
+    }
+}
+
+void
+sip_platform_icmp_unreachable_callback (void *ccb, uint32_t ipaddr)
+{
+    static const char fname[] = "sip_platform_icmp_unreachable_callback";
+    uint32_t *icmp_msg;
+
+    icmp_msg = (uint32_t *) SIPTaskGetBuffer(sizeof(uint32_t));
+    if (!icmp_msg) {
+        CCSIP_DEBUG_ERROR("%s: Error: get buffer failed.\n", fname);
+        return;
+    }
+    *icmp_msg = ((ccsipCCB_t *)ccb)->index;
+
+    if (SIPTaskSendMsg(SIP_ICMP_UNREACHABLE, (cprBuffer_t)icmp_msg,
+                       sizeof(uint32_t), (void *)(long)ipaddr) == CPR_FAILURE) {
+        CCSIP_DEBUG_ERROR("%s: Error: send msg failed.\n", fname);
+        cpr_free((cprBuffer_t)icmp_msg);
+    }
+    return;
+}
+
+/*
+ * Sends the SIP REFER request, to Transfer the call
+ *
+ * Parameters:
+ *     ccb - reference tio the existing call control block
+ *     referto - Dial string to make a call (If null it will pick up from ccb)
+ *     referto_typ  to indicate if the referto is trasnfer or token refer
+ *
+ */
+boolean
+sipSPISendRefer (ccsipCCB_t *ccb, char *referto, sipRefEnum_e referto_type)
+{
+    const char     *fname    = "sipSPISendRefer";
+    sipMessage_t   *request  = NULL;
+    sipRet_t        flag     = STATUS_SUCCESS;
+    sipRet_t        tflag    = STATUS_SUCCESS;
+    ccsipCCB_t     *xfer_ccb = NULL;
+    char            tempreferto[MAX_SIP_URL_LENGTH + 2];
+    char            callid[MAX_SIP_HEADER_LENGTH + 2];
+    sipMessageFlag_t messageflag;
+    char           *semi            = NULL;
+    char           *left_bracket    = NULL;
+    char           *right_bracket   = NULL;
+    char           *msg_referto     = NULL;
+    string_t        copy_of_referto = NULL;
+    int             rpid_flag;
+    char            *ref_to_callid = NULL;
+    const char      *to_tag = NULL;
+    const char      *from_tag = NULL;
+    boolean         dm_info = FALSE;
+    sipJoinInfo_t   join_info;
+
+    memset(&join_info, 0, sizeof(join_info));
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "REFER");
+    /*
+     * Build the request
+     */
+
+    if (sipSPIGenerateReferredByHeader(ccb) == FALSE) {
+        tflag = HSTATUS_FAILURE;
+    } else {
+        tflag = HSTATUS_SUCCESS;
+    }
+    UPDATE_FLAGS(flag, tflag);
+
+    messageflag.flags = 0;
+    /*
+     * Don't add the content length bit here. Content length
+     * needs to be the last in the header. TCP uses the content
+     * length to do framing.
+     */
+    messageflag.flags = SIP_HEADER_CONTACT_BIT | SIP_HEADER_ROUTE_BIT;
+
+    /*
+     * Set RPID header.
+     */
+    rpid_flag = sipSPISetRPID(ccb, TRUE);
+    if (rpid_flag == RPID_ENABLED) {
+        messageflag.flags |= SIP_HEADER_REMOTE_PARTY_ID_BIT;
+    }
+
+    if (referto) {
+        // Get a new call-id if this REFER is for fallback token registration
+        if (strncmp(referto, TOKEN_REFER_TO, sizeof(TOKEN_REFER_TO)) == 0) {
+            ccb->sipCallID[0] = '\0';
+            sip_util_get_new_call_id(ccb);
+        }
+    }
+
+    request = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateRequest(ccb, messageflag, sipMethodRefer, request, FALSE, 0)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Recalc and add Authorization header if needed */
+    sipSPIGenerateGenAuthorizationResponse(ccb, request, &flag,
+                                           SIP_METHOD_REFER);
+
+    memset(tempreferto, 0, MAX_SIP_URL_LENGTH + 2);
+    memset(callid, 0, MAX_SIP_HEADER_LENGTH + 2);
+
+    /* see if we have ;user= */
+    if (referto) {
+        semi = strchr(referto, ';');
+    }
+
+    if (CC_FEATURE_XFER == ccb->featuretype) {
+        // The con_call_id is filled up by GSM when it opens up the line This
+        // is used to cross reference the call_ids
+        xfer_ccb = sip_sm_get_ccb_by_target_call_id(ccb->con_call_id);
+
+        if (xfer_ccb != NULL) {
+            ref_to_callid = xfer_ccb->sipCallID;
+            to_tag = xfer_ccb->sip_to_tag;
+            from_tag = xfer_ccb->sip_from_tag;
+        }
+
+        if (xfer_ccb != NULL || dm_info == TRUE) {
+            int i = 0;
+
+            // Create Refer_to header with replace id (call_id of other call)
+            // and To-Tag of other call - escape the replaced callid
+            while (*ref_to_callid != '\0') {
+                if (*ref_to_callid != '@') {
+                    callid[i++] = *ref_to_callid;
+                } else {
+                    callid[i++] = '%';
+                    callid[i++] = '4';
+                    callid[i++] = '0';
+                }
+                ref_to_callid++;
+            }
+            callid[i] = '\0';
+
+            /* first get rid of opening and closing braces, if there */
+            copy_of_referto = strlib_copy(referto);
+            if (copy_of_referto) {
+                left_bracket = strpbrk(copy_of_referto, "<");
+            }
+            if (left_bracket) {
+                left_bracket++;
+                right_bracket = strchr(left_bracket, '>');
+                if (right_bracket) {
+                    *right_bracket++ = 0;
+                }
+                msg_referto = left_bracket;
+            } else {
+                msg_referto = referto;
+            }
+            if (msg_referto) {
+                if (strncmp(msg_referto, "sip:", 4) == 0) {
+                    snprintf(tempreferto, sizeof(tempreferto),
+                             "<%s%c%s%c%s%%3B%s%%3D%s%%3B%s%%3D%s>",
+                             msg_referto, QUESTION_MARK,
+                             SIP_HEADER_REPLACES, EQUAL_SIGN, callid,
+                             TO_TAG, to_tag,
+                             FROM_TAG, from_tag);
+                } else {
+                    snprintf(tempreferto, sizeof(tempreferto),
+                             "<sip:%s%c%s%c%s%%3B%s%%3D%s%%3B%s%%3D%s>",
+                             msg_referto, QUESTION_MARK,
+                             SIP_HEADER_REPLACES, EQUAL_SIGN, callid,
+                             TO_TAG, to_tag,
+                             FROM_TAG, from_tag);
+                }
+            }
+            strlib_free(copy_of_referto);
+        }
+
+        if (dm_info) {
+            cpr_free(join_info.call_id);
+            cpr_free(join_info.to_tag);
+            cpr_free(join_info.from_tag);
+        }
+
+        tflag = sippmh_add_text_header(request, SIP_HEADER_REFER_TO,
+                                   ((NULL != xfer_ccb)|| (dm_info == TRUE)) ? tempreferto : referto);
+        UPDATE_FLAGS(flag, tflag);
+    } else {
+        if (referto) {
+            if ((strncmp(referto, "<sip:", 5) == 0) ||
+                (strncmp(referto, "sip:", 4) == 0) ||
+                (strncmp(referto, "<urn:", 5) == 0)) {
+                sstrncpy(tempreferto, referto, sizeof(tempreferto));
+
+                /* REFER to get the token should be norefersub */
+                if (strncmp(referto, TOKEN_REFER_TO, sizeof(TOKEN_REFER_TO))
+                        == 0) {
+                    (void) sippmh_add_text_header(request, SIP_HEADER_REQUIRE,
+                                                  "norefersub");
+                }
+            } else {
+                if (semi) {
+                    snprintf(tempreferto, sizeof(tempreferto), "<sip:%s>",
+                             referto);
+                } else {
+                    snprintf(tempreferto, sizeof(tempreferto), "sip:%s",
+                             referto);
+                }
+            }
+        }
+        tflag = sippmh_add_text_header(request, SIP_HEADER_REFER_TO,
+                                       tempreferto);
+        UPDATE_FLAGS(flag, tflag);
+    }
+
+    ccb->sip_referTo = strlib_update(ccb->sip_referTo, referto);
+    tflag = sippmh_add_text_header(request, SIP_HEADER_REFERRED_BY,
+                                   ccb->sip_referredBy);
+    if (tflag != HSTATUS_SUCCESS) {
+        return FALSE;
+    }
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request)
+            free_sip_message(request);
+        clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+        return (FALSE);
+    }
+//    cpr_free(referto);
+
+    /*
+     * Add the content length now.
+     */
+    tflag = sippmh_add_int_header(request, SIP_HEADER_CONTENT_LENGTH, 0);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request) {
+            free_sip_message(request);
+        }
+        return FALSE;
+    }
+
+    ccb->retx_counter = 0;
+
+    if (SendRequest(ccb, request, sipMethodRefer, FALSE, TRUE, FALSE) == FALSE) {
+        clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/*
+ * Sends the Notify
+ * Will be sent when we get OK from target (in case of Refer)
+ *
+ * Note: Assumes that the connection is setup.
+ *
+ * Parameter:
+ *    ccb - call control block
+ *    response
+*/
+boolean
+sipSPISendNotify (ccsipCCB_t *ccb, int response)
+{
+    const char     *fname = "sipSPISendNotify";
+    sipMessage_t   *request = NULL;
+    sipRet_t        flag = STATUS_SUCCESS;
+    sipRet_t        tflag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    char           *body;
+    char            errortext[MAX_SIP_URL_LENGTH];
+    char            subs_state_hdr[SUBS_STATE_HDR_LEN];
+    int             respClass;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "Notify");
+
+    /*
+     * Clean up any remaining transactions before sending the next request.
+     * Normally the transactions are cleaned after final responses (200OK)
+     * and if not they will be cleaned after the timeouts. Currently for
+     * NOTIFYs there are not timeout cleanups, so we will explicitly clean
+     * any outstanding transactions now before the next request.
+     *
+     * This fix works today because we don't really care about any responses
+     * to NOTIFY requests and do not act on them. The transaction layer should
+     * be enhanced to handles these cases. This should be addressed under the
+     * Ringpops feature.
+     */
+    clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+
+    /*
+     * Before we do the real notify work, we may need to clean up the
+     * refer proxy authorization stuff that may have been left laying around
+     * during the transfer.
+     *
+     * This is the Proxy-Authorization that came in the REFER's Refer-To
+     * as an escaped header and therefore should only be alive till the
+     * transfer is done.
+     *
+     * But we have to do this only if we are sending the last notify for
+     * a REFER, i.e. if we are sending the last sipfrag. For ex:
+     * "200 OK", or "404 Not Found". We have to check for this because
+     * after sending a NOTIFY("100 Trying") to the Transferor we send an
+     * INVITE to the target and the Proxy-Authorization is required. When
+     * we send a NOTIFY indicating success or failure, we can remove the
+     * refer_proxy_auth in both ccbs.
+     */
+    respClass = response / 100;
+    if (respClass >= 2) {
+        if (ccb->refer_proxy_auth) {
+            ccsipCCB_t *other_ccb;
+
+            cpr_free(ccb->refer_proxy_auth);
+            ccb->refer_proxy_auth = NULL;
+            // now, find the ccb of the call we transferred to....
+            other_ccb = sip_sm_get_ccb_by_callid(ccb->sipxfercallid);
+            if (other_ccb != NULL) {
+                if (other_ccb->refer_proxy_auth) {
+                    cpr_free(other_ccb->refer_proxy_auth);
+                    other_ccb->refer_proxy_auth = NULL;
+                }
+            }
+        }
+    }
+
+    /*
+     * Build the request
+     */
+    // The addition of the CSeq method will be done when CSeq number is added
+    // ccb->last_sent_request_cseq_method = sipMethodNotify;
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_ROUTE_BIT | SIP_HEADER_CONTACT_BIT;
+
+    request = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateRequest(ccb, messageflag, sipMethodNotify, request, FALSE, 0)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+
+    UPDATE_FLAGS(flag, tflag);
+
+    tflag = sippmh_add_text_header(request, SIP_HEADER_EVENT, SIP_EVENT_REFER);
+    UPDATE_FLAGS(flag, tflag);
+
+    // Add Subscription-State header
+    if (ccb->flags & FINAL_NOTIFY) {
+        snprintf(subs_state_hdr, SUBS_STATE_HDR_LEN,
+                 "terminated; reason=noresource");
+    } else {
+        uint32_t expires_timeout = 0;
+
+        config_get_value(CFGID_TIMER_INVITE_EXPIRES, &expires_timeout,
+                         sizeof(expires_timeout));
+        snprintf(subs_state_hdr, SUBS_STATE_HDR_LEN, "active; expires=%d",
+                 expires_timeout);
+    }
+    tflag = sippmh_add_text_header(request, SIP_HEADER_SUBSCRIPTION_STATE,
+                                   subs_state_hdr);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Recalc and add Authorization header if needed */
+    sipSPIGenerateGenAuthorizationResponse(ccb, request, &flag,
+                                           SIP_METHOD_NOTIFY);
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request) {
+            free_sip_message(request);
+        }
+        clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+        return (FALSE);
+    }
+
+    /* Write message */
+    /*
+     * Currently the only notify message the phone sends is with respect
+     * to refer. This routine looks like a general send notify routine.
+     * One would assume then there needs to be some sort of if (refer)
+     * check to cause it to send "message/sipfrag" otherwise send
+     * "application/sip"
+     */
+//    tflag = sippmh_add_text_header(request, SIP_HEADER_CONTENT_TYPE,
+//                                   SIP_CONTENT_TYPE_SIP );
+    // Don't add content-type explicitly
+    // tflag = sippmh_add_text_header(request, SIP_HEADER_CONTENT_TYPE,
+    //                               SIP_CONTENT_TYPE_SIPFRAG );
+//  UPDATE_FLAGS(flag, tflag);
+
+    body = (char *) cpr_malloc(MAX_SIP_URL_LENGTH * sizeof(char));
+    if (!body) {
+        if (request) {
+            free_sip_message(request);
+        }
+        clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+        return FALSE;
+    }
+    memset(errortext, 0, MAX_SIP_URL_LENGTH);
+    get_sip_error_string(errortext, response);
+    snprintf(body, MAX_SIP_URL_LENGTH, "%s %d %s\r\n", SIP_VERSION,
+             response, errortext);
+
+    tflag = sippmh_add_message_body(request, body, strlen(body),
+                                    SIP_CONTENT_TYPE_SIPFRAG,
+                                    SIP_CONTENT_DISPOSITION_SESSION_VALUE,
+                                    TRUE, NULL);
+    UPDATE_FLAGS(flag, tflag);
+
+    // No need to add header length separately
+    // tflag = sippmh_add_int_header(request, SIP_HEADER_CONTENT_LENGTH,
+    //                              strlen(message_body));
+    // UPDATE_FLAGS(flag, tflag);
+
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request) {
+            free_sip_message(request);
+        }
+        clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+        return (FALSE);
+    }
+
+    ccb->retx_counter = 0;
+
+    if (SendRequest(ccb, request, sipMethodNotify, FALSE, TRUE, FALSE) == FALSE) {
+        clean_method_request_trx(ccb, sipMethodNotify, TRUE);
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/*
+ * Sends the Info
+ *
+ * Note: Assumes that the connection is setup.
+ *
+ * Parameter:
+ *    ccb - call control block
+ *    info_package - the Info-Package header of the Info Package
+ *    content_type - the Content-Type header of the Info Package
+ *    message_body - the message body of the Info Package
+*/
+boolean
+sipSPISendInfo (ccsipCCB_t *ccb, const char *info_package,
+                const char *content_type, const char *message_body)
+{
+    const char     *fname = "sipSPISendInfo";
+    sipMessage_t   *request = NULL;
+    sipRet_t        flag = STATUS_SUCCESS;
+    sipRet_t        tflag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    char           *body;
+    boolean         retval;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "Info");
+
+    /*
+     * Build the request
+     */
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_ROUTE_BIT | SIP_HEADER_CONTACT_BIT;
+
+    request = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateRequest(ccb, messageflag, sipMethodInfo, request, FALSE, 0)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+
+    UPDATE_FLAGS(flag, tflag);
+
+    /* FIXME Media Control currently does not follow the offer/offer
+             negotiation process outlined in IETF draft
+             draft-ietf-sip-info-events-01, so do not add Info-Package
+             header field if it's Media Control Info Package. */
+    if (cpr_strncasecmp(content_type, SIP_CONTENT_TYPE_MEDIA_CONTROL,
+                        strlen(SIP_CONTENT_TYPE_MEDIA_CONTROL)) != 0) {
+        tflag = sippmh_add_text_header(request, SIP_HEADER_INFO_PACKAGE, info_package);
+        UPDATE_FLAGS(flag, tflag);
+    }
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request) {
+            free_sip_message(request);
+        }
+        return FALSE;
+    }
+
+    body = (char *) cpr_malloc((strlen(message_body) + 1) * sizeof(char));
+    if (!body) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_MEMORY_OUT_OF_MEM), fname);
+        if (request) {
+            free_sip_message(request);
+        }
+        return FALSE;
+    }
+    memcpy(body, message_body, strlen(message_body) + 1);
+
+    tflag = sippmh_add_message_body(request, body, strlen(body),
+                                    content_type,
+                                    SIP_CONTENT_DISPOSITION_SESSION_VALUE,
+                                    TRUE, NULL);
+    flag = tflag;
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        cpr_free(body);
+        if (request) {
+            free_sip_message(request);
+        }
+        return FALSE;
+    }
+
+    retval = SendRequest(ccb, request, sipMethodInfo, TRUE, FALSE, FALSE);
+
+    // Don't keep the trx so as not to mess up the retran timer of other requests
+    /*
+     * FIXME fix it when the framework is modified to support concurrent requests
+     */
+    clean_method_request_trx(ccb, sipMethodInfo, TRUE);
+
+    return retval;
+}
+
+/*
+ * Sends the BYE or CANCEL response for the call.
+ * Assumes that
+ * - the connection is setup.
+ */
+boolean
+sipSPISendByeOrCancelResponse (ccsipCCB_t *ccb, sipMessage_t *request,
+                               sipMethod_t sipMethodByeorCancel)
+{
+    const char      *fname = "sipSPISendByeResponse";
+    sipMessage_t    *response = NULL;
+    sipRet_t         flag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    boolean          result;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_RESPONSE),
+                      fname, 200);
+
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_CONTENT_LENGTH_BIT;
+
+    response = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateResponse(ccb, messageflag, SIP_STATUS_SUCCESS, response,
+                       SIP_SUCCESS_SETUP_PHRASE, 0, NULL, sipMethodByeorCancel)) {
+        flag = HSTATUS_SUCCESS;
+    } else {
+        flag = HSTATUS_FAILURE;
+    }
+
+    /* send call stats on BYE response */
+    if ((flag == STATUS_SUCCESS) && (sipMethodByeorCancel == sipMethodBye)) {
+        flag = sipSPIAddCallStats(ccb, response);
+    }
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (response) {
+            free_sip_message(response);
+        }
+        clean_method_request_trx(ccb, sipMethodByeorCancel, FALSE);
+        return (FALSE);
+    }
+    result = sendResponse(ccb, response, request, FALSE, sipMethodByeorCancel);
+    clean_method_request_trx(ccb, sipMethodByeorCancel, FALSE);
+    return (result);
+}
+
+
+
+/*
+ * Send 100 TRYING
+ */
+void
+sipSPISendInviteResponse100 (ccsipCCB_t *ccb, boolean remove_to_tag)
+{
+    char    *temp = NULL;
+    char     local_cpy[MAX_SIP_URL_LENGTH];
+    string_t hold_to_tag = NULL;
+
+    if (remove_to_tag) {
+        hold_to_tag = strlib_copy(ccb->sip_to);
+
+    /* remove the to_tag from the 100 Trying message */
+    sstrncpy(local_cpy, ccb->sip_to, MAX_SIP_URL_LENGTH);
+    temp = strstr(local_cpy, ">");
+    if (temp != NULL) {
+        *(temp + 1) = '\0';
+    }
+    ccb->sip_to = strlib_update(ccb->sip_to, local_cpy);
+    }
+
+    sipSPISendInviteResponse(ccb, SIP_1XX_TRYING, SIP_1XX_TRYING_PHRASE,
+                             0, NULL, FALSE, /* no SDP */
+                             FALSE /* no reTx */);
+
+    if (hold_to_tag) {
+        ccb->sip_to = strlib_update(ccb->sip_to, hold_to_tag);
+        strlib_free(hold_to_tag);
+    }
+}
+
+
+/*
+ * Send 180 RINGING
+ */
+void
+sipSPISendInviteResponse180 (ccsipCCB_t *ccb)
+{
+    sipSPISendInviteResponse(ccb, SIP_1XX_RINGING,
+                             SIP_1XX_RINGING_PHRASE, 0, NULL,
+                             (boolean)(ccb->flags & INBAND_ALERTING),
+                             FALSE /* no reTx */);
+}
+
+
+/*
+ * Send 200 OK
+ */
+void
+sipSPISendInviteResponse200 (ccsipCCB_t *ccb)
+{
+    sipSPISendInviteResponse(ccb, SIP_STATUS_SUCCESS,
+                             SIP_SUCCESS_SETUP_PHRASE, 0, NULL,
+                             TRUE, TRUE /* reTx */);
+}
+
+void
+sipSPISendInviteResponse302 (ccsipCCB_t *ccb)
+{
+
+    sipSPISendInviteResponse(ccb, SIP_RED_MOVED_TEMP,
+                             SIP_RED_MOVED_TEMP_PHRASE,
+                             0, NULL, FALSE, /* no SDP */
+                             TRUE /* reTx */);
+}
+
+/*Send Option Response
+ * - connection is set up.
+ * Currently just gives the methods supported
+ * Does not affect call state.
+*/
+boolean
+sipSPISendOptionResponse (ccsipCCB_t *ccb, sipMessage_t *request)
+{
+    const char      *fname = "SIPSPISendOptionResponse";
+    sipMessage_t    *response = NULL;
+    sipRet_t         flag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    boolean          result;
+
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_CONTACT_BIT |
+                        SIP_HEADER_RECORD_ROUTE_BIT |
+                        SIP_HEADER_ALLOW_BIT |
+                        SIP_HEADER_ACCEPT_BIT |
+                        SIP_HEADER_ACCEPT_ENCODING_BIT |
+                        SIP_HEADER_ACCEPT_LANGUAGE_BIT |
+                        SIP_HEADER_SUPPORTED_BIT;
+
+    /* Write SDP */
+    messageflag.flags |= SIP_HEADER_OPTIONS_CONTENT_TYPE_BIT;
+
+    response = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateResponse(ccb, messageflag, SIP_STATUS_SUCCESS, response,
+                       SIP_SUCCESS_SETUP_PHRASE, 0, NULL, sipMethodOptions)) {
+        flag = HSTATUS_SUCCESS;
+    } else {
+        flag = HSTATUS_FAILURE;
+    }
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (response) {
+            free_sip_message(response);
+        }
+        clean_method_request_trx(ccb, sipMethodOptions, FALSE);
+        return (FALSE);
+    }
+    result = sendResponse(ccb, response, request, FALSE, sipMethodOptions);
+    clean_method_request_trx(ccb, sipMethodOptions, FALSE);
+    return (result);
+}
+
+/*
+ * The function below handles an OPTIONS message not associated with
+ * any ongoing dialog and serves for the phone to gather capabilities
+ * of its server
+ */
+boolean
+sipSPIsendNonActiveOptionResponse (sipMessage_t *msg,
+                                   cc_msgbody_info_t *local_msg_body)
+{
+    const char    *fname = "sipSPIsendNonActiveOptionResponse";
+    sipMessage_t  *response       = NULL;
+    sipRet_t       flag           = STATUS_SUCCESS;
+    sipRet_t       tflag          = STATUS_SUCCESS;
+    const char    *sip_from       = NULL;
+    const char    *sip_to         = NULL;
+    const char    *request_callid = NULL;
+    const char    *request_cseq   = NULL;
+    sipCseq_t     *request_cseq_structure = NULL;
+    char           temp[MAX_SIP_HEADER_LENGTH];
+    sipLocation_t *to_loc         = NULL;
+    char           sip_to_tag[MAX_SIP_TAG_LENGTH];
+    char           sip_to_temp[MAX_SIP_URL_LENGTH];
+    sipLocation_t *from_loc       = NULL;
+    boolean        request_uri_error = FALSE;
+    sipReqLine_t  *requestURI     = NULL;
+    sipLocation_t *uri_loc        = NULL;
+    const char    *accept_hdr     = NULL;
+    const char    *supported      = NULL;
+    int            kpml_config;
+
+    if (!msg) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "msg");
+        return (FALSE);
+    }
+
+    // Parse through the Accept header to get the supported capabilities
+    accept_hdr = sippmh_get_header_val(msg, SIP_HEADER_ACCEPT, NULL);
+    if (accept_hdr) {
+        server_caps = sippmh_parse_accept_header(accept_hdr);
+    }
+
+    // Parse through the Supported header to get the supported capabilities
+    supported = sippmh_get_cached_header_val(msg, SUPPORTED);
+    if (supported) {
+        sippmh_parse_supported_require(supported, NULL);
+    }
+
+    response = GET_SIP_MESSAGE();
+    if (!response) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "GET_SIP_MESSAGE()");
+        return (FALSE);
+    }
+
+    sip_from = sippmh_get_cached_header_val(msg, FROM);
+    sip_to = sippmh_get_cached_header_val(msg, TO);
+    sstrncpy(sip_to_temp, sip_to, MAX_SIP_URL_LENGTH);
+    request_callid = sippmh_get_cached_header_val(msg, CALLID);
+
+    requestURI = sippmh_get_request_line(msg);
+    if (requestURI) {
+        if (requestURI->url) {
+            uri_loc = sippmh_parse_from_or_to(requestURI->url, TRUE);
+            if (uri_loc) {
+                if (uri_loc->genUrl->schema != URL_TYPE_SIP) {
+                    request_uri_error = TRUE;
+                }
+                sippmh_free_location(uri_loc);
+            } else {
+                request_uri_error = TRUE;
+            }
+        } else {
+            request_uri_error = TRUE;
+        }
+        SIPPMH_FREE_REQUEST_LINE(requestURI);
+    } else {
+        request_uri_error = TRUE;
+    }
+    if (request_uri_error) {
+        CCSIP_DEBUG_ERROR("%s: Error: Invalid Request URI failed.\n", fname);
+        free_sip_message(response);
+        /* Send 400 error */
+        if (sipSPISendErrorResponse(msg, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_REQLINE_ERROR,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return (FALSE);
+    }
+
+
+    /*
+     * Parse From
+     */
+    from_loc = sippmh_parse_from_or_to((char *)sip_from, TRUE);
+    if (!from_loc) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_FROM));
+        free_sip_message(response);
+        /* Send 400 error */
+        if (sipSPISendErrorResponse(msg, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_FROMURL_ERROR,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return (FALSE);
+    }
+
+    /*
+     * From is parsed just to make sure we got valid From
+     * Header, so we free it now
+     */
+    sippmh_free_location(from_loc);
+
+    /*
+     * Parse To
+     */
+    to_loc = sippmh_parse_from_or_to((char *)sip_to, TRUE);
+    if (!to_loc) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO));
+        if (response) {
+            free_sip_message(response);
+        }
+        /* Send 400 error */
+        if (sipSPISendErrorResponse(msg, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_ToURL_ERROR,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return (FALSE);
+    }
+
+    /* Check/Generate tags */
+    if (to_loc->tag) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), NULL,
+                          NULL, fname, "Initial Option with to_tag");
+        if (response) {
+            free_sip_message(response);
+        }
+        if (sipSPISendErrorResponse(msg, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_ToURL_ERROR,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        sippmh_free_location(to_loc);
+        return (FALSE);
+    } else {
+        sip_util_make_tag(sip_to_tag);
+        sstrncat(sip_to_temp, ";tag=",
+                sizeof(sip_to_temp) - strlen(sip_to_temp));
+        sstrncat(sip_to_temp, sip_to_tag,
+                sizeof(sip_to_temp) - strlen(sip_to_temp));
+    }
+    sippmh_free_location(to_loc);
+
+    tflag = sippmh_add_response_line(response, SIP_VERSION, SIP_STATUS_SUCCESS,
+                                     SIP_SUCCESS_SETUP_PHRASE);
+    UPDATE_FLAGS(flag, tflag);
+    tflag = (sipSPIAddRequestVia(NULL, response, msg, sipMethodOptions)) ?
+        STATUS_SUCCESS : STATUS_FAILURE;
+    UPDATE_FLAGS(flag, tflag);
+    tflag = sippmh_add_text_header(response, SIP_HEADER_FROM, sip_from);
+    UPDATE_FLAGS(flag, tflag);
+    tflag = sippmh_add_text_header(response, SIP_HEADER_TO, sip_to_temp);
+    UPDATE_FLAGS(flag, tflag);
+    tflag = sippmh_add_text_header(response, SIP_HEADER_CALLID, request_callid);
+    UPDATE_FLAGS(flag, tflag);
+
+    // Add date header
+    tflag = sipAddDateHeader(response);
+    UPDATE_FLAGS(flag, tflag);
+    /* Write CSeq */
+    request_cseq = sippmh_get_cached_header_val(msg, CSEQ);
+    if (request_cseq) {
+        request_cseq_structure = sippmh_parse_cseq(request_cseq);
+        if (!request_cseq_structure) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sippmh_parse_cseq()");
+            free_sip_message(response);
+            /*
+             * We should send 400 here but as we don't have Cseq this
+             * is not possible.
+             */
+            return FALSE;
+        }
+        if (request_cseq_structure->method != sipMethodOptions) {
+            CCSIP_DEBUG_ERROR("%s: Error: Invalid method in Cseq failed.\n",
+                              fname);
+            free_sip_message(response);
+            /* Send 400 error */
+            if (sipSPISendErrorResponse(msg, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        SIP_WARN_MISC,
+                                        SIP_CLI_ERR_BAD_REQ_VIA_OR_CSEQ,
+                                        NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+            cpr_free(request_cseq_structure);
+            return FALSE;
+        }
+        tflag = sippmh_add_text_header(response, SIP_HEADER_CSEQ, request_cseq);
+        cpr_free(request_cseq_structure);
+        UPDATE_FLAGS(flag, tflag);
+    }
+
+    tflag = sippmh_add_text_header(response, SIP_HEADER_SERVER,
+                                   sipHeaderServer);
+
+    UPDATE_FLAGS(flag, tflag);
+
+    /*
+     * Add the local sdp we got from gsm into the response
+     */
+    tflag = CopyLocalSDPintoResponse(response, local_msg_body);
+    UPDATE_FLAGS(flag, tflag);
+
+    // Add Allow
+    snprintf(temp, MAX_SIP_HEADER_LENGTH, "%s,%s,%s,%s,%s,%s,%s,%s,%s",
+             SIP_METHOD_ACK, SIP_METHOD_BYE, SIP_METHOD_CANCEL,
+             SIP_METHOD_INVITE, SIP_METHOD_NOTIFY, SIP_METHOD_OPTIONS,
+             SIP_METHOD_REFER, SIP_METHOD_REGISTER, SIP_METHOD_UPDATE);
+    tflag = sippmh_add_text_header(response, SIP_HEADER_ALLOW, temp);
+    UPDATE_FLAGS(flag, tflag);
+
+    // Add Allow-Events
+    config_get_value(CFGID_KPML_ENABLED, &kpml_config, sizeof(kpml_config));
+    if (kpml_config) {
+        snprintf(temp, MAX_SIP_HEADER_LENGTH, "%s,%s,%s", SIP_EVENT_KPML,
+                 SIP_EVENT_DIALOG, SIP_EVENT_REFER);
+    } else {
+        snprintf(temp, MAX_SIP_HEADER_LENGTH, "%s,%s", SIP_EVENT_DIALOG,
+                 SIP_EVENT_REFER);
+    }
+    tflag = sippmh_add_text_header(response, SIP_HEADER_ALLOW_EVENTS, temp);
+    UPDATE_FLAGS(flag, tflag);
+
+    // Add Accept
+    snprintf(temp, MAX_SIP_HEADER_LENGTH, "%s,%s,%s",
+             SIP_CONTENT_TYPE_SDP,
+             SIP_CONTENT_TYPE_MULTIPART_MIXED,
+             SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE);
+    tflag = sippmh_add_text_header(response, SIP_HEADER_ACCEPT, temp);
+    UPDATE_FLAGS(flag, tflag);
+
+    // Add Accept-Encoding
+    tflag = sippmh_add_text_header(response, SIP_HEADER_ACCEPT_ENCODING,
+                                   "identity");
+    UPDATE_FLAGS(flag, tflag);
+
+    // Add Accept-Language
+    tflag = sippmh_add_text_header(response, SIP_HEADER_ACCEPT_LANGUAGE, "en");
+    UPDATE_FLAGS(flag, tflag);
+
+    tflag = sippmh_add_text_header(response, SIP_HEADER_SUPPORTED,
+                                   SIP_RFC_SUPPORTED_TAGS);
+
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Determine from the Via field where the message is supposed
+     * to be sent
+     */
+
+    if (tflag != HSTATUS_SUCCESS) {
+        free_sip_message(response);
+        return FALSE;
+    }
+    return sendResponse(NULL, response, msg, FALSE, sipMethodOptions);
+}
+
+
+/*
+ * Send the invite response. Adds the session description
+ * if send_sd is TRUE. Assumes
+ * - connection is set up.
+ * Does not affect call state.
+ */
+void
+sipSPISendInviteResponse (ccsipCCB_t *ccb,
+                          uint16_t statusCode,
+                          const char *reason_phrase,
+                          uint16_t status_code_warning,
+                          const char *reason_phrase_warning,
+                          boolean send_sd,
+                          boolean retx)
+{
+    const char      *fname = "SIPSPISendInviteResponse";
+    sipMessage_t    *response = NULL;
+    sipRet_t         flag = STATUS_SUCCESS;
+    sipRet_t         tflag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    int              rpid_flag;
+
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_RESPONSE),
+                      fname, statusCode);
+
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_CONTACT_BIT |
+                        SIP_HEADER_RECORD_ROUTE_BIT |
+                        SIP_HEADER_ALLOW_BIT |
+                        SIP_HEADER_DIVERSION_BIT |
+                        SIP_HEADER_ALLOW_EVENTS_BIT;
+
+    /* Write Content-Type */
+    /* Add SDP body if required */
+    if (send_sd) {
+        messageflag.flags |= SIP_HEADER_CONTENT_TYPE_BIT;
+    } else {
+        messageflag.flags |= SIP_HEADER_CONTENT_LENGTH_BIT;
+    }
+    if (statusCode == SIP_CLI_ERR_EXTENSION) {
+        messageflag.flags |= SIP_HEADER_UNSUPPORTED_BIT;
+    }
+    if ((statusCode >=SIP_1XX_TRYING) && (statusCode <= SIP_STATUS_SUCCESS)) {
+        messageflag.flags |= SIP_HEADER_SUPPORTED_BIT;
+    }
+    if (statusCode == SIP_SERV_ERR_INTERNAL) {
+        messageflag.flags |= SIP_HEADER_RETRY_AFTER_BIT;
+    }
+    if ((statusCode == SIP_1XX_TRYING) || (statusCode == SIP_STATUS_SUCCESS)) {
+        messageflag.flags |= SIP_HEADER_RECV_INFO_BIT;
+    }
+
+    /*
+     * Set RPID header.
+     */
+    if (statusCode != SIP_1XX_TRYING) {
+        /*
+         * The RPID header is not sent in the 100 Trying because the response is directly
+         * sent from the SIP stack.The information needed for building the RPID is updated
+         * by the gsm later.
+         */
+        rpid_flag = sipSPISetRPID(ccb, FALSE);
+        if (rpid_flag == RPID_ENABLED) {
+            messageflag.flags |= SIP_HEADER_REMOTE_PARTY_ID_BIT;
+        }
+    }
+
+    response = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateResponse(ccb, messageflag, statusCode, response, reason_phrase,
+                       status_code_warning, reason_phrase_warning,
+                       sipMethodInvite)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+
+    UPDATE_FLAGS(flag, tflag);
+
+    tflag = sipSPIAddCallStats(ccb, response);
+
+    UPDATE_FLAGS(flag, tflag);
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (response) {
+            free_sip_message(response);
+        }
+        return;
+    }
+
+    (void) sendResponse(ccb, response, ccb->last_request, retx, sipMethodInvite);
+    return;
+}
+
+
+void
+sipSPIGenerateGenAuthorizationResponse (ccsipCCB_t *ccb,
+                                        sipMessage_t *request,
+                                        sipRet_t *flag,
+                                        char *method)
+{
+    const char   *fname = "sipSPIGenerateGenAuthorizationResponse";
+    sipRet_t      tflag = STATUS_SUCCESS;
+    char         *author_str = NULL;
+    credentials_t credentials;
+
+    /*
+     * CSCds70538
+     * Re-generate the Authorization header for CANCEL, BYE, mid-INVITE
+     * The header is cached after an INVITE is challenged.
+     * I know that I have been challenged preciously because sip_authen
+     * is not NULL
+     */
+    if (ccb->authen.sip_authen) {
+        cred_get_line_credentials(ccb->dn_line, &credentials,
+                                  sizeof(credentials.id),
+                                  sizeof(credentials.pw));
+        if (sipSPIGenerateAuthorizationResponse(ccb->authen.sip_authen,
+                    ccb->ReqURI, method, credentials.id, credentials.pw,
+                    &author_str, &(ccb->authen.nc_count), ccb)) {
+
+            if (ccb->authen.authorization != NULL) {
+                cpr_free(ccb->authen.authorization);
+                ccb->authen.authorization = NULL;
+            }
+
+            /*
+             * Don't free the sip_authen since we were not challenged
+             */
+            ccb->authen.authorization = (char *)
+                cpr_malloc(strlen(author_str) + 1);
+
+            /*
+             * Cache the Authorization header so that it can be used for later
+             * requests
+             */
+            if (ccb->authen.authorization != NULL) {
+                sstrncpy(ccb->authen.authorization, author_str,
+                         strlen(author_str) * sizeof(char) + 1);
+            }
+
+            cpr_free(author_str);
+        } else {
+            CCSIP_DEBUG_ERROR("%s: Error: Authorization header build "
+                              "unsuccessful\n", fname);
+        }
+    }
+
+    if (ccb->authen.authorization != NULL) {
+        tflag = sippmh_add_text_header(request,
+                                       AUTHOR_HDR(ccb->authen.status_code),
+                                       ccb->authen.authorization);
+        UPDATE_FLAGS(*flag, tflag);
+    }
+}
+
+
+/*
+ * Sends the 202 Refer Accepted .
+ * Assumes that
+ * - the connection is setup.
+ * Will be sent when we get Refer
+*/
+boolean
+sipSPISendReferResponse202 (ccsipCCB_t *ccb)
+{
+    const char      *fname = "SIPSPISendReferResponse";
+    sipMessage_t    *response = NULL;
+    sipRet_t         flag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    boolean          result;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_RESPONSE),
+                      fname, SIP_ACCEPTED);
+
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_CONTACT_BIT |
+                        SIP_HEADER_RECORD_ROUTE_BIT |
+                        SIP_HEADER_CONTENT_LENGTH_BIT;
+
+    /* Add Content Length */
+    response = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateResponse(ccb, messageflag, SIP_ACCEPTED, response,
+                       SIP_ACCEPTED_PHRASE, 0, NULL, sipMethodRefer)) {
+        flag = HSTATUS_SUCCESS;
+    } else {
+        flag = HSTATUS_FAILURE;
+    }
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (response)
+            free_sip_message(response);
+        clean_method_request_trx(ccb, sipMethodRefer, FALSE);
+        return (FALSE);
+    }
+
+    result = sendResponse(ccb, response, ccb->last_request, FALSE,
+                          sipMethodRefer);
+    clean_method_request_trx(ccb, sipMethodRefer, FALSE);
+    return (result);
+}
+
+/*
+ * General function to send an error response for a given request.
+ * Does not affect call state or disconnection flags.
+ *
+ * Valid values of warn_code is 3xx. Set warn_code to ZERO, when
+ * warn_code is absent. Use of a valid warn_code, will lead to
+ * generation of Warning Header in the response.
+ */
+boolean
+sipSPISendErrorResponse (sipMessage_t *msg,
+                         uint16_t status_code,
+                         const char *reason_phrase,
+                         uint16_t status_code_warning,
+                         const char *reason_phrase_warning,
+                         ccsipCCB_t *ccb)
+{
+    const char   *fname = "sipSPISendErrorResponse";
+    sipMessage_t *response = NULL;
+    sipRet_t      flag     = STATUS_SUCCESS;
+    sipRet_t      tflag    = STATUS_SUCCESS;
+    const char   *sip_from = NULL;
+    const char   *sip_to   = NULL;
+    const char   *request_callid = NULL;
+    const char   *request_cseq   = NULL;
+    sipCseq_t    *request_cseq_structure = NULL;
+    sipMethod_t   method   = sipMethodInvalid;
+    boolean       result   = FALSE;
+    char          temp[MAX_SIP_HEADER_LENGTH];
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_RESPONSE),
+                      fname, status_code);
+
+    if (!msg) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "msg");
+        return (FALSE);
+    }
+
+    response = GET_SIP_MESSAGE();
+    if (!response) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "GET_SIP_MESSAGE()");
+        return (FALSE);
+    }
+
+    tflag = sippmh_add_response_line(response, SIP_VERSION, status_code,
+                                      reason_phrase) ? STATUS_FAILURE : STATUS_SUCCESS;
+
+    UPDATE_FLAGS(flag, tflag);
+
+    tflag = (sipSPIAddRequestVia(NULL, response, msg, sipMethodInvalid)) ?
+        STATUS_SUCCESS : STATUS_FAILURE;
+    UPDATE_FLAGS(flag, tflag);
+
+    sip_from = sippmh_get_cached_header_val(msg, FROM);
+    sip_to = sippmh_get_cached_header_val(msg, TO);
+    request_callid = sippmh_get_cached_header_val(msg, CALLID);
+    tflag = sippmh_add_text_header(response, SIP_HEADER_FROM, sip_from);
+    UPDATE_FLAGS(flag, tflag);
+    tflag = sippmh_add_text_header(response, SIP_HEADER_TO, sip_to);
+    UPDATE_FLAGS(flag, tflag);
+    tflag = sippmh_add_text_header(response, SIP_HEADER_CALLID, request_callid);
+    UPDATE_FLAGS(flag, tflag);
+
+    // Add date header
+    tflag = sipAddDateHeader(response);
+    UPDATE_FLAGS(flag, tflag);
+
+    if (reason_phrase_warning) {
+        char *warning = NULL;
+
+        warning = (char *) cpr_malloc(strlen(reason_phrase_warning) + 5);
+        if (warning) {
+            snprintf(warning, strlen(reason_phrase_warning) + 5,
+                     "%d %s", status_code_warning, reason_phrase_warning);
+            tflag = sippmh_add_text_header(response, SIP_HEADER_WARN, warning);
+            UPDATE_FLAGS(flag, tflag);
+            cpr_free(warning);
+        }
+    }
+
+    // Add Retry-After, if needed
+    if (status_code == SIP_SERV_ERR_INTERNAL &&
+        status_code_warning == SIP_WARN_PROCESSING_PREVIOUS_REQUEST) {
+        tflag = sippmh_add_int_header(response, SIP_HEADER_RETRY_AFTER,
+                                      abs((cpr_rand() % 11)));
+    }
+
+    //Add Accept-Encoding, Accept and Accept-Language headers
+    //if outgoing response is 415-Unsupported Media Type
+    if (status_code == SIP_CLI_ERR_MEDIA) {
+        snprintf(temp, MAX_SIP_HEADER_LENGTH, "%s,%s,%s,%s,%s",
+                 SIP_CONTENT_TYPE_SDP,
+                 SIP_CONTENT_TYPE_MULTIPART_MIXED,
+                 SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE,
+                 SIP_CONTENT_TYPE_SIPFRAG,
+                 SIP_CONTENT_TYPE_MWI);
+        // should we add all registered Info Package Content-Type here??
+        tflag = sippmh_add_text_header(response, SIP_HEADER_ACCEPT, temp);
+        UPDATE_FLAGS(flag, tflag);
+
+        tflag = sippmh_add_text_header(response, SIP_HEADER_ACCEPT_ENCODING,
+                                       SIP_CONTENT_ENCODING_IDENTITY);
+        UPDATE_FLAGS(flag, tflag);
+
+        tflag = sippmh_add_text_header(response, SIP_HEADER_ACCEPT_LANGUAGE,
+                                       "en");
+        UPDATE_FLAGS(flag, tflag);
+    }
+
+    if (status_code == SIP_CLI_ERR_NOT_ALLOWED) {
+        // Add Allow
+        snprintf(temp, MAX_SIP_HEADER_LENGTH, "%s,%s,%s,%s,%s,%s,%s,%s,%s",
+                 SIP_METHOD_ACK, SIP_METHOD_BYE, SIP_METHOD_CANCEL,
+                 SIP_METHOD_INVITE, SIP_METHOD_NOTIFY, SIP_METHOD_OPTIONS,
+                 SIP_METHOD_REFER, SIP_METHOD_UPDATE, SIP_METHOD_SUBSCRIBE);
+        tflag = sippmh_add_text_header(response, SIP_HEADER_ALLOW, temp);
+        UPDATE_FLAGS(flag, tflag);
+    }
+
+    /* Write CSeq */
+    request_cseq = sippmh_get_cached_header_val(msg, CSEQ);
+    if (request_cseq) {
+        request_cseq_structure = sippmh_parse_cseq(request_cseq);
+        if (!request_cseq_structure) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sippmh_parse_cseq()");
+            free_sip_message(response);
+            return (FALSE);
+        }
+        tflag = sippmh_add_text_header(response, SIP_HEADER_CSEQ, request_cseq);
+        method = request_cseq_structure->method;
+        cpr_free(request_cseq_structure);
+        UPDATE_FLAGS(flag, tflag);
+    } else {
+        CCSIP_DEBUG_ERROR("%s: Error: Did not find valid CSeq header. "
+                          "Cannot send response.\n", fname);
+        if (response) {
+            free_sip_message(response);
+            response = NULL;
+        }
+    }
+
+    /* Write Content-Length: 0 */
+    tflag = sippmh_add_int_header(response, SIP_HEADER_CONTENT_LENGTH, 0);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (response) {
+            free_sip_message(response);
+            response = NULL;
+        }
+    }
+
+    /* Determine from the Via field where the message is supposed
+     * to be sent
+     */
+    if (response) {
+        result = sendResponse(NULL, response, msg, FALSE, method);
+    }
+    if (ccb) {
+        clean_method_request_trx(ccb, method, FALSE);
+    }
+    return (result);
+}
+
+void
+sipSPISendFailureResponseAck (ccsipCCB_t *ccb, sipMessage_t *response,
+                              boolean prevcall, line_t previous_call_line)
+{
+    const char *fname = "sipSPISendFailureResponseAck";
+
+    sipMessage_t   *request = NULL;
+    sipRet_t        flag = STATUS_SUCCESS;
+    sipRet_t        tflag = STATUS_SUCCESS;
+
+    char            src_addr_str[MAX_IPADDR_STR_LEN];
+    static char     via[SIP_MAX_VIA_LENGTH];
+    char            via_branch[SIP_MAX_VIA_LENGTH];
+    const char     *response_to = NULL;
+    const char     *response_from = NULL;
+    const char     *response_callid = NULL;
+    const char     *response_cseq = NULL;
+
+    cpr_ip_addr_t   dest_ipaddr;
+    cpr_ip_addr_t   src_ipaddr;
+    uint32_t        dest_port = 0;
+    uint32_t        cseq_number = 0;
+    sipCseq_t      *response_cseq_structure;
+    sipMethod_t     response_cseq_method = sipMethodInvalid;
+    sipRespLine_t  *respLine = NULL;
+    int             status_code = 0;
+    char            local_ReqURI[MAX_SIP_URL_LENGTH];
+    line_t          dn_line;
+    const char     *authenticate = NULL;
+    credentials_t   credentials;
+    sip_authen_t   *sip_authen = NULL;
+    sipAuthenticate_t authen;
+    char           *author_str = NULL;
+    int             nc_count = 0;
+    boolean         bad_authentication = FALSE;
+    int16_t         trx_index = -1;
+    line_t          line;
+    int             nat_enable = 0;
+    int             reldel_stored_msg = RELDEV_NO_STORED_MSG;
+
+    CPR_IP_ADDR_INIT(dest_ipaddr);
+    CPR_IP_ADDR_INIT(src_ipaddr);
+
+    if (prevcall) {
+        dest_ipaddr = gCallHistory[previous_call_line].last_bye_dest_ipaddr;
+        dest_port   = gCallHistory[previous_call_line].last_bye_dest_port;
+        dn_line     = gCallHistory[previous_call_line].dn_line;
+        sstrncpy(local_ReqURI,
+                 gCallHistory[previous_call_line].last_route_request_uri,
+                 sizeof(local_ReqURI));
+        sstrncpy(via_branch, gCallHistory[previous_call_line].via_branch,
+                sizeof(via_branch));
+    } else {
+        /*
+         * The ACK to a 300-699 response MUST
+         * be sent to same address, port, and
+         * transport to which the original request was sent.
+         */
+        dn_line = ccb->dn_line;
+        // Get the via_branch from the last request that we sent
+        trx_index = get_method_request_trx_index(ccb, sipMethodInvite, TRUE);
+        if (trx_index != -1) {
+            sstrncpy(via_branch,
+                    (const char *)(ccb->sent_request[trx_index].u.sip_via_branch),
+                    sizeof(via_branch));
+        }
+        // via_branch = (char *) ccb->sip_via_branch;
+        /*
+         * Use outbound proxy if we used to send messages for this call,
+         * we are using the default proxy, we are not using the Emergency
+         * route, and it is not the backup proxy registration.
+         */
+        if (util_check_if_ip_valid(&(ccb->outBoundProxyAddr)) &&
+            (ccb->proxySelection == SIP_PROXY_DEFAULT) &&
+            (ccb->routeMode != RouteEmergency) &&
+            (ccb->index != REG_BACKUP_CCB)) {
+            dest_ipaddr = ccb->outBoundProxyAddr;
+            if (ccb->outBoundProxyPort != 0) {
+                dest_port = ccb->outBoundProxyPort;
+            } else {
+                config_get_value(CFGID_OUTBOUND_PROXY_PORT, &dest_port,
+                                 sizeof(dest_port));
+            }
+        } else {
+            dest_ipaddr = ccb->dest_sip_addr;
+            dest_port = ccb->dest_sip_port;
+        }
+    }
+
+    response_cseq = sippmh_get_cached_header_val(response, CSEQ);
+    if (!response_cseq) {
+        CCSIP_DEBUG_ERROR("%s: Error: Unable to obtain request's CSeq "
+                          "header.\n", fname);
+        return;
+    }
+    response_cseq_structure = sippmh_parse_cseq(response_cseq);
+    if (!response_cseq_structure) {
+        CCSIP_DEBUG_ERROR("%s: Error: Unable to parse request's CSeq "
+                          "header.\n", fname);
+        return;
+    }
+    cseq_number = response_cseq_structure->number;
+    response_cseq_method = response_cseq_structure->method;
+    cpr_free(response_cseq_structure);
+
+    // Process the Failure response
+    response_to = sippmh_get_cached_header_val(response, TO);
+    response_from = sippmh_get_cached_header_val(response, FROM);
+    response_callid = sippmh_get_cached_header_val(response, CALLID);
+
+    request = GET_SIP_MESSAGE();
+    if (!request) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "GET_SIP_MESSAGE()");
+        return;
+    }
+
+    /*
+     * Determine the Request-URI
+     */
+    respLine = sippmh_get_response_line(response);
+    if (respLine) {
+        status_code = respLine->status_code;
+        SIPPMH_FREE_RESPONSE_LINE(respLine);
+    }
+
+    if (prevcall == FALSE) {
+        char tmp_ReqURI[MAX_SIP_URL_LENGTH];
+
+        // Save original ReqURI
+        sstrncpy(tmp_ReqURI, ccb->ReqURI, MAX_SIP_URL_LENGTH);
+        // Update ReqURI using RR, etc
+        (void) sipSPIGenRequestURI(ccb, sipMethodAck, FALSE);
+        // Use the ReqURI
+        sstrncpy(local_ReqURI, ccb->ReqURI, MAX_SIP_URL_LENGTH);
+        // Restore original ReqURI
+        sstrncpy(ccb->ReqURI, tmp_ReqURI, MAX_SIP_URL_LENGTH);
+
+        // Clean up the INVITE transaction block as we no longer need it
+        clean_method_request_trx(ccb, sipMethodInvite, TRUE);
+    }
+
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        sip_config_get_net_device_ipaddr(&src_ipaddr);
+        ipaddr2dotted(src_addr_str, &src_ipaddr);
+    } else {
+        sip_config_get_nat_ipaddr(&src_ipaddr);
+        ipaddr2dotted(src_addr_str, &src_ipaddr);
+    }
+
+    /*
+     * Build the request
+     */
+    tflag = sippmh_add_request_line(request, SIP_METHOD_ACK, local_ReqURI,
+                                    SIP_VERSION);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Write Via */
+    if (prevcall) {
+        /*
+         * Use line 1 as default to get the transport type.
+         * Transport type is going to be device specific with
+         * the first release of skittles. Will need to change
+         * for mixed cc mode support.
+         */
+        line = 1;
+    } else {
+        line = ccb->dn_line;
+    }
+    snprintf(via, sizeof(via), "SIP/2.0/%s %s:%d;%s=%s",
+             sipTransportGetTransportType(line, TRUE, ccb),
+             src_addr_str, sipTransportGetListenPort(line, ccb),
+             VIA_BRANCH, via_branch);
+    tflag = sippmh_add_text_header(request, SIP_HEADER_VIA, via);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Write To, From, and Call-ID standard headers */
+    tflag = sippmh_add_text_header(request, SIP_HEADER_FROM, response_from);
+    UPDATE_FLAGS(flag, tflag);
+    tflag = sippmh_add_text_header(request, SIP_HEADER_TO, response_to);
+    UPDATE_FLAGS(flag, tflag);
+    tflag = sippmh_add_text_header(request, SIP_HEADER_CALLID, response_callid);
+    UPDATE_FLAGS(flag, tflag);
+
+    switch (status_code) {
+    case SIP_CLI_ERR_UNAUTH:
+    case SIP_CLI_ERR_PROXY_REQD:
+        if (response_cseq_method == sipMethodAck) {
+            authen.authorization = NULL;
+            cred_get_line_credentials(dn_line, &credentials,
+                                      sizeof(credentials.id),
+                                      sizeof(credentials.pw));
+            authenticate = sippmh_get_header_val(response,
+                                                 AUTH_HDR(status_code), NULL);
+            if (authenticate != NULL) {
+                sip_authen = sippmh_parse_authenticate(authenticate);
+                if (sip_authen) {
+                    if (sipSPIGenerateAuthorizationResponse(sip_authen,
+                                                            local_ReqURI,
+                                                            SIP_METHOD_ACK,
+                                                            credentials.id,
+                                                            credentials.pw,
+                                                            &author_str,
+                                                            &nc_count, NULL)) {
+                        authen.authorization = (char *)
+                            cpr_malloc(strlen(author_str) * sizeof(char) + 1);
+                        if (authen.authorization != NULL) {
+                            sstrncpy(authen.authorization, author_str,
+                                     strlen(author_str) * sizeof(char) + 1);
+                            authen.status_code = status_code;
+                        } else {
+                            CCSIP_DEBUG_ERROR("%s: Error: malloc() failed "
+                                              "for authen.authorization\n",
+                                              fname);
+                            bad_authentication = TRUE;
+                        }
+                        cpr_free(author_str);
+                    } else {
+                        CCSIP_DEBUG_ERROR("%s: Error: "
+                                          "sipSPIGenerateAuthorizationResponse()"
+                                          " returned null.\n", fname);
+                        bad_authentication = TRUE;
+                    }
+                    sippmh_free_authen(sip_authen);
+                } else {
+                    CCSIP_DEBUG_ERROR("%s: Error: sippmh_parse_authenticate() "
+                                      "returned null.\n", fname);
+                    bad_authentication = TRUE;
+                }
+            } else {
+                CCSIP_DEBUG_ERROR("%s: Error: sippmh_get_header_val(AUTH_HDR) "
+                                  "returned null.\n", fname);
+                bad_authentication = TRUE;
+            }
+
+            if (!bad_authentication) {
+                tflag = sippmh_add_text_header(request,
+                                               AUTHOR_HDR(status_code),
+                                               authen.authorization);
+                cpr_free(authen.authorization);
+                UPDATE_FLAGS(flag, tflag);
+            } else {
+                CCSIP_DEBUG_ERROR("%s: Error: Bad authentication header.\n",
+                                  fname);
+                if (request) {
+                    free_sip_message(request);
+                }
+                if (authen.authorization) {
+                    cpr_free(authen.authorization);
+                }
+                return;
+            }
+
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    /* Write Date header */
+    tflag = sipAddDateHeader(request);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Write CSeq */
+    tflag = sippmh_add_cseq(request, SIP_METHOD_ACK, cseq_number);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Add Route Header */
+    tflag = (sipSPIAddRouteHeaders(request, ccb, NULL, 0)) ?
+        STATUS_SUCCESS : STATUS_FAILURE;
+
+    /* Write Content-Length */
+    tflag = sippmh_add_int_header(request, SIP_HEADER_CONTENT_LENGTH, 0);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request) {
+            free_sip_message(request);
+        }
+        return;
+    }
+    /* Store the ACK so that it can be retransmitted in response
+     * to any duplicate 200 OK or error responses
+     */
+    if ((previous_call_line == 0xFF) && (prevcall == FALSE)) {
+        CCSIP_DEBUG_ERROR("%s: INFO: Skipping store for this Ack\n", fname);
+    } else {
+        reldel_stored_msg = sipRelDevCoupledMessageStore(request,
+                                                         response_callid,
+                                                         cseq_number,
+                                                         response_cseq_method,
+                                                         TRUE,
+                                                         status_code,
+                                                         &dest_ipaddr,
+                                                         (int16_t)dest_port,
+                                                         FALSE /*Do check tag*/);
+    }
+    /* Send message */
+    if (sipTransportChannelCreateSend(NULL, request, sipMethodAck,
+                                      &dest_ipaddr, (int16_t)dest_port, 0,
+                                      reldel_stored_msg) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipTransportChannelCreateSend()");
+        if (request)
+            free_sip_message(request);
+        return;
+    }
+
+    return;
+}
+
+
+/*
+ * Resend the last message sent
+ */
+boolean
+sipSPISendLastMessage (ccsipCCB_t *ccb)
+{
+    const char *fname = "sipSPISendLastMessage";
+
+    /* Args Check */
+    if (!ccb) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "ccb");
+        return (FALSE);
+    }
+
+
+    /* Send message */
+    if (ccb->index == 0) {
+        if (sipTransportSendMessage(ccb,
+                            sipPlatformUISMTimers[ccb->index].message_buffer,
+                            sipPlatformUISMTimers[ccb->index].message_buffer_len,
+                            sipPlatformUISMTimers[ccb->index].message_type,
+                            &(sipPlatformUISMTimers[ccb->index].ipaddr),
+                            sipPlatformUISMTimers[ccb->index].port,
+                            TRUE, TRUE, 0, NULL) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipTransportSendMessage()");
+            return (FALSE);
+        }
+    } else {
+        if (sipTransportChannelSend(ccb,
+                            sipPlatformUISMTimers[ccb->index].message_buffer,
+                            sipPlatformUISMTimers[ccb->index].message_buffer_len,
+                            sipPlatformUISMTimers[ccb->index].message_type,
+                            &(sipPlatformUISMTimers[ccb->index].ipaddr),
+                            sipPlatformUISMTimers[ccb->index].port, 0) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipTransportChannelSend()");
+            return (FALSE);
+        }
+    }
+    return (TRUE);
+}
+
+void
+sipGetRequestMethod (sipMessage_t *pRequest, sipMethod_t *pMethod)
+{
+    const char   *fname = "SIPGetRequestMethod";
+    sipReqLine_t *pReqLine = NULL;
+
+    *pMethod = sipMethodInvalid;
+    pReqLine = sippmh_get_request_line(pRequest);
+
+    if (!pReqLine) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_request_line()");
+        return;
+    }
+
+    if (pReqLine->method) {
+        *pMethod = sippmh_get_method_code(pReqLine->method);
+    } else {
+        CCSIP_DEBUG_ERROR("%s: Error: No recognizable method in Req-URI!\n",
+                          fname);
+    }
+
+    SIPPMH_FREE_REQUEST_LINE(pReqLine);
+    return;
+}
+
+
+int
+sipGetResponseMethod (sipMessage_t *pResponse, sipMethod_t *pMethod)
+{
+    const char    *fname = "SIPGetResponseMethod";
+    sipRespLine_t *pRespLine = NULL;
+    const char    *cseq = NULL;
+    sipCseq_t     *sipCseq = NULL;
+
+    pRespLine = sippmh_get_response_line(pResponse);
+    if (pRespLine) {
+        cseq = sippmh_get_cached_header_val(pResponse, CSEQ);
+        if (!cseq) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sippmh_get_cached_header_val(CSEQ)");
+            SIPPMH_FREE_RESPONSE_LINE(pRespLine);
+            return (-1);
+        }
+        /* Extract method code */
+        sipCseq = sippmh_parse_cseq(cseq);
+        if (!sipCseq) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sippmh_parse_cseq()");
+            SIPPMH_FREE_RESPONSE_LINE(pRespLine);
+            return (-1);
+        }
+        *pMethod = sipCseq->method;
+        cpr_free(sipCseq);
+    } else {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_response_line()");
+        return (-1);
+    }
+
+    SIPPMH_FREE_RESPONSE_LINE(pRespLine);
+    return (0);
+}
+
+
+int
+sipGetResponseCode (sipMessage_t *pResponse, int *pResponseCode)
+{
+    const char    *fname = "SIPGetResponseCode";
+    sipRespLine_t *pRespLine = NULL;
+
+    pRespLine = sippmh_get_response_line(pResponse);
+    if (pRespLine) {
+        *pResponseCode = pRespLine->status_code;
+    } else {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_response_line()");
+        return (-1);
+    }
+
+    SIPPMH_FREE_RESPONSE_LINE(pRespLine);
+    return (0);
+}
+
+
+/*
+ * Util workhorse to add the standard headers to all SIP messages.
+ * Does not affect call state.
+ */
+boolean
+sipSPIAddStdHeaders (sipMessage_t *msg, ccsipCCB_t *ccb, boolean isResponse)
+{
+    boolean retval = FALSE;
+    boolean flip = TRUE;
+    int     max_fwd = 0;
+
+    if (!(ccb && msg)) {
+        return (retval);
+    }
+
+    if (isResponse) {
+        if (ccb->flags & INCOMING) {
+            flip = FALSE;
+        } else {
+            flip = TRUE;
+        }
+    } else {
+        if (ccb->flags & INCOMING) {
+            flip = TRUE;
+        } else {
+            flip = FALSE;
+        }
+    }
+
+    if (!flip) {
+        retval = (boolean)
+            ((STATUS_SUCCESS == sippmh_add_text_header(msg,
+                                                       SIP_HEADER_FROM,
+                                                       ccb->sip_from)) &&
+             (STATUS_SUCCESS == sippmh_add_text_header(msg,
+                                                       SIP_HEADER_TO,
+                                                       ccb->sip_to)) &&
+             (STATUS_SUCCESS == sippmh_add_text_header(msg,
+                                                       SIP_HEADER_CALLID,
+                                                       ccb->sipCallID)));
+    } else {
+        retval = (boolean)
+            ((STATUS_SUCCESS == sippmh_add_text_header(msg,
+                                                       SIP_HEADER_FROM,
+                                                       ccb->sip_to)) &&
+             (STATUS_SUCCESS == sippmh_add_text_header(msg,
+                                                       SIP_HEADER_TO,
+                                                       ccb->sip_from)) &&
+             (STATUS_SUCCESS == sippmh_add_text_header(msg,
+                                                       SIP_HEADER_CALLID,
+                                                       ccb->sipCallID)));
+    }
+
+    // Add max-forwards header if this is a request
+    if (isResponse == FALSE && retval == TRUE) {
+        config_get_value(CFGID_SIP_MAX_FORWARDS, &max_fwd, sizeof(max_fwd));
+        if (max_fwd == 0) {
+            max_fwd = SIP_MAX_FORWARDS_DEFAULT_VALUE;
+        }
+        if (sippmh_add_int_header(msg, SIP_HEADER_MAX_FORWARDS, max_fwd)
+                != STATUS_SUCCESS) {
+            retval = FALSE;
+        }
+    }
+    return (retval);
+}
+
+
+/*
+ * Add a SIP Via header to the sipMessage_t.
+ * Should return (TRUE) if the right members are
+ * present in the CCB struct.
+ * Does not affect call state.
+ */
+boolean
+sipSPIAddLocalVia (sipMessage_t *msg, ccsipCCB_t *ccb, sipMethod_t method)
+{
+    const char *fname = "sipSPIAddLocalVia";
+
+    if (msg && ccb) {
+        if (util_check_if_ip_valid(&(ccb->src_addr))) {
+            static char via[SIP_MAX_VIA_LENGTH];
+            char src_addr_str[MAX_IPADDR_STR_LEN];
+            char  *sip_via_branch;
+            int16_t trx_index = -1;
+
+            /*
+             * We don't allocate a block for ACK(200 OK) although
+             * it is technically another transaction.  Instead we
+             * locate the INVITE transaction in ccb.
+             */
+            if (method == sipMethodAck) {
+                trx_index = get_method_request_trx_index(ccb, sipMethodInvite,
+                                                         TRUE);
+            } else {
+                trx_index = get_last_request_trx_index(ccb, TRUE);
+            }
+
+            if (trx_index < 0) {
+                return FALSE;
+            }
+            ipaddr2dotted(src_addr_str, &ccb->src_addr);
+            if (method == sipMethodCancel) {
+                // Get the via header from last sent request transaction
+                if (trx_index < 1) {
+                    return FALSE;
+                }
+                sip_via_branch = strlib_open(ccb->sent_request[trx_index].u.sip_via_branch,
+                                             VIA_BRANCH_LENGTH);
+                sstrncpy(sip_via_branch, (char *)(ccb->sent_request[trx_index - 1].u.sip_via_branch), VIA_BRANCH_LENGTH);
+                ccb->sent_request[trx_index].u.sip_via_branch = strlib_close(sip_via_branch);
+
+                snprintf(via, sizeof(via),
+                         "SIP/2.0/%s %s:%d;%s=%s",
+                         sipTransportGetTransportType(ccb->dn_line, TRUE, ccb),
+                         src_addr_str, ccb->local_port, VIA_BRANCH,
+                         (char *)(ccb->sent_request[trx_index].u.sip_via_branch));
+
+            } else {
+                snprintf(via, sizeof(via),
+                         "SIP/2.0/%s %s:%d;%s=",
+                         sipTransportGetTransportType(ccb->dn_line, TRUE, ccb),
+                         src_addr_str, ccb->local_port, VIA_BRANCH);
+
+                sip_via_branch = strlib_open(ccb->sent_request[trx_index].u.sip_via_branch,
+                                             VIA_BRANCH_LENGTH);
+                if (sip_via_branch) {
+                    snprintf(sip_via_branch, VIA_BRANCH_LENGTH,
+                             "%s%.8x", VIA_BRANCH_START,
+                             (unsigned int)cpr_rand());
+                }
+                ccb->sent_request[trx_index].u.sip_via_branch =
+                    strlib_close(sip_via_branch);
+
+                if (sip_via_branch) {
+                    sstrncat(via, sip_via_branch,
+                            SIP_MAX_VIA_LENGTH - strlen(via));
+                }
+            }
+            if (sippmh_add_text_header(msg, SIP_HEADER_VIA, via)
+                    != STATUS_SUCCESS) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "sippmh_add_text_header(VIA)");
+                return (FALSE);
+            }
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                              fname, "ccb->src_addr");
+            return (FALSE);
+        }
+    }
+
+    return (TRUE);
+}
+
+
+/*
+ * For a response, add the Via headers that came in with the
+ * request to the response.
+ */
+boolean
+sipSPIAddRequestVia (ccsipCCB_t *ccb, sipMessage_t *response,
+                    sipMessage_t *request, sipMethod_t method)
+{
+    const char *fname = "sipSPIAddRequestVia";
+    int16_t     trx_index = -1;
+
+    /* Check args */
+    if (!response) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "response");
+        return (FALSE);
+    }
+    if (!request) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "request");
+        return (FALSE);
+    }
+
+    // Get the stored via from the transaction that we are responding to
+    if (ccb) {
+        trx_index = get_method_request_trx_index(ccb, method, FALSE);
+        if (trx_index >= 0) {
+            (void) sippmh_add_text_header(response, SIP_HEADER_VIA,
+                                          (char *)(ccb->recv_request[trx_index].u.sip_via_header));
+            return (TRUE);
+        }
+    }
+
+    // If no transaction, get the via directly from the request
+    (void) sippmh_add_text_header(response, SIP_HEADER_VIA,
+                                  sippmh_get_cached_header_val(request, VIA));
+
+    return (TRUE);
+}
+
+
+/*
+ * Procedure to add a Route/Record-Route header in SIP responses
+ */
+boolean
+sipSPIAddRouteHeaders (sipMessage_t *msg, ccsipCCB_t *ccb,
+                       char *result_route, int result_route_length)
+{
+    const char *fname = "SIPSPIAddRouteHeaders";
+    /* NOTE: route will be limited to 4 hops */
+    static char route[MAX_SIP_HEADER_LENGTH * NUM_INITIAL_RECORD_ROUTE_BUFS];
+    static char Contact[MAX_SIP_HEADER_LENGTH];
+    boolean     lr = FALSE;
+
+    /* Check args */
+    if (!msg) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "msg");
+        return (FALSE);
+    }
+    if (!ccb) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "ccb");
+        return (FALSE);
+    }
+
+    if (!ccb->record_route_info) {
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Route info not available; will not"
+                            " add Route header.\n", DEB_F_PREFIX_ARGS(SIP_ROUTE, fname));
+        return (TRUE);
+    }
+
+    memset(route, 0, MAX_SIP_HEADER_LENGTH * NUM_INITIAL_RECORD_ROUTE_BUFS);
+    memset(Contact, 0, MAX_SIP_HEADER_LENGTH);
+
+    if (ccb->flags & INCOMING) {
+        /*
+         * For Incoming call (UAS), Copy the RR headers as it is
+         * If Contact is present, append it at the end
+         */
+        if (sipSPIGenerateRouteHeaderUAS(ccb->record_route_info, route,
+                    sizeof(route), &lr) == FALSE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPIGenerateRouteHeaderUAS()");
+            return (FALSE);
+        }
+    } else {
+        /*
+         * For Outgoing call (UAC), Copy the RR headers in the reverse
+         * order. If Contact is present, append it at the end
+         */
+        if (sipSPIGenerateRouteHeaderUAC(ccb->record_route_info, route,
+                    sizeof(route), &lr) == FALSE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPIGenerateRouteHeaderUAC()");
+            return (FALSE);
+        }
+    }
+    /*
+     * If loose_routing is TRUE, then the contact header is NOT appended
+     * to the Routeset but is instead used in the Req-URI
+     */
+    if (!lr) {
+        Contact[0] = '\0';
+        if (sipSPIGenerateContactHeader(ccb->contact_info, Contact,
+                    sizeof(Contact)) == FALSE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPIGenerateContactHeader()");
+            return (FALSE);
+        }
+
+        /* Append Contact to the Route Header, if Contact is available */
+        if (Contact[0] != '\0') {
+            if (route[0] != '\0') {
+                sstrncat(route, ", ", sizeof(route) - strlen(route));
+            }
+            sstrncat(route, Contact, sizeof(route) - strlen(route));
+        }
+    }
+
+    if (route[0] != '\0') {
+        if (sippmh_add_text_header(msg, SIP_HEADER_ROUTE, route)
+                == STATUS_SUCCESS) {
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Adding route = %s\n",
+                DEB_F_PREFIX_ARGS(SIP_ROUTE, fname), route);
+            if (result_route) {
+                sstrncpy(result_route, route, result_route_length);
+            }
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sippmh_add_text_header(ROUTE)");
+            return (FALSE);
+        }
+    } else {
+        /* Having nothing in Route header is a legal case.
+         * This would happen when the Record-Route header has
+         * a single entry and Contact was NULL
+         */
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Not adding route \n", DEB_F_PREFIX_ARGS(SIP_ROUTE, fname));
+    }
+
+    return (TRUE);
+}
+
+
+boolean
+sipSPIAddRequestRecordRoute (sipMessage_t *response, sipMessage_t *request)
+{
+    const char *fname = "SIPSPIAddRequestRecordRoute";
+
+    /* Check args */
+    if (!response) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "response");
+        return (FALSE);
+    }
+    if (!request) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "request");
+        return (FALSE);
+    }
+
+    (void) sippmh_add_text_header(response, SIP_HEADER_RECORD_ROUTE,
+                                  sippmh_get_cached_header_val(request, RECORD_ROUTE));
+
+    return (TRUE);
+}
+
+
+/*
+ * Procedure to add a proprietary SIP header to carry GUID in the SIP messsage
+ */
+boolean
+sipSPIAddCiscoGuid (sipMessage_t *msg, ccsipCCB_t *ccb)
+{
+    boolean retval = STATUS_FAILURE;
+/*
+    const char *fname = "sipSPIAddCiscoGuid";
+    uint32_t guid[4];
+    char guid_char[4*CC_GUID_SIZE];
+
+    if (ccb && msg) {
+
+        memcpy(&guid[0], ccb->guid, sizeof(guid[0]));
+        memcpy(&guid[1], &ccb->guid[sizeof(guid[0])], sizeof(guid[0]));
+        memcpy(&guid[2], &ccb->guid[2*sizeof(guid[0])], sizeof(guid[0]));
+        memcpy(&guid[3], &ccb->guid[3*sizeof(guid[0])], sizeof(guid[0]));
+
+        sprintf(guid_char, "%u-%u-%u-%u", guid[0], guid[1], guid[2], guid[3]);
+
+        retval = (STATUS_SUCCESS == sippmh_add_text_header(msg,
+                                                      SIP_HEADER_CISCO_GUID,
+                                                      guid_char));
+    } else {
+        CCSIP_DEBUG_ERROR("%s: Error: Fatal error in parsing ccb.\n",
+                          fname);
+    }
+*/
+    return (retval);
+}
+
+int
+sipSPICheckContentHeaders (sipMessage_t *msg)
+{
+    uint8_t     i;
+    const char *accept_hdr = NULL;
+    const char *content_enc = NULL;
+    const char *content_disp_str = NULL;
+    const char *accepted_enc_str = NULL;
+    cc_content_disposition_t *content_disp = NULL;
+    const char *fname = "sipSPICheckContentHeaders";
+    sipMethod_t method = sipMethodInvalid;
+    char *lasts = NULL;
+
+    if (!msg) {
+        return (SIP_MESSAGING_ERROR);
+    }
+
+    if (sippmh_msg_header_present(msg, SIP_HEADER_ACCEPT)) {
+        accept_hdr = sippmh_get_header_val(msg, SIP_HEADER_ACCEPT, NULL);
+        if (!accept_hdr) {
+            if (sippmh_is_request(msg)) {
+                sipGetRequestMethod(msg, &method);
+                if (method == sipMethodInvite) {
+                    /*
+                     * Currently we are rejecting empty Accept headers for
+                     * INVITE only.  This check is done here instead of
+                     * previously because Accept is Content related header
+                     * and also in the future if we want to extend this
+                     * check to other requests and responses this would be
+                     * a good place
+                     */
+                    return (SIP_MESSAGING_NOT_ACCEPTABLE);
+                }
+            }
+        }
+    }
+    content_enc = sippmh_get_header_val(msg, SIP_HEADER_CONTENT_ENCODING,
+                                        SIP_C_HEADER_CONTENT_ENCODING);
+    content_disp_str = sippmh_get_header_val(msg, SIP_HEADER_CONTENT_DISP,
+                                             SIP_HEADER_CONTENT_DISP);
+    accepted_enc_str = sippmh_get_header_val(msg, SIP_HEADER_ACCEPT_ENCODING,
+                                             SIP_HEADER_ACCEPT_ENCODING);
+    if (content_disp_str) {
+        content_disp = sippmh_parse_content_disposition(content_disp_str);
+    }
+
+    // Check Content-Encoding
+    // Only identity encoding is supported at this time
+    if (content_enc) {
+        if (cpr_strcasecmp(content_enc, SIP_CONTENT_ENCODING_IDENTITY)) {
+            // If Content-Encoding is not understood, check to see if
+            // the Content-Disposition is required. If so, flag an error
+            if (content_disp) {
+                if (content_disp->required_handling) {
+                    cpr_free(content_disp);
+                    return (SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA);
+                }
+            } else {
+                return (SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA);
+            }
+        }
+    }
+
+    // Check Content-Disposition
+    // Only disposition of "session", if not reject if handling it
+    // content is required
+    if (content_disp) {
+        if (content_disp->disposition != cc_disposition_session) {
+            if (content_disp->required_handling) {
+                cpr_free(content_disp);
+                return (SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA);
+            }
+        }
+    }
+    if (content_disp) {
+        cpr_free(content_disp);
+    }
+
+    if (accepted_enc_str) {
+        //parse the accepted_enc_str
+        //check to see if it contains "identity"
+        //if it does not contain "identity" return error
+
+        boolean found = FALSE;
+        char *ptr = NULL;
+        char *accepted_enc_str_dup;
+
+        accepted_enc_str_dup = cpr_strdup(accepted_enc_str);
+        if (accepted_enc_str_dup == NULL) {
+            CCSIP_DEBUG_ERROR("%s: Error: cpr_strdup() failed "
+                              "for accepted_enc_str_dup\n", fname);
+            return (SIP_SERV_ERR_INTERNAL);
+        }
+
+        ptr = PL_strtok_r(accepted_enc_str_dup, ", ", &lasts);
+
+        while (ptr) {
+            if (strcmp(ptr, SIP_CONTENT_ENCODING_IDENTITY) == 0) {
+                found = TRUE;
+                break;
+            }
+            ptr = PL_strtok_r(NULL, ", ", &lasts);
+        }
+
+        cpr_free(accepted_enc_str_dup);
+
+        if (found == FALSE) {
+            return (SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA);
+        }
+    }
+
+
+    // Check all the body parts
+    for (i = 0; i < HTTPISH_MAX_BODY_PARTS; i++) {
+        if (msg->mesg_body[i].msgBody) {
+            if (msg->mesg_body[i].msgContentTypeValue
+                    == SIP_CONTENT_TYPE_UNKNOWN_VALUE) {
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Pass-through \"%s\"\n",
+                                    DEB_F_PREFIX_ARGS(SIP_CONTENT_TYPE, fname),
+                                    msg->mesg_body[i].msgContentType);
+                // return (SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA);
+            }
+            if ((msg->mesg_body[i].msgContentEnc
+                        != SIP_CONTENT_ENCODING_IDENTITY_VALUE) &&
+                (msg->mesg_body[i].msgRequiredHandling == TRUE)) {
+                return (SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA);
+            }
+            if ((msg->mesg_body[i].msgContentDisp
+                        != SIP_CONTENT_DISPOSITION_SESSION_VALUE) &&
+                (msg->mesg_body[i].msgRequiredHandling == TRUE)) {
+                return (SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA);
+            }
+        }
+    }
+
+    return (SIP_MESSAGING_OK);
+}
+
+// This function returns TRUE if the header length is acceptable
+boolean
+is_good_header_length (const char *header, uint8_t header_type)
+{
+
+    if (!header) {
+        return FALSE;
+    }
+    switch (header_type) {
+    case FROM:
+    case TO:
+        if ((strlen(header) >= MAX_SIP_URL_LENGTH) || (strlen(header) == 0)) {
+            return FALSE;
+        }
+        break;
+    case CALLID:
+        if ((strlen(header) >= MAX_SIP_CALL_ID) || (strlen(header) == 0)) {
+            return FALSE;
+        }
+        break;
+    default:
+        break;
+    }
+    return TRUE;
+}
+
+/*
+ * sipCheckRequestURI
+ *
+ * Check the validity of mandatory fields in Req URI
+ *
+ * Adding restrictive checks to the following will break
+ * ccm features: "user=phone"
+ *
+ */
+int
+sipCheckRequestURI (ccsipCCB_t *ccb, sipMessage_t *request)
+{
+    sipReqLine_t   *requestURI = NULL;
+    genUrl_t       *genUrl = NULL;
+    sipUrl_t       *sipUriUrl = NULL;
+    char           src_addr_str[MAX_IPADDR_STR_LEN];
+    char           *pUser = NULL;
+    cpr_ip_addr_t  src_addr;
+    int            nat_enable = 0;
+    boolean        request_uri_error = FALSE;
+    int            errorCode = SIP_MESSAGING_ERROR;
+    cpr_ip_addr_t  ipaddr;
+
+    CPR_IP_ADDR_INIT(src_addr);
+    CPR_IP_ADDR_INIT(ipaddr);
+
+    requestURI = sippmh_get_request_line(request);
+    if (requestURI) {
+        if (requestURI->url) {
+            genUrl = sippmh_parse_url(requestURI->url, TRUE);
+            if (genUrl) {
+                if (genUrl->schema == URL_TYPE_SIP) {
+                    sipUriUrl = genUrl->u.sipUrl;
+                }
+                if (sipUriUrl) {
+                    pUser = sippmh_parse_user(sipUriUrl->user);
+                    if (pUser) {
+                        if (sipUriUrl->host) {
+                            if (!str2ip(sipUriUrl->host, &ipaddr)) {
+                                config_get_value(CFGID_NAT_ENABLE, &nat_enable,
+                                                 sizeof(nat_enable));
+                                if (nat_enable == 0) {
+                                    sip_config_get_net_device_ipaddr(&src_addr);
+                                } else {
+                                    sip_config_get_nat_ipaddr(&src_addr);
+                                }
+                                ipaddr2dotted(src_addr_str, &src_addr);
+                                if (strcmp(sipUriUrl->host, src_addr_str)) {
+                                    if (!validateHostName(sipUriUrl->host, pUser)) {
+                                        CCSIP_DEBUG_ERROR("Unknown address in Request URI\n");
+                                        request_uri_error = TRUE;
+                                        errorCode = SIP_MESSAGING_ENDPOINT_NOT_FOUND;
+                                    }
+                                }
+                            } else {
+                                if (!validateHostName(sipUriUrl->host, pUser)) {
+                                    CCSIP_DEBUG_ERROR("Unknown address in Request URI\n");
+                                    request_uri_error = TRUE;
+                                    errorCode = SIP_MESSAGING_ENDPOINT_NOT_FOUND;
+                                }
+                            }
+                            if (sipUriUrl->port_present) {
+                                if (ccb &&
+                                    cpr_strcasecmp(sipTransportGetTransportType(ccb->dn_line, FALSE, ccb), "udp") == 0) {
+                                    if (sipUriUrl->port != ccb->local_port) {
+                                        CCSIP_DEBUG_ERROR("Port Mismatch(UDP), URL Port: %d, Port Used: %d\n",
+                                             sipUriUrl->port, ccb->local_port);
+                                        request_uri_error = TRUE;
+                                        errorCode = SIP_MESSAGING_ENDPOINT_NOT_FOUND;
+                                    }
+                                }
+                            }
+                        }
+                        if (pUser[0] == '\0') {
+                            request_uri_error = TRUE;
+                            errorCode = SIP_MESSAGING_ENDPOINT_NOT_FOUND;
+                        } else {
+                            if (ccb && !request_uri_error) {
+                                sstrncpy(ccb->ReqURI, pUser,
+                                         sizeof(ccb->ReqURI));
+                            }
+                        }
+                        cpr_free(pUser);
+                    }
+                }
+                sippmh_genurl_free(genUrl);
+            } else {
+                request_uri_error = TRUE;
+                errorCode = SIP_MESSAGING_ENDPOINT_NOT_FOUND;
+            }
+        } else {
+            request_uri_error = TRUE;
+            errorCode = SIP_MESSAGING_ERROR;
+        }
+        SIPPMH_FREE_REQUEST_LINE(requestURI);
+    } else {
+        request_uri_error = TRUE;
+        errorCode = SIP_MESSAGING_ERROR;
+    }
+
+    if (request_uri_error) {
+        return errorCode;
+    } else {
+        return (SIP_MESSAGING_OK);
+    }
+}
+
+/*
+ * This is called for every request received.
+ * It checks the basic fields(url, From, To).
+ * Does not affect call state.
+ * It sends error responses for generic error types.
+ */
+int
+sipSPICheckRequest (ccsipCCB_t *ccb, sipMessage_t *request)
+{
+    const char   *fname = "sipSPICheckRequest";
+    const char   *callID = NULL;
+    const char   *via = NULL;
+    const char   *from = NULL;
+    const char   *to = NULL;
+    const char   *contact = NULL;
+    int           retval = SIP_MESSAGING_OK;
+    char         *replaceshdr = NULL;
+
+    const char   *request_cseq = NULL;
+    sipCseq_t    *request_cseq_structure = NULL;
+    uint32_t      request_cseq_number = 0;
+    sipMethod_t   request_cseq_method = sipMethodInvalid;
+    sipReqLine_t *requestURI;
+
+    /*
+     * Check args - CCB may be NULL
+     */
+    if (!request) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "request");
+        return (SIP_MESSAGING_ERROR);
+    }
+
+    /*
+     * Check for the incomplete message body
+     */
+    if (!sippmh_is_message_complete(request)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_is_message_complete()");
+        return (SIP_MESSAGING_ERROR);
+    }
+
+
+    if ((retval = sipCheckRequestURI(ccb, request)) != SIP_MESSAGING_OK) {
+        CCSIP_DEBUG_ERROR("%s: Request URI Not Found\n", fname);
+        return (retval);
+    }
+
+
+    /*
+     * Check whether all mandatory SIP request headers are there.
+     * Also check if their lengths are within an acceptable range.
+     */
+    from = sippmh_get_cached_header_val(request, FROM);
+    if (!from || (!is_good_header_length(from, FROM))) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(FROM)");
+        return (SIP_MESSAGING_ERROR);
+    }
+
+    to = sippmh_get_cached_header_val(request, TO);
+    if (!to || (!is_good_header_length(to, TO))) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(TO)");
+        return (SIP_MESSAGING_ERROR);
+    }
+
+    via = sippmh_get_cached_header_val(request, VIA);
+    if (!via) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(VIA)");
+        return (SIP_MESSAGING_ERROR);
+    }
+
+    callID = sippmh_get_cached_header_val(request, CALLID);
+    if (!callID || (!is_good_header_length(callID, CALLID))) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(CALLID)");
+        return (SIP_MESSAGING_ERROR);
+    }
+
+    contact = sippmh_get_cached_header_val(request, CONTACT);
+    if (contact) {
+        int contact_check_result = 0;
+
+        contact_check_result = sipSPICheckContact(contact);
+        if (contact_check_result < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPICheckContact()");
+            return contact_check_result;
+        }
+    }
+
+    if ((retval = sipSPICheckContentHeaders(request)) != SIP_MESSAGING_OK) {
+        CCSIP_DEBUG_ERROR("%s: Content header value not supported\n", fname);
+        return (retval);
+    }
+
+    /*
+     * Check whether the current call id and this request's CallId match
+     */
+    if (ccb && ccb->sipCallID[0]) {
+        if (strcmp(ccb->sipCallID, callID) != 0) {
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Call ID match: new call id.\n", DEB_F_PREFIX_ARGS(SIP_CALL_ID, fname));
+            retval = SIP_MESSAGING_NEW_CALLID;
+        } else {
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Call ID match: same call id.\n", DEB_F_PREFIX_ARGS(SIP_CALL_ID, fname));
+        }
+    }
+
+    /*
+     * Parse CSeq
+     */
+    request_cseq = sippmh_get_cached_header_val(request, CSEQ);
+    if (!request_cseq) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(CSEQ)");
+        return (SIP_MESSAGING_ERROR);
+    }
+
+    request_cseq_structure = sippmh_parse_cseq(request_cseq);
+    if (!request_cseq_structure) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_parse_cseq()");
+        return (SIP_MESSAGING_ERROR);
+    }
+    request_cseq_number = request_cseq_structure->number;
+    request_cseq_method = request_cseq_structure->method;
+    cpr_free(request_cseq_structure);
+
+    // Check continuity of this request wrt CSeq number and method
+    if (request_cseq_method != sipMethodAck &&
+        request_cseq_method != sipMethodCancel) {
+        // If ccb is present, check if the CSeq number is acceptable
+        if (ccb) {
+            if (request_cseq_number < ccb->last_recv_request_cseq) {
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Inconsistent CSEQ number. "
+                                    "current= %d, last= %d.\n",
+                                    DEB_F_PREFIX_ARGS(SIP_CSEQ, fname), request_cseq_number,
+                                    ccb->last_recv_request_cseq);
+
+                return (SIP_CLI_ERR_BAD_REQ);
+            }
+            if (request_cseq_number == ccb->last_recv_request_cseq) {
+                if (request_cseq_method != ccb->last_recv_request_cseq_method) {
+                    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Inconsistent CSEQ method. "
+                                        "current= %s, last= %s\n.",
+                                        DEB_F_PREFIX_ARGS(SIP_CSEQ, fname),
+                                        sipGetMethodString(request_cseq_method),
+                                        sipGetMethodString(ccb->last_recv_request_cseq_method));
+
+                    return (SIP_CLI_ERR_BAD_REQ);
+                }
+            }
+        }
+    }
+
+    if (request->mesg_line) {
+        if (strlen(request->mesg_line) >= MAX_SIP_URL_LENGTH) {
+            CCSIP_DEBUG_ERROR("%s: Request URI length exceeds acceptable value\n",
+                              fname);
+            return (SIP_MESSAGING_ERROR);
+        }
+    }
+
+    requestURI = sippmh_get_request_line(request);
+    if (requestURI) {
+        if (sippmh_get_method_code(requestURI->method) != request_cseq_method) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "Method in RURI != method in CSeq");
+            SIPPMH_FREE_REQUEST_LINE(requestURI);
+            return (SIP_MESSAGING_ERROR);
+        }
+        SIPPMH_FREE_REQUEST_LINE(requestURI);
+    }
+
+    if (request_cseq_method != sipMethodInvite) {
+        if (sippmh_msg_header_present(request, SIP_HEADER_REPLACES)) {
+            CCSIP_DEBUG_ERROR("%s: Error: Replace header is not valid for "
+                              "this request\n", fname);
+            return SIP_MESSAGING_ERROR;
+        }
+    } else {
+        if (sippmh_get_num_particular_headers(request, SIP_HEADER_REPLACES,
+                    NULL, &replaceshdr, MAX_REPLACES_HEADERS + 1)
+                    > MAX_REPLACES_HEADERS) {
+            CCSIP_DEBUG_ERROR("%s: Error: More than one replaces header "
+                              "in this request\n", fname);
+            return SIP_MESSAGING_ERROR;
+        }
+    }
+
+    /*
+     * Reliable Delivery
+     */
+    if (SipRelDevEnabled) {
+        /*
+         * responseRecord is a static because the structure it is made out of is
+         * so large.
+         */
+        static sipRelDevMessageRecord_t requestRecord;
+        int            handle = -1;
+        const char    *reldev_to = NULL;
+        sipLocation_t *reldev_to_loc = NULL;
+        char           reldev_to_tag[MAX_SIP_TAG_LENGTH];
+        const char    *reldev_from = NULL;
+        sipLocation_t *reldev_from_loc = NULL;
+        char           reldev_from_tag[MAX_SIP_TAG_LENGTH];
+
+        memset(&requestRecord, 0, sizeof(requestRecord));
+        memset(reldev_to_tag, 0, MAX_SIP_TAG_LENGTH);
+        memset(reldev_from_tag, 0, MAX_SIP_TAG_LENGTH);
+
+        /* Get to_tag */
+        reldev_to = sippmh_get_cached_header_val(request, TO);
+
+        if (reldev_to) {
+            reldev_to_loc = sippmh_parse_from_or_to((char *)reldev_to, TRUE);
+            if (reldev_to_loc) {
+                if (reldev_to_loc->genUrl->schema != URL_TYPE_SIP) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR),
+                                      fname);
+                    sippmh_free_location(reldev_to_loc);
+                    return (SIP_CLI_ERR_FORBIDDEN);
+                }
+
+                if (reldev_to_loc->tag) {
+                    sstrncpy(reldev_to_tag,
+                             sip_sm_purify_tag(reldev_to_loc->tag),
+                             MAX_SIP_TAG_LENGTH);
+                }
+                sstrncpy(requestRecord.to_user,
+                         reldev_to_loc->genUrl->u.sipUrl->user,
+                         RELDEV_MAX_USER_NAME_LEN);
+                sippmh_free_location(reldev_to_loc);
+
+            } else {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname,
+                                  get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO));
+                return (SIP_MESSAGING_ERROR);
+            }
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sippmh_get_cached_header_val(TO)");
+            return (SIP_MESSAGING_ERROR);
+        }
+
+        /* Store from_user and from_host */
+        reldev_from = sippmh_get_cached_header_val(request, FROM);
+        if (reldev_from) {
+            reldev_from_loc = sippmh_parse_from_or_to((char *)reldev_from, TRUE);
+            if (reldev_from_loc) {
+                sstrncpy(requestRecord.from_user,
+                         reldev_from_loc->genUrl->u.sipUrl->user,
+                         RELDEV_MAX_USER_NAME_LEN);
+                sstrncpy(requestRecord.from_host,
+                         reldev_from_loc->genUrl->u.sipUrl->host,
+                         RELDEV_MAX_HOST_NAME_LEN);
+                if (reldev_from_loc->tag) {
+                    sstrncpy(reldev_from_tag,
+                             sip_sm_purify_tag(reldev_from_loc->tag),
+                             MAX_SIP_TAG_LENGTH);
+                }
+                sippmh_free_location(reldev_from_loc);
+            }
+        }
+
+        /* Check whether the tag matches the stored tag */
+        if (ccb) {
+            if ((request_cseq_method == sipMethodInvite) ||
+                (request_cseq_method == sipMethodAck) ||
+                (request_cseq_method == sipMethodBye)) {
+
+                boolean         to_tag_match = TRUE;
+                boolean         from_tag_match = TRUE;
+
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"in_to_tag:<%s>, in_from_tag:<%s>, "
+                                    "stored to tag=<%s>, stored from tag=<%s>\n",
+                                    DEB_F_PREFIX_ARGS(SIP_TAG, fname), reldev_to_tag, reldev_from_tag,
+                                    ccb->sip_to_tag, ccb->sip_from_tag);
+
+                /* Check to_tag first */
+                if (ccb->sip_to_tag[0]) {
+                    if ((request_cseq_method == sipMethodBye) &&
+                        (reldev_to_tag[0] == '\0') &&
+                        (SIP_SM_CALL_SETUP_NOT_COMPLETED(ccb))) {
+                        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Allow early call termination "
+                                            "BYE.\n", DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+                    } else if ((request_cseq_method == sipMethodInvite) &&
+                               (reldev_to_tag[0] == '\0') &&
+                               (SIP_SM_CALL_SETUP_NOT_COMPLETED(ccb))) {
+                        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Allow crossed "
+                                            "response and INVITE.\n", DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname));
+                    } else {
+                        if (ccb->flags & INCOMING) {
+                            // Incoming request for an incoming call
+                            // Match the stored to-tag with the to-tag in request
+                            if (strcasecmp_ignorewhitespace(reldev_to_tag,
+                                        ccb->sip_to_tag) != 0) {
+                                to_tag_match = FALSE;
+                            }
+                        } else {
+                            // We made the call, so match the stored to-tag
+                            // with the from-tag in the incoming request
+                            if (strcasecmp_ignorewhitespace(reldev_from_tag,
+                                        ccb->sip_to_tag) != 0) {
+                                to_tag_match = FALSE;
+                            }
+                        }
+                    }
+                }
+                if (!to_tag_match) {
+                    CCSIP_DEBUG_ERROR("%s: To-Tag mismatch detected!\n", fname);
+                    return (SIP_CLI_ERR_CALLEG);
+                }
+                // Now check from-tag
+                if (ccb->sip_from_tag[0]) {
+                    if (ccb->flags & INCOMING) {
+                        // Incoming request for an incoming call
+                        // Match the stored from-tag with the from-tag in request
+                        if (strcasecmp_ignorewhitespace(reldev_from_tag,
+                                    ccb->sip_from_tag) != 0) {
+                            from_tag_match = FALSE;
+                        }
+                    } else {
+                        // We made the call, so match the stored from-tag
+                        // with the to-tag in the incoming request
+                        if (strcasecmp_ignorewhitespace(reldev_to_tag,
+                                    ccb->sip_from_tag) != 0) {
+                            from_tag_match = FALSE;
+                        }
+                    }
+                    if (!from_tag_match) {
+                        CCSIP_DEBUG_ERROR("%s: From-Tag mismatch detected!\n",
+                                          fname);
+                        return (SIP_CLI_ERR_CALLEG);
+                    }
+                }
+            }
+        }
+
+        requestRecord.is_request = TRUE;
+        sstrncpy(requestRecord.call_id, (callID) ? callID : "",
+                 MAX_SIP_CALL_ID);
+        requestRecord.cseq_number = request_cseq_number;
+        requestRecord.cseq_method = request_cseq_method;
+        sstrncpy(requestRecord.tag, reldev_to_tag, MAX_SIP_TAG_LENGTH);
+        if (ccb) {
+            //requestRecord.line = ccb->index;
+        } else {
+            //requestRecord.line = 1;
+        }
+        if (ccb && (requestRecord.cseq_method == sipMethodInvite) &&
+            (ccb->state == SIP_STATE_IDLE)) {
+            sipRelDevMessagesClear(requestRecord.call_id,
+                                   requestRecord.from_user,
+                                   requestRecord.from_host,
+                                   requestRecord.to_user);
+        }
+        /* Check if duplicate */
+        if (sipRelDevMessageIsDuplicate(&requestRecord, &handle)) {
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Duplicate request detected...\n", DEB_F_PREFIX_ARGS(SIP_RESP, fname));
+            // If the request is an ACK we do not have any thing to send
+            // This was retransmitted ACK so drop it.
+            if (requestRecord.cseq_method != sipMethodAck) {
+                if (sipRelDevCoupledMessageSend(handle) < 0) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                      fname, "sipRelDevCoupledMessageSend()");
+                }
+            }
+            return (SIP_MESSAGING_DUPLICATE);
+        }
+
+        /* Record the request in Recent Requests List */
+        sipRelDevMessageStore(&requestRecord);
+    }
+    return (retval);
+}
+
+
+/*
+ * This is called for every response received.
+ * It checks the basic fields(response line, From, To).
+ * If a session description is present, it reads and parses it.
+ * (and overwrites an old one if present in the CCB.)
+ * Does not affect call state.
+ * Returns TRUE if the request is valid.
+ * Usually, but not necessarily an action function will drop
+ * an invalid response. Exceptions are usually disconnection states,
+ * where  a bad response may still be accepted.
+ *
+ * NOTE:
+ *  It is assumed that this routine would never be called recursively or
+ *  reentered via a separate task because responseRecord is declared as a
+ *  large static variable
+ */
+int
+sipSPICheckResponse (ccsipCCB_t *ccb, sipMessage_t *response)
+{
+    const char    *fname = "sipSPICheckResponse";
+    const char    *from = NULL;
+    const char    *to = NULL;
+    const char    *callID = NULL;
+    const char    *cseq = NULL;
+    sipCseq_t     *sipCseq = NULL;
+    sipRespLine_t *pRespLine = NULL;
+    uint32_t       response_cseq_number = 0;
+    sipMethod_t    response_method = sipMethodInvalid;
+    uint16_t       response_code = 0;
+    sipStatusCodeClass_t code_class = codeClassInvalid;
+    int16_t        trx_index = -1;
+    const char    *via = NULL;
+
+    /*
+     * Check for the incomplete message body
+     */
+    if (!sippmh_is_message_complete(response)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_is_message_complete()");
+        return (SIP_MESSAGING_ERROR);
+    }
+
+    /*
+     * Check whether all mandatory SIP request headers are there.
+     */
+    from = sippmh_get_cached_header_val(response, FROM);
+    if (!from || (!is_good_header_length(from, FROM))) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(FROM)");
+        return (SIP_MESSAGING_ERROR);
+    }
+    to = sippmh_get_cached_header_val(response, TO);
+    if (!to || (!is_good_header_length(to, TO))) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(TO)");
+        return (SIP_MESSAGING_ERROR);
+    }
+    callID = sippmh_get_cached_header_val(response, CALLID);
+    if (!callID || (!is_good_header_length(callID, CALLID))) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(CALLID)");
+        return (SIP_MESSAGING_ERROR);
+    }
+    cseq = sippmh_get_cached_header_val(response, CSEQ);
+    if (!cseq) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_cached_header_val(CSEQ)");
+        return (SIP_MESSAGING_ERROR);
+    }
+    via = sippmh_get_cached_header_val(response, VIA);
+    if (via) {
+        if (strchr(via, COMMA)) {
+            CCSIP_DEBUG_ERROR("%s: Multiple Via headers found in response\n",
+                              fname);
+            return (SIP_MESSAGING_ERROR);
+        }
+    }
+
+    if (sipSPICheckContentHeaders(response) != SIP_MESSAGING_OK) {
+        CCSIP_DEBUG_ERROR("%s: Content header value not supported\n", fname);
+        return (SIP_MESSAGING_ERROR);
+    }
+
+    /*
+     * Get SIP response code
+     */
+    pRespLine = sippmh_get_response_line(response);
+    if (!pRespLine) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_get_response_line()");
+        return (SIP_MESSAGING_ERROR);
+    }
+    response_code = pRespLine->status_code;
+    SIPPMH_FREE_RESPONSE_LINE(pRespLine);
+    code_class = sippmh_get_code_class(response_code);
+
+    /*
+     * Extract response method and Cseq number from CSeq
+     */
+    sipCseq = sippmh_parse_cseq(cseq);
+    if (!sipCseq) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_parse_cseq()");
+        return (SIP_MESSAGING_ERROR);
+    }
+    response_method      = sipCseq->method;
+    response_cseq_number = sipCseq->number;
+    cpr_free(sipCseq);
+
+    /*
+     * If its a authentication response should not do any response
+     * mismatch checking since some ACK's are not stored and so this
+     * check will could return response mismatch.
+     */
+    switch (response_code) {
+    case SIP_CLI_ERR_UNAUTH:
+    case SIP_CLI_ERR_PROXY_REQD:
+        if (response_method == sipMethodAck) {
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Authentication Request for ACK: callid=%s,"
+                                "cseq=%u, cseq_method=%s\n",
+                                DEB_F_PREFIX_ARGS(SIP_ACK, fname), callID, response_cseq_number,
+                                sipGetMethodString(response_method));
+            return (SIP_MESSAGING_OK);
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    if (sippmh_msg_header_present(response, SIP_HEADER_REPLACES)) {
+        CCSIP_DEBUG_ERROR("%s: Error: Replace header is not valid "
+                          "in a response\n", fname);
+        return SIP_MESSAGING_ERROR;
+    }
+
+    /*
+     * Reliable Delivery
+     */
+    if (SipRelDevEnabled) {
+        /*
+         * responseRecord is a static because the structure it is
+         * made out of is so large.
+         */
+        static sipRelDevMessageRecord_t responseRecord;
+        int handle = -1;
+        sipLocation_t *reldev_to_loc = NULL;
+        char reldev_to_tag[MAX_SIP_TAG_LENGTH];
+        sipLocation_t *reldev_from_loc = NULL;
+
+        memset(&responseRecord, 0, sizeof(responseRecord));
+        memset(reldev_to_tag, 0, MAX_SIP_TAG_LENGTH);
+
+        /* Get to_tag */
+        reldev_to_loc = sippmh_parse_from_or_to((char *)to, TRUE);
+        if (reldev_to_loc) {
+            if (reldev_to_loc->tag) {
+                sstrncpy(reldev_to_tag,
+                         sip_sm_purify_tag(reldev_to_loc->tag),
+                         MAX_SIP_TAG_LENGTH);
+            } else {
+                reldev_to_tag[0] = '\0';
+            }
+            sstrncpy(responseRecord.to_user,
+                     reldev_to_loc->genUrl->u.sipUrl->user,
+                     RELDEV_MAX_USER_NAME_LEN);
+            sippmh_free_location(reldev_to_loc);
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname,
+                              get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO));
+            return (SIP_MESSAGING_ERROR);
+        }
+
+        reldev_from_loc = sippmh_parse_from_or_to((char *)from, TRUE);
+        if (reldev_from_loc) {
+            sstrncpy(responseRecord.from_user,
+                     reldev_from_loc->genUrl->u.sipUrl->user,
+                     RELDEV_MAX_USER_NAME_LEN);
+            sstrncpy(responseRecord.from_host,
+                     reldev_from_loc->genUrl->u.sipUrl->host,
+                     RELDEV_MAX_HOST_NAME_LEN);
+
+            /* Check from-tag */
+            if (reldev_from_loc->tag) {
+                if (!(ccb->flags & INCOMING)) {
+                    if (strcmp(reldev_from_loc->tag, ccb->sip_from_tag) != 0) {
+                        sippmh_free_location(reldev_from_loc);
+                        CCSIP_DEBUG_ERROR("%s: Outgoing: From tag in response "
+                                          "does not match stored value\n",
+                                          fname);
+                        return (SIP_MESSAGING_ERROR);
+                    }
+                } else {
+                    if (strcmp(reldev_from_loc->tag, ccb->sip_to_tag) != 0) {
+                        sippmh_free_location(reldev_from_loc);
+                        CCSIP_DEBUG_ERROR("%s: Incoming: From tag in response "
+                                          "does not match stored value\n",
+                                          fname);
+                        return (SIP_MESSAGING_ERROR);
+                    }
+                }
+            }
+
+            sippmh_free_location(reldev_from_loc);
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname,
+                              get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_FROM));
+            return (SIP_MESSAGING_ERROR);
+        }
+
+        responseRecord.is_request = FALSE;
+        sstrncpy(responseRecord.call_id, (callID) ? callID : "",
+                 MAX_SIP_CALL_ID);
+        responseRecord.cseq_method   = response_method;
+        responseRecord.cseq_number   = response_cseq_number;
+        responseRecord.response_code = response_code;
+        sstrncpy(responseRecord.tag, reldev_to_tag, MAX_SIP_TAG_LENGTH);
+        //responseRecord.line = ccb->index;
+
+        /* Check if duplicate */
+        if (sipRelDevMessageIsDuplicate(&responseRecord, &handle)) {
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Duplicate response detected...\n", DEB_F_PREFIX_ARGS(SIP_RESP, fname));
+            if (sipRelDevCoupledMessageSend(handle) < 0) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "sipRelDevCoupledMessageSend()");
+            }
+            if ((response_method == sipMethodInvite) &&
+                (code_class == codeClass1xx)) {
+                /* Allow multiple provisional responses */
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Allowed duplicate provisional response\n",
+                                    DEB_F_PREFIX_ARGS(SIP_RESP, fname));
+            } else {
+                return (SIP_MESSAGING_DUPLICATE);
+            }
+        }
+        /* Record the response in Recent Requests List */
+        sipRelDevMessageStore(&responseRecord);
+    }
+
+    /*
+     * Check whether the outstanding request's and this response's
+     * call id, cseq, and cseq method match
+     */
+    trx_index = get_method_request_trx_index(ccb, response_method, TRUE);
+    if (trx_index < 0) {
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"No Matching Request Found!:\n"
+                            "(Response: cseq=%u, trx_method=%s)\n",
+                            DEB_F_PREFIX_ARGS(SIP_CALL_STATUS, fname), response_cseq_number,
+                            sipGetMethodString(response_method));
+        return (SIP_MESSAGING_ERROR_NO_TRX);
+    }
+    if ((ccb->sent_request[trx_index].cseq_number != response_cseq_number) ||
+        (ccb->sent_request[trx_index].cseq_method != response_method) ||
+        (strcmp(ccb->sipCallID, callID) != 0)) {
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Response mismatch:\n(Response:"
+                            "callid=%s, cseq=%u, cseq_method=%s),\n"
+                            "(Request: callid=%s, cseq=%u, "
+                            "cseq_method=%s)\n",
+                            DEB_F_PREFIX_ARGS(SIP_RESP, fname), callID, response_cseq_number,
+                            sipGetMethodString(response_method),
+                            ccb->sipCallID,
+                            ccb->sent_request[trx_index].cseq_number,
+                            sipGetMethodString(ccb->sent_request[trx_index].cseq_method));
+        /*
+         * What happened here is that a timing
+         * issue occured between the proxy and phone where we
+         * timed out at almost the same time and we have already
+         * sent another request(probably a cancel) when the proxy
+         * sends this response back to us. So that we just Ack this
+         * response, we play games with previous call fields. We don't
+         * want to store the information because this response is basically
+         * stale at this point. We set the prevcall boolean to FALSE
+         * but we set the previous_line to 0xFF. This will indicate to
+         * the send routine to skip the storing of this Ack for retransmission.
+         */
+        if (response_method == sipMethodInvite) {
+            const char *resp_via = NULL;
+            sipVia_t *resp_via_parm = NULL;
+            int16_t trx_index_temp = -1;
+            const char *sip_via_branch = NULL;
+
+            switch (code_class) {
+            case codeClass2xx:
+                /*
+                 * If we get 200 response to a cancelled invite, send bye
+                 * to clear up the call.
+                 */
+                trx_index_temp = get_method_request_trx_index(ccb,
+                                                              sipMethodCancel,
+                                                              TRUE);
+                // if there is a CANCEL request outstanding -
+                // if (ccb->last_sent_request_cseq_method == sipMethodCancel) {
+                if (trx_index_temp > 0) {
+                    const char *contact = NULL;
+
+                    /*
+                     * We need to update the contact and to, from tags recvd.
+                     * in ccb because the CANCEL effectively becomes a nop due
+                     * to the race condition where the UAS sent 200 OK to
+                     * INVITE at the same instance we sent CANCEL. Hence we
+                     * need to ACK the 200 OK for INVITE with route header and
+                     * then send a BYE with to tag sent in order to clean up
+                     * the call at the called party
+                     */
+                    ccb->sip_to = strlib_update(ccb->sip_to, to);
+                    ccb->sip_from = strlib_update(ccb->sip_from, from);
+                    contact = sippmh_get_cached_header_val(response, CONTACT);
+                    if (contact) {
+                        if (ccb->contact_info) {
+                            sippmh_free_contact(ccb->contact_info);
+                        }
+                        ccb->contact_info = sippmh_parse_contact(contact);
+                    }
+                    sipSPISendFailureResponseAck(ccb, response, FALSE, 0xFF);
+                    sipSPISendBye(ccb, NULL, NULL);
+                } else {
+                    /*
+                     * No CANCEL was sent, so this is a plain mismatch
+                     * of the CSeq #
+                     */
+                    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Ignoring response message\n",
+                                        DEB_F_PREFIX_ARGS(SIP_RESP, fname));
+                    return (SIP_MESSAGING_ERROR);
+                }
+                break;
+            case codeClass4xx:
+            case codeClass5xx:
+            case codeClass6xx:
+                /*
+                 * Locate the branch Parameter. If the branch parameter is
+                 * the same, we are dealing with an outstanding transaction,
+                 * but the response was out of sync with expected. If branch
+                 * parameter is different, the transaction is gone. Ignore
+                 * stray responses
+                 */
+                resp_via = sippmh_get_cached_header_val(response, VIA);
+                if (resp_via) {
+                    resp_via_parm = sippmh_parse_via(resp_via);
+                    if (resp_via_parm) {
+                        /* check for branch param match for transaction */
+                        if ((resp_via_parm->branch_param) &&
+                            (strncmp(resp_via_parm->branch_param,
+                                     VIA_BRANCH_START, 7) == 0)) {
+                            trx_index_temp = get_last_request_trx_index(ccb,
+                                                                        TRUE);
+                            if (trx_index_temp != -1) {
+                                sip_via_branch = (char *)
+                                    ccb->sent_request[trx_index_temp].u.sip_via_branch;
+                                if (strncmp(resp_via_parm->branch_param,
+                                            // (char *) ccb->sip_via_branch,
+                                            sip_via_branch, VIA_BRANCH_LENGTH) != 0) {
+                                    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Stray Response: "
+                                            "Response branch: %s Request "
+                                            "branch: %s\n", DEB_F_PREFIX_ARGS(SIP_RESP, fname),
+                                            resp_via_parm->branch_param,
+                                            // (char *) ccb->sip_via_branch);
+                                            sip_via_branch);
+                                    sippmh_free_via(resp_via_parm);
+                                    return (SIP_MESSAGING_ERROR_STALE_RESP);
+                                }
+                            }
+                        }
+                        sippmh_free_via(resp_via_parm);
+                    }
+                }
+                sipSPISendFailureResponseAck(ccb, response, FALSE, 0xFF);
+                break;
+            default:
+                sipSPISendFailureResponseAck(ccb, response, FALSE, 0xFF);
+            }
+        }
+        return (SIP_MESSAGING_ERROR_STALE_RESP);
+    }
+    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Response match: callid=%s, cseq=%u, "
+                        "cseq_method=%s\n", DEB_F_PREFIX_ARGS(SIP_RESP, fname),
+                        callID, response_cseq_number, sipGetMethodString(response_method));
+
+
+    return (SIP_MESSAGING_OK);
+}
+
+/*
+ * Generate Authorization response
+ * Concats all the right strings and MD5s the result
+ */
+boolean
+sipSPIGenerateAuthorizationResponse (sip_authen_t *sip_authen,
+                                    const char *uri,
+                                    const char *method,
+                                    const char *user_name,
+                                    const char *user_password,
+                                    char **author_str,
+                                    int *nc_count,
+                                    ccsipCCB_t *ccb)
+{
+    static const char fname[] = "sipSPIGenerateAuthorizationResponse";
+    sip_author_t    sip_author;
+    HASHHEX         HA1;
+    HASHHEX         HA2 = "";
+    char            cnonce_str[NONCE_LEN];
+    char            nc_count_str[NONCE_LEN];
+    boolean         md5_sess_used;
+    uint32_t        i;
+
+    md5_sess_used = (cpr_strcasecmp(sip_authen->algorithm, "md5-sess") == 0) ?
+        TRUE : FALSE;
+
+    if ((sip_authen->scheme != SIP_DIGEST) ||
+        (!md5_sess_used &&
+         (cpr_strcasecmp(sip_authen->algorithm, "md5") != 0))) {
+        CCSIP_DEBUG_ERROR("%s: Error: invalid XXX-Authenticate.\n", fname);
+        CCSIP_DEBUG_ERROR("  scheme: %d, algorithm: %s.\n", sip_authen->scheme,
+                          sip_authen->algorithm);
+        return (FALSE);
+    }
+
+    sip_author.response = (char *) cpr_malloc(33 * sizeof(char));
+
+    /* sip_author has pointers that point to the same data that sip_authen
+     * points to. Ensure that sip_authen is not freed before sip_author
+     * is finished.
+     */
+
+    sip_author.str_start = NULL;
+    sip_author.user_pass = (char *) user_password;
+    sip_author.d_username   = (char *) user_name;
+    sip_author.unparsed_uri = (char *) uri;
+    sip_author.scheme    = sip_authen->scheme;
+    sip_author.realm     = sip_authen->realm;
+    sip_author.nonce     = sip_authen->nonce;
+    sip_author.algorithm = sip_authen->algorithm;
+
+    sip_author.opaque    = sip_authen->opaque;
+    sip_author.qop       = sip_authen->qop;
+
+    /*
+     * Setup the cnonce and nc_count if qop options are used or if
+     * md5-sess is the algorithm requested.
+     */
+    if (!md5_sess_used && (sip_authen->qop == NULL)) {
+        sip_author.cnonce = NULL;
+        sip_author.nc_count = NULL;
+    } else {
+        if (md5_sess_used && (ccb != NULL)) {
+            if (ccb->authen.cnonce[0] == '\0') {
+                snprintf(ccb->authen.cnonce, NONCE_LEN, "%8.8x",
+                         (unsigned int)cpr_rand());
+            }
+            sip_author.cnonce = ccb->authen.cnonce;
+        } else {
+            snprintf(cnonce_str, NONCE_LEN, "%8.8x",
+                     (unsigned int)cpr_rand());
+            sip_author.cnonce = cnonce_str;
+        }
+        snprintf(nc_count_str, NONCE_LEN, "%08x", ++(*nc_count));
+        sip_author.nc_count = nc_count_str;
+    }
+    sip_author.auth_param = NULL;
+
+    DigestCalcHA1(sip_author.algorithm, sip_author.d_username,
+                  sip_author.realm, (char *) user_password, sip_author.nonce,
+                  sip_author.cnonce, HA1);
+
+    /*
+     * Need to calculate HEntity.
+     * HEntity is basically just the hash of the SDP. Only the INVITE has SDP
+     * so we hash the SDP for the INVITE and for other methods we just
+     * hash the "".
+     */
+    if ((strcmp(method, SIP_METHOD_INVITE) == 0) && (ccb != NULL)) {
+        /* Use the content from the body saved */
+        for (i = 0; i < ccb->local_msg_body.num_parts; i++) {
+            if (ccb->local_msg_body.parts[i].body != NULL) {
+                DigestString(ccb->local_msg_body.parts[i].body, HA2);
+                AUTH_DEBUG(DEB_F_PREFIX"entity body= \n", DEB_F_PREFIX_ARGS(SIP_MSG, fname));
+            }
+        }
+    } else {
+        DigestString("", HA2);
+    }
+
+    DigestCalcResponse(HA1, sip_author.nonce, sip_author.nc_count,
+                       sip_author.cnonce, sip_author.qop,
+                       (char *)method, //SIP_METHOD_REGISTER,
+                       sip_author.unparsed_uri, HA2, sip_author.response);
+
+    *author_str = sippmh_generate_authorization(&sip_author);
+
+    cpr_free(sip_author.response);
+
+    return (TRUE);
+}
+
+
+/*
+ * Generates Route Headers for UAC
+ * Pops the first RR entry and puts in Req Line
+ * Copies the rest of RR entries in reverse order into Route Header
+ * Appends Contact Header at the end of Route
+ * header
+ * If the first route entry has loose routing,"lr", set. does not add the
+ * Contact Header to the end of Route, and does not pop the first entry.
+ * In this case, indicates loose
+ * routing to caller so that they may add the Contact to the Req-URI
+ * instead
+ */
+boolean
+sipSPIGenerateRouteHeaderUAC (sipRecordRoute_t *rr_info,
+                              char *route,
+                              int route_str_len,
+                              boolean *loose_routing)
+{
+    boolean     retval = FALSE;
+    int         i, j, start, limit;
+    static char temp_route[MAX_SIP_HEADER_LENGTH];
+    sipUrl_t   *url_info = NULL;
+    genUrl_t   *gen;
+    boolean     lr = FALSE;
+    char        url[SIPS_URL_LEN];
+
+    if (route == NULL) {
+        return retval;
+    }
+
+    start = rr_info->num_locations - 1;
+    limit = 0;
+    route[0] = '\0';
+
+    for (i = start; i >= limit; i--) {
+        url_info = rr_info->locations[i]->genUrl->u.sipUrl;
+        if (i == start) {
+            if (url_info->lr_flag == FALSE) {
+                // Skip the first entry if lr flag is not set
+                continue;
+            } else {
+                lr = TRUE;
+            }
+        }
+        if (rr_info->locations[i]->genUrl->sips) {
+            snprintf(url, sizeof(url), "sips");
+        } else {
+            snprintf(url, sizeof(url), "sip");
+        }
+        temp_route[0] = '\0';
+        if (url_info->user == NULL) {
+            snprintf(temp_route, sizeof(temp_route),
+                     "<%s:%s:%d", url, url_info->host, url_info->port);
+        } else {
+            if (url_info->password) {
+                snprintf(temp_route, sizeof(temp_route), "<%s:%s:%s@%s:%d",
+                         url, url_info->user, url_info->password,
+                         url_info->host, url_info->port);
+            } else {
+                snprintf(temp_route, sizeof(temp_route), "<%s:%s@%s:%d",
+                         url, url_info->user, url_info->host, url_info->port);
+            }
+        }
+        if (url_info->maddr) {
+            /*static */ char maddr[MAX_SIP_HEADER_LENGTH];
+
+            snprintf(maddr, sizeof(maddr), ";maddr=%s", url_info->maddr);
+            sstrncat(temp_route, maddr,
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+
+        if (url_info->ttl_val) {
+            /*static */ char ttl[MAX_SIP_HEADER_LENGTH];
+
+            snprintf(ttl, sizeof(ttl), ";ttl=%d", url_info->ttl_val);
+            sstrncat(temp_route, ttl,
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+
+        switch (url_info->transport) {
+        case TRANSPORT_UDP:
+            sstrncat(temp_route, ";transport=udp",
+                    sizeof(temp_route) - strlen(temp_route));
+            break;
+        case TRANSPORT_TCP:
+            sstrncat(temp_route, ";transport=tcp",
+                    sizeof(temp_route) - strlen(temp_route));
+            break;
+        case TRANSPORT_TLS:
+            sstrncat(temp_route, ";transport=tls",
+                    sizeof(temp_route) - strlen(temp_route));
+            break;
+        case TRANSPORT_SCTP:
+            sstrncat(temp_route, ";transport=sctp",
+                    sizeof(temp_route) - strlen(temp_route));
+            break;
+        }
+
+        if (url_info->is_phone) {
+            sstrncat(temp_route, ";user=phone",
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+
+        if (url_info->lr_flag) {
+            sstrncat(temp_route, ";lr",
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+
+        j = 0;
+        gen = rr_info->locations[i]->genUrl;
+
+        while (j < SIP_MAX_LOCATIONS) {
+            if (gen->other_params[j] != NULL) {
+                sstrncat(temp_route, ";",
+                        sizeof(temp_route) - strlen(temp_route));
+                sstrncat(temp_route, gen->other_params[j],
+                        sizeof(temp_route) - strlen(temp_route));
+                break;
+            }
+            j++;
+        }
+
+        if (i > limit) {
+            sstrncat(temp_route, ">,",
+                    sizeof(temp_route) - strlen(temp_route));
+        } else {
+            sstrncat(temp_route, ">",
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+
+        sstrncat(route, temp_route, route_str_len - strlen(route));
+
+    }
+
+    *loose_routing = lr;
+    retval = TRUE;
+    return retval;
+}
+
+/*
+ * Generates Route Headers for UAS
+ * Pops the first RR entry and puts in Req Line
+ * Copies the rest of RR entries into Route Header
+ * Appends Contact Header at the end of Route
+ * header
+ */
+boolean
+sipSPIGenerateRouteHeaderUAS (sipRecordRoute_t *rr_info,
+                              char *route,
+                              int route_str_len,
+                              boolean *loose_routing)
+{
+    const char *fname = "sipSPIGenerateRouteHeaderUAS";
+    boolean     retval = FALSE;
+    int         i, j, start, limit;
+    static char temp_route[MAX_SIP_HEADER_LENGTH];
+    sipUrl_t   *url_info;
+    genUrl_t   *gen;
+    boolean     lr = FALSE;
+    char        url[SIPS_URL_LEN];
+
+    if (route == NULL) {
+        return (retval);
+    }
+
+    start = 0;
+    limit = rr_info->num_locations - 1;
+    route[0] = '\0';
+
+    for (i = start; i <= limit; i++) {
+        if (rr_info->locations[i]->genUrl->schema == URL_TYPE_SIP) {
+            url_info = rr_info->locations[i]->genUrl->u.sipUrl;
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR), fname);
+            return (FALSE);
+        }
+        if (i == start) {
+            if (url_info->lr_flag == FALSE) {
+                // don't add the first route, if the lr flag is not set
+                continue;
+            } else {
+                lr = TRUE;
+            }
+        }
+        if (rr_info->locations[i]->genUrl->sips) {
+            snprintf(url, sizeof(url), "sips");
+        } else {
+            snprintf(url, sizeof(url), "sip");
+        }
+        temp_route[0] = '\0';
+        if (url_info->user == NULL) {
+            snprintf(temp_route, sizeof(temp_route),
+                     "<%s:%s:%d", url, url_info->host, url_info->port);
+        } else {
+            if (url_info->password) {
+                snprintf(temp_route, sizeof(temp_route), "<%s:%s:%s@%s:%d",
+                         url, url_info->user,
+                         url_info->password, url_info->host, url_info->port);
+            } else {
+                snprintf(temp_route, sizeof(temp_route), "<%s:%s@%s:%d",
+                         url, url_info->user, url_info->host, url_info->port);
+            }
+        }
+
+        if (url_info->maddr) {
+            /*static */ char maddr[MAX_SIP_HEADER_LENGTH];
+
+            snprintf(maddr, sizeof(maddr), ";maddr=%s", url_info->maddr);
+            sstrncat(temp_route, maddr,
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+
+        if (url_info->ttl_val) {
+            /*static */ char ttl[MAX_SIP_HEADER_LENGTH];
+
+            snprintf(ttl, sizeof(ttl), ";ttl=%d", url_info->ttl_val);
+            sstrncat(temp_route, ttl,
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+
+        switch (url_info->transport) {
+        case TRANSPORT_UDP:
+            sstrncat(temp_route, ";transport=udp",
+                    sizeof(temp_route) - strlen(temp_route));
+            break;
+        case TRANSPORT_TCP:
+            sstrncat(temp_route, ";transport=tcp",
+                    sizeof(temp_route) - strlen(temp_route));
+            break;
+        case TRANSPORT_TLS:
+            sstrncat(temp_route, ";transport=tls",
+                    sizeof(temp_route) - strlen(temp_route));
+            break;
+        case TRANSPORT_SCTP:
+            sstrncat(temp_route, ";transport=sctp",
+                    sizeof(temp_route) - strlen(temp_route));
+            break;
+        }
+
+        if (url_info->is_phone) {
+            sstrncat(temp_route, ";user=phone",
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+
+        if (url_info->lr_flag) {
+            sstrncat(temp_route, ";lr",
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+
+        j = 0;
+        gen = rr_info->locations[i]->genUrl;
+
+        while (j < SIP_MAX_LOCATIONS) {
+            if (gen->other_params[j] != NULL) {
+                sstrncat(temp_route, ";",
+                        sizeof(temp_route) - strlen(temp_route));
+                sstrncat(temp_route, gen->other_params[j],
+                        sizeof(temp_route) - strlen(temp_route));
+                break;
+            }
+            j++;
+        }
+
+        if (i < limit) {
+            sstrncat(temp_route, ">,",
+                    sizeof(temp_route) - strlen(temp_route));
+        } else {
+            sstrncat(temp_route, ">",
+                    sizeof(temp_route) - strlen(temp_route));
+        }
+        sstrncat(route, temp_route, route_str_len - strlen(route));
+    }
+
+    *loose_routing = lr;
+    retval = TRUE;
+    return (retval);
+}
+
+/*
+ * This function regenerates the Contact Header
+ * from the parsed information stored in CCB
+ */
+boolean
+sipSPIGenerateContactHeader (sipContact_t *contact_info,
+                             char *contact,
+                             int len)
+{
+    const char *fname = "sipSPIGenerateContactHeader";
+    boolean     retval = FALSE;
+    sipUrl_t   *sipUrl;
+
+    if (contact == NULL) {
+        return (retval);
+    }
+
+    if (contact_info == NULL) {
+        contact[0] = '\0';
+        retval = TRUE;
+        return (retval);
+    }
+
+    if (contact_info->locations[0]->genUrl->schema == URL_TYPE_SIP) {
+        sipUrl = contact_info->locations[0]->genUrl->u.sipUrl;
+    } else {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR), fname);
+        return (FALSE);
+    }
+
+    if (sipUrl->user == NULL) {
+        snprintf(contact, len, "<sip:%s:%d", sipUrl->host, sipUrl->port);
+    } else {
+        if ((sipUrl->password) && (*(sipUrl->password))) {
+            snprintf(contact, len, "<sip:%s:%s@%s:%d", sipUrl->user,
+                     sipUrl->password, sipUrl->host, sipUrl->port);
+        } else {
+            snprintf(contact, len, "<sip:%s@%s:%d", sipUrl->user,
+                     sipUrl->host, sipUrl->port);
+        }
+    }
+
+    /* Assume contact length is the same for all clients */
+
+    if (sipUrl->maddr) {
+        /*static */ char maddr[MAX_SIP_HEADER_LENGTH];
+
+        snprintf(maddr, sizeof(maddr), ";maddr=%s", sipUrl->maddr);
+        sstrncat(contact, maddr, len - strlen(contact));
+    }
+
+    if (sipUrl->ttl_val) {
+        /*static */ char ttl[MAX_SIP_HEADER_LENGTH];
+
+        snprintf(ttl, sizeof(ttl), ";ttl=%d", sipUrl->ttl_val);
+        sstrncat(contact, ttl, len - strlen(contact));
+    }
+
+    switch (sipUrl->transport) {
+    case TRANSPORT_UDP:
+        sstrncat(contact, ";transport=udp", len - strlen(contact));
+        break;
+    case TRANSPORT_TCP:
+        sstrncat(contact, ";transport=tcp", len - strlen(contact));
+        break;
+    case TRANSPORT_TLS:
+        sstrncat(contact, ";transport=tls", len - strlen(contact));
+        break;
+    case TRANSPORT_SCTP:
+        sstrncat(contact, ";transport=sctp", len - strlen(contact));
+        break;
+    }
+
+    if (sipUrl->is_phone) {
+        sstrncat(contact, ";user=phone", len - strlen(contact));
+    }
+
+    sstrncat(contact, ">", len - strlen(contact));
+    retval = TRUE;
+    return (retval);
+}
+
+
+char *
+sipSPIUrlDestination (sipUrl_t *sipUrl)
+{
+    return ((sipUrl->maddr) ? sipUrl->maddr : sipUrl->host);
+}
+
+
+int
+sipSPICheckContact (const char *contact)
+{
+    const char   *fname = "sipSPICheckContact";
+    sipContact_t *contact_info = NULL;
+    int           result = 0;
+
+    contact_info = sippmh_parse_contact(contact);
+    if (contact_info) {
+        if (contact_info->locations[0]->genUrl->schema != URL_TYPE_SIP) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR), fname);
+            sippmh_free_contact(contact_info);
+            return (-1);
+        }
+
+        sippmh_free_contact(contact_info);
+    }
+
+    return (result);
+}
+
+
+sipRet_t
+sipAddDateHeader (sipMessage_t *sip_message)
+{
+    char       sip_date[MAX_SIP_DATE_LENGTH];
+    cpr_time_t timestamp;
+    struct tm  ts;
+
+    (void) time((time_t *)&timestamp);
+
+    (void) gmtime_r((time_t *)&timestamp, &ts);
+
+    if (strftime(sip_date, MAX_SIP_DATE_LENGTH, "%a, %d %b %Y %H:%M:%S GMT", &ts)
+        == 0) {
+        /* If defined, allow for a failure if unable to create date header */
+        return STATUS_FAILURE;
+    }
+
+    return sippmh_add_text_header(sip_message, SIP_HEADER_DATE, sip_date);
+}
+
+int
+sipGetMessageCSeq (sipMessage_t *pMessage,
+                   uint32_t *pResultCSeqNumber,
+                   sipMethod_t *pResultCSeqMethod)
+{
+    const char *cseq = NULL;
+    sipCseq_t  *sipCseq = NULL;
+
+    cseq = sippmh_get_cached_header_val(pMessage, CSEQ);
+    if (!cseq) {
+        return (-1);
+    }
+
+    sipCseq = sippmh_parse_cseq(cseq);
+    if (!sipCseq) {
+        return (-1);
+    }
+
+    *pResultCSeqNumber = sipCseq->number;
+    *pResultCSeqMethod = sipCseq->method;
+
+    cpr_free(sipCseq);
+    return (0);
+}
+
+
+void
+sipGetMessageToTag (sipMessage_t *pMessage, char *to_tag,
+                    int to_tag_max_length)
+{
+    const char    *to = NULL;
+    sipLocation_t *to_loc = NULL;
+
+    memset(to_tag, 0, to_tag_max_length);
+    //For self generated methods (i.e. outgoing), the TO HEADER
+    //may not be in the cached area of the message structue.
+    to = sippmh_get_cached_header_val(pMessage, TO);
+    if (!to) {
+        to = sippmh_get_header_val(pMessage, SIP_HEADER_TO, SIP_C_HEADER_TO);
+    }
+
+    if (to) {
+        to_loc = sippmh_parse_from_or_to((char *)to, TRUE);
+        if (to_loc) {
+            if (to_loc->tag) {
+                sstrncpy(to_tag, sip_sm_purify_tag(to_loc->tag),
+                         to_tag_max_length);
+            }
+            sippmh_free_location(to_loc);
+        }
+    }
+
+    return;
+}
+
+
+boolean
+sipSPISendByeAuth (sipMessage_t *pResponse,
+                  sipAuthenticate_t authen,
+                  cpr_ip_addr_t *dest_ipaddr,
+                  uint16_t dest_port,
+                  uint32_t cseq_number,
+                  char *alsoString,
+                  char *last_call_route,
+                  char *last_call_route_request_uri,
+                  line_t previous_call_line)
+{
+    const char     *fname = "sipSPISendByeAuth";
+    sipMessage_t   *request = NULL;
+    sipRet_t        flag = STATUS_SUCCESS;
+    sipRet_t        tflag = STATUS_SUCCESS;
+    const char     *response_contact = NULL;
+    const char     *response_record_route = NULL;
+    const char     *response_to = NULL;
+    const char     *response_from = NULL;
+    const char     *response_callid = NULL;
+    sipContact_t   *response_contact_info = NULL;
+    sipRecordRoute_t *response_record_route_info = NULL;
+
+    cpr_ip_addr_t   request_uri_addr;
+    uint16_t        request_uri_port = 0;
+    cpr_ip_addr_t  src_ipaddr;
+    char            src_addr_str[MAX_IPADDR_STR_LEN];
+    static char     ReqURI[MAX_SIP_URL_LENGTH];
+    static char     via[SIP_MAX_VIA_LENGTH];
+    ccsipCCB_t     *ccb = NULL;
+    int             timeout = 0;
+    sipLocation_t  *request_uri_loc = NULL;
+    sipUrl_t       *request_uri_url = NULL;
+    sipUrl_t       *sipUrl = NULL;
+    int             i = 0;
+    int             nat_enable = 0;
+
+    CPR_IP_ADDR_INIT(request_uri_addr);
+    CPR_IP_ADDR_INIT(src_ipaddr);
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "BYE(Auth)");
+
+    request = GET_SIP_MESSAGE();
+    if (!request) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "GET_SIP_MESSAGE()");
+        return (FALSE);
+    }
+
+    /* Init temp ccb to ccbs[previous_call_line] */
+    ccb = &gGlobInfo.ccbs[previous_call_line];
+    if (ccb->state != SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING) {
+        CCSIP_DEBUG_ERROR("%s: Error: ccb %d is not in "
+                          "SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING state.\n",
+                          fname, previous_call_line);
+        free_sip_message(request);
+        return (FALSE);
+    }
+    ccb->contact_info = NULL;
+    ccb->record_route_info = NULL;
+    ccb->flags = 0;
+
+    response_contact = sippmh_get_cached_header_val(pResponse, CONTACT);
+    response_record_route =
+        sippmh_get_cached_header_val(pResponse, RECORD_ROUTE);
+    response_to = sippmh_get_cached_header_val(pResponse, TO);
+    response_from = sippmh_get_cached_header_val(pResponse, FROM);
+    response_callid = sippmh_get_cached_header_val(pResponse, CALLID);
+
+    if (response_contact) {
+        response_contact_info = sippmh_parse_contact(response_contact);
+        ccb->contact_info = response_contact_info;
+    }
+    if (response_record_route) {
+        response_record_route_info =
+            sippmh_parse_record_route(response_record_route);
+        ccb->record_route_info = response_record_route_info;
+    }
+
+    /*
+     * Determine the Request-URI
+     */
+    memset(ReqURI, 0, sizeof(ReqURI));
+    if (response_record_route_info) {
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Forming Req-URI: using 401/407's Record-Route\n",
+                          DEB_F_PREFIX_ARGS(SIP_REQ_URI, fname));
+        i = response_record_route_info->num_locations - 1;
+        if (response_record_route_info->locations[i]->genUrl->schema
+                == URL_TYPE_SIP) {
+            sipUrl = response_record_route_info->locations[i]->genUrl->u.sipUrl;
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR), fname);
+            if (ccb->record_route_info) {
+                sippmh_free_record_route(ccb->record_route_info);
+                ccb->record_route_info = NULL;
+            }
+            free_sip_message(request);
+            return (FALSE);
+        }
+
+        request_uri_port = sipUrl->port;
+        if (!sipUrl->port_present) {
+            dns_error_code =
+                sipTransportGetServerAddrPort(sipSPIUrlDestination(sipUrl),
+                                              &request_uri_addr,
+                                              &request_uri_port,
+                                              NULL, FALSE);
+        } else {
+            dns_error_code = dnsGetHostByName(sipSPIUrlDestination(sipUrl),
+                                               &request_uri_addr, 100, 1);
+        }
+        if (dns_error_code == 0) {
+            util_ntohl(&request_uri_addr, &request_uri_addr);
+
+        } else {
+            request_uri_addr = ip_addr_invalid;
+        }
+
+        if (sipUrl->user != NULL) {
+            if (sipUrl->password) {
+                snprintf(ReqURI, sizeof(ReqURI),
+                         sipUrl->is_phone ? "sip:%s:%s@%s:%d;user=phone" :
+                         "sip:%s:%s@%s:%d",
+                         sipUrl->user, sipUrl->password,
+                         sipUrl->host, sipUrl->port);
+            } else {
+                snprintf(ReqURI, sizeof(ReqURI),
+                         sipUrl->is_phone ? "sip:%s@%s:%d;user=phone" :
+                         "sip:%s@%s:%d", sipUrl->user, sipUrl->host,
+                         sipUrl->port);
+            }
+        } else {
+            snprintf(ReqURI, sizeof(ReqURI),
+                     sipUrl->is_phone ? "sip:%s:%d;user=phone" : "sip:%s:%d",
+                     sipUrl->host, sipUrl->port);
+        }
+    } else if (last_call_route[0] && last_call_route_request_uri[0]) {
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Forming Req-URI: using current Route "
+                          "information.\n", DEB_F_PREFIX_ARGS(SIP_REQ_URI, fname));
+        sstrncpy(ReqURI, last_call_route_request_uri, sizeof(ReqURI));
+        request_uri_addr = *dest_ipaddr;
+        request_uri_port = dest_port;
+    } else if (response_contact_info) {
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Forming Req-URI: using Contact\n", DEB_F_PREFIX_ARGS(SIP_REQ_URI, fname));
+        if (response_contact_info->locations[0]->genUrl->schema == URL_TYPE_SIP) {
+            sipUrl = response_contact_info->locations[0]->genUrl->u.sipUrl;
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR), fname);
+            if (ccb->contact_info) {
+                sippmh_free_contact(ccb->contact_info);
+                ccb->contact_info = NULL;
+            }
+            free_sip_message(request);
+            return (FALSE);
+        }
+
+        request_uri_port = sipUrl->port;
+        if (!sipUrl->port_present) {
+            dns_error_code =
+                sipTransportGetServerAddrPort(sipSPIUrlDestination(sipUrl),
+                                              &request_uri_addr,
+                                              &request_uri_port,
+                                              NULL, FALSE);
+        } else {
+            dns_error_code = dnsGetHostByName(sipSPIUrlDestination(sipUrl),
+                                               &request_uri_addr, 100, 1);
+        }
+        if (dns_error_code == 0) {
+
+            util_ntohl(&request_uri_addr, &request_uri_addr);
+        } else {
+            request_uri_addr = ip_addr_invalid;
+        }
+
+        if (sipUrl->user != NULL) {
+            if (sipUrl->password) {
+                snprintf(ReqURI, sizeof(ReqURI),
+                         sipUrl->is_phone ? "sip:%s:%s@%s:%d;user=phone" :
+                         "sip:%s:%s@%s:%d",
+                         sipUrl->user, sipUrl->password,
+                         sipUrl->host, sipUrl->port);
+            } else {
+                snprintf(ReqURI, sizeof(ReqURI),
+                         sipUrl->is_phone ? "sip:%s@%s:%d;user=phone" :
+                         "sip:%s@%s:%d", sipUrl->user,
+                         sipUrl->host, sipUrl->port);
+            }
+        } else {
+            snprintf(ReqURI, sizeof(ReqURI),
+                     sipUrl->is_phone ? "sip:%s:%d;user=phone" : "sip:%s:%d",
+                     sipUrl->host, sipUrl->port);
+        }
+    } else {
+        request_uri_addr = *dest_ipaddr;
+        request_uri_port = dest_port;
+        request_uri_loc = sippmh_parse_from_or_to((char *)response_to, TRUE);
+        if (!request_uri_loc) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname,
+                              get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO));
+            free_sip_message(request);
+            return (FALSE);
+        }
+
+        if (!sippmh_valid_url(request_uri_loc->genUrl)) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sippmh_valid_url()");
+            sippmh_free_location(request_uri_loc);
+            free_sip_message(request);
+            return (FALSE);
+        }
+
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"Forming Req-URI (Caller): using original "
+                          "Req-URI\n", DEB_F_PREFIX_ARGS(SIP_REQ_URI, fname));
+        if (request_uri_loc->name) {
+            if (request_uri_loc->name[0]) {
+                sstrncat(ReqURI, "\"", sizeof(ReqURI) - strlen(ReqURI));
+                sstrncat(ReqURI, request_uri_loc->name,
+                        sizeof(ReqURI) - strlen(ReqURI));
+                sstrncat(ReqURI, "\" ", sizeof(ReqURI) - strlen(ReqURI));
+            }
+        }
+        sstrncat(ReqURI, "sip:", sizeof(ReqURI) - strlen(ReqURI));
+        if (request_uri_loc->genUrl->schema == URL_TYPE_SIP) {
+            request_uri_url = request_uri_loc->genUrl->u.sipUrl;
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR), fname);
+            sippmh_free_location(request_uri_loc);
+            free_sip_message(request);
+            return (FALSE);
+        }
+
+        if (request_uri_url->user) {
+            sstrncat(ReqURI, request_uri_url->user,
+                    sizeof(ReqURI) - strlen(ReqURI));
+            sstrncat(ReqURI, "@", sizeof(ReqURI) - strlen(ReqURI));
+        }
+        if (request_uri_url->is_phone) {
+            sstrncat(ReqURI, ";user=phone",
+                    sizeof(ReqURI) - strlen(ReqURI));
+        }
+        sstrncat(ReqURI, request_uri_url->host,
+                sizeof(ReqURI) - strlen(ReqURI));
+        sippmh_free_location(request_uri_loc);
+    }
+
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        sip_config_get_net_device_ipaddr(&src_ipaddr);
+        ipaddr2dotted(src_addr_str, &src_ipaddr);
+    } else {
+        sip_config_get_nat_ipaddr(&src_ipaddr);
+        ipaddr2dotted(src_addr_str, &src_ipaddr);
+    }
+
+    /*
+     * Build the request
+     */
+
+    /* Write Req-URI */
+    tflag = sippmh_add_request_line(request, SIP_METHOD_BYE, ReqURI,
+                                    SIP_VERSION);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Write Via */
+    snprintf(via, sizeof(via), "SIP/2.0/%s %s:%d;%s=%s%.8x",
+             sipTransportGetTransportType(ccb->dn_line, TRUE, ccb),
+             src_addr_str,
+             ccb->local_port,
+             VIA_BRANCH, VIA_BRANCH_START,
+             (unsigned int)cpr_rand());
+    tflag = sippmh_add_text_header(request, SIP_HEADER_VIA, via);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Write To, From, and Call-ID standard headers */
+    tflag = sippmh_add_text_header(request, SIP_HEADER_FROM, response_from);
+    UPDATE_FLAGS(flag, tflag);
+    tflag = sippmh_add_text_header(request, SIP_HEADER_TO, response_to);
+    UPDATE_FLAGS(flag, tflag);
+    tflag = sippmh_add_text_header(request, SIP_HEADER_CALLID, response_callid);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Write Date header */
+    tflag = sipAddDateHeader(request);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Write User Agent header */
+    tflag = sippmh_add_text_header(request, SIP_HEADER_USER_AGENT,
+                                   sipHeaderUserAgent);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* add in call stats header if needed */
+    tflag = sipSPIAddCallStats(ccb, request);
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Write Route */
+    if (response_record_route_info) {
+        tflag = (sipSPIAddRouteHeaders(request, ccb, NULL, 0)) ?
+            STATUS_SUCCESS : STATUS_FAILURE;
+    } else if (last_call_route[0]) {
+        tflag = sippmh_add_text_header(request, SIP_HEADER_ROUTE,
+                                       last_call_route);
+    }
+    UPDATE_FLAGS(flag, tflag);
+
+    /* Free contact and record-route */
+    if (response_contact) {
+        if (ccb->contact_info) {
+            sippmh_free_contact(ccb->contact_info);
+            ccb->contact_info = NULL;
+        }
+    }
+    if (response_record_route) {
+        if (ccb->record_route_info) {
+            sippmh_free_record_route(ccb->record_route_info);
+            ccb->record_route_info = NULL;
+        }
+    }
+
+    /* Write CSeq */
+    tflag = sippmh_add_cseq(request, SIP_METHOD_BYE, cseq_number);
+    UPDATE_FLAGS(flag, tflag);
+
+    if (alsoString) {
+        if (alsoString[0]) {
+            tflag = sippmh_add_text_header(request, SIP_HEADER_ALSO,
+                                           alsoString);
+            UPDATE_FLAGS(flag, tflag);
+        }
+    }
+
+    if (authen.authorization != NULL) {
+        tflag = sippmh_add_text_header(request, AUTHOR_HDR(authen.status_code),
+                                       authen.authorization);
+        UPDATE_FLAGS(flag, tflag);
+        cpr_free(authen.authorization);
+    }
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (request)
+            free_sip_message(request);
+        return (FALSE);
+    }
+
+    /* Send message */
+    config_get_value(CFGID_TIMER_T1, &timeout, sizeof(timeout));
+    ccb->retx_counter = 0;
+    if (sipTransportChannelCreateSend(ccb, request, sipMethodBye,
+                                      &request_uri_addr,
+                                      request_uri_port, timeout,
+                                      RELDEV_NO_STORED_MSG) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipTransportChannelCreateSend()");
+        if (request)
+            free_sip_message(request);
+
+        return (FALSE);
+    }
+    return (TRUE);
+}
+
+void
+free_sip_message (sipMessage_t *message)
+{
+    if (message) {
+        sippmh_message_free(message);
+    }
+}
+
+/*
+ * Generate the full URL (currently limited to the sipUrl only)
+ */
+void
+sipSPIGenerateTargetUrl (genUrl_t *genUrl, char *sipurlstr)
+{
+    uint8_t i;
+    char    temp[MAX_SIP_HEADER_LENGTH];
+    size_t  url_length = 0;
+    char   *right_bracket = NULL;
+    boolean right_bracket_removed = FALSE;
+
+    if (genUrl->schema == URL_TYPE_SIP) {
+        sipSPIGenerateSipUrl(genUrl->u.sipUrl, sipurlstr);
+    } else {
+        return;
+    }
+
+    url_length = strlen(sipurlstr);
+    if (url_length == 0) {
+        return;
+    }
+
+    for (i = 0; i < SIP_MAX_LOCATIONS; i++) {
+        if (genUrl->other_params[i] != NULL) {
+            if (i == 0) {
+                // Remove the ">" from the end of sipurlstr
+                right_bracket = strchr(sipurlstr, '>');
+                if (right_bracket) {
+                    *right_bracket = '\0';
+                    right_bracket_removed = TRUE;
+                }
+            }
+            snprintf(temp, sizeof(temp), ";%s", genUrl->other_params[i]);
+            sstrncat(sipurlstr, temp, MAX_SIP_URL_LENGTH - url_length );
+            url_length = strlen(sipurlstr);
+        }
+    }
+
+    if (right_bracket_removed) {
+        sstrncat(sipurlstr, ">", MAX_SIP_URL_LENGTH - url_length);
+    }
+}
+
+/*
+ * Generates a plain string from sipUrl structure
+ *
+*/
+void
+sipSPIGenerateSipUrl (sipUrl_t *sipUrl, char *sipurlstr)
+{
+    char temp[MAX_SIP_HEADER_LENGTH];
+
+    if (sipUrl->user == NULL) {
+        snprintf(sipurlstr, MAX_SIP_HEADER_LENGTH, "<sip:%s:%d",
+                 sipUrl->host, sipUrl->port);
+    } else {
+        snprintf(sipurlstr, MAX_SIP_HEADER_LENGTH, "<sip:%s@%s:%d",
+                 sipUrl->user, sipUrl->host, sipUrl->port);
+    }
+
+    /* Assume sipurlstr _length is the same for all clients */
+
+    if (sipUrl->maddr) {
+        snprintf(temp, sizeof(temp), ";maddr=%s", sipUrl->maddr);
+        sstrncat(sipurlstr, temp, MAX_SIP_HEADER_LENGTH );
+    }
+
+    if (sipUrl->ttl_val) {
+        snprintf(temp, sizeof(temp), ";ttl=%d", sipUrl->ttl_val);
+        sstrncat(sipurlstr, temp, MAX_SIP_HEADER_LENGTH);
+    }
+
+    switch (sipUrl->transport) {
+    case TRANSPORT_UDP:
+        sstrncat(sipurlstr, ";transport=udp", MAX_SIP_HEADER_LENGTH);
+        break;
+    case TRANSPORT_TCP:
+        sstrncat(sipurlstr, ";transport=tcp", MAX_SIP_HEADER_LENGTH);
+        break;
+    case TRANSPORT_TLS:
+        sstrncat(sipurlstr, ";transport=tls", MAX_SIP_HEADER_LENGTH);
+        break;
+    case TRANSPORT_SCTP:
+        sstrncat(sipurlstr, ";transport=sctp", MAX_SIP_HEADER_LENGTH);
+        break;
+    }
+
+    if (sipUrl->is_phone) {
+        sstrncat(sipurlstr, ";user=phone", MAX_SIP_HEADER_LENGTH);
+    }
+
+    sstrncat(sipurlstr, ">", MAX_SIP_HEADER_LENGTH);
+
+}
+
+#define MAX_SIP_METHOD_STRINGS 17
+#define MAX_SIP_METHOD_STRING_LEN 16
+const char *
+sipGetMethodString (sipMethod_t methodname /*, char *methodstring */)
+{
+    int ino = (int) methodname; //Register is defined as 100 in pmh.h file
+    //Please make sure the following array is consistent with the enum
+    int idx = 0;
+    static const char methods[MAX_SIP_METHOD_STRINGS][MAX_SIP_METHOD_STRING_LEN] =
+        { "REGISTER", "OPTIONS", "INVITE", "BYE",
+        "CANCEL", "PRACK", "COMET", "NOTIFY",
+        "REFER", "ACK", "MESSAGE", "SUBSCRIBE",
+        "PUBLISH", "UPDATE", "RESPONSE", "INFO", "UNKNOWN"
+    };
+
+
+    idx = ino - (int) sipMethodRegister;
+    if (idx >= 0 && idx <= (int) (sizeof(methods) / sizeof(methods[0]) - 1)) {
+        return methods[idx]; // Its OK to send this as it is a static array
+    } else {
+        return NULL;
+    }
+}
+
+sipRet_t
+sipSPIAddRequestLine (ccsipCCB_t *ccb, sipMessage_t *request,
+                      sipMethod_t methodname, boolean initInvite)
+{
+    sipRet_t tflag = STATUS_FAILURE;
+
+    if (TRUE == sipSPIGenRequestURI(ccb, methodname, initInvite)) {
+        tflag = sippmh_add_request_line(request,
+                                        sipGetMethodString(methodname),
+                                        ccb->ReqURI,
+                                        SIP_VERSION);
+    } else {
+        tflag = STATUS_FAILURE;
+    }
+    return tflag;
+}
+
+boolean
+getCSeqInfo (sipMessage_t *request, sipCseq_t ** request_cseq_structure)
+{
+    const char *request_cseq = NULL;
+
+    request_cseq = sippmh_get_cached_header_val(request, CSEQ);
+    if (!request_cseq) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          "AddCSeq in Factory",
+                          "sippmh_get_cached_header_val()");
+        return (FALSE);
+    }
+    *request_cseq_structure = sippmh_parse_cseq(request_cseq);
+    if (!*request_cseq_structure) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          "AddCSeq in Factory",
+                          "sippmh_parse_cseq()");
+        return (FALSE);
+    }
+    return TRUE;
+}
+
+// Allocate an outgoing transaction and fill in the cseq number and method
+boolean
+allocateTrx (ccsipCCB_t *ccb, sipMethod_t method)
+{
+    uint32_t trx_cseq_number = 0;
+    int16_t  new_trx_index = -1, last_trx_index = -1;
+
+    if (method == sipMethodCancel) {
+        // For CANCEL, the cseq is the same as the cseq
+        // of the earlier sent request that it cancels
+        last_trx_index = get_last_request_trx_index(ccb, TRUE);
+        if (last_trx_index < 0) {
+            return FALSE;
+        }
+        trx_cseq_number = ccb->sent_request[last_trx_index].cseq_number;
+        // Now allocate a new CSeq for the CANCEL request itself
+        new_trx_index = get_next_request_trx_index(ccb, TRUE);
+        if (new_trx_index < 0) {
+            return FALSE;
+        }
+        ccb->sent_request[new_trx_index].cseq_number = trx_cseq_number;
+        ccb->sent_request[new_trx_index].cseq_method = sipMethodCancel;
+
+    } else if (method != sipMethodAck) {
+        // No new transaction is needed for Ack
+        new_trx_index = get_next_request_trx_index(ccb, TRUE);
+        if (new_trx_index < 0) {
+            return FALSE;
+        }
+        ccb->sent_request[new_trx_index].cseq_number = ++(ccb->last_used_cseq);
+        ccb->sent_request[new_trx_index].cseq_method = method;
+    }
+    return TRUE;
+}
+
+boolean
+AddCSeq (ccsipCCB_t *ccb,
+         sipMessage_t *request,
+         boolean isResponse,
+         sipMethod_t method,
+         uint32_t response_cseq_number)
+{
+    uint32_t request_cseq_number = 0;
+    sipRet_t tflag = STATUS_FAILURE;
+    int16_t  trx_index = -1;
+
+    if (TRUE == isResponse) {
+        if (response_cseq_number == 0) {
+            trx_index = get_method_request_trx_index(ccb, method, FALSE);
+            if (trx_index != -1) {
+                request_cseq_number = ccb->recv_request[trx_index].cseq_number;
+            } else {
+                return FALSE;
+            }
+        } else {
+            request_cseq_number = response_cseq_number;
+        }
+    } else {
+        // For Request
+        if (method == sipMethodAck) {
+            request_cseq_number = response_cseq_number;
+        } else {
+            // Pull up the previously allocated transaction
+            trx_index = get_last_request_trx_index(ccb, TRUE);
+            if (trx_index < 0) {
+                return FALSE;
+            }
+            request_cseq_number = ccb->sent_request[trx_index].cseq_number;
+        }
+    }
+    tflag = sippmh_add_cseq(request, sipGetMethodString(method),
+                            request_cseq_number);
+    if (tflag != HSTATUS_SUCCESS) {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ *   This will generate the most common headers for every message
+ */
+
+sipRet_t
+sipSPIAddCommonHeaders (ccsipCCB_t *ccb,
+                        sipMessage_t *request,
+                        boolean isResponse,
+                        sipMethod_t method,
+                        uint32_t response_cseq_number)
+{
+    sipRet_t tflag = STATUS_FAILURE;
+
+    tflag = (sipSPIAddStdHeaders(request, ccb, isResponse)) ?
+        STATUS_SUCCESS : STATUS_FAILURE;
+    if (tflag != HSTATUS_SUCCESS) {
+        return tflag;
+    }
+
+    // Add date header
+    tflag = sipAddDateHeader(request);
+    if (tflag != HSTATUS_SUCCESS) {
+        return tflag;
+    }
+
+    // Add CSEQ
+    if (AddCSeq(ccb, request, isResponse, method, response_cseq_number)
+            == FALSE) {
+        return STATUS_FAILURE;
+    }
+
+    return HSTATUS_SUCCESS;
+}
+
+boolean
+is_extended_feature (ccsipCCB_t *ccb)
+{
+    if (ccb) {
+        switch (ccb->featuretype) {
+        case CC_FEATURE_B2BCONF:
+        case CC_FEATURE_CANCEL:
+            return TRUE;
+        default:
+            return FALSE;
+        }
+    }
+    return FALSE;
+}
+
+boolean
+sipSPIGenRequestURI (ccsipCCB_t *ccb, sipMethod_t sipmethod, boolean initInvite)
+{
+    sipUrl_t   *sipUrl = NULL;
+    int         i = 0;
+    const char *fname = "sipSPIGenRequestURI";
+    char        dest_sip_addr_str[MAX_IPADDR_STR_LEN];
+    char       *domainloc;
+    genUrl_t   *gen;
+    int         j = 0;
+    boolean     lr = FALSE, uriAdded = FALSE;
+    char        hdr_str[MAX_SIP_URL_LENGTH];
+
+    /*
+     * Construct the request URI
+     */
+    // Special case for Initial Invite and Cancel messgae for creating
+    // the Request URI
+    if ((sipMethodInvite == sipmethod) && (initInvite == TRUE)) {
+        // This is the first Invite initiated so Request URI is
+        // built differently.
+        if (ccb->calledNumber[0] == '<') {
+            // Remove leading '<'
+            sstrncpy(ccb->ReqURI, ccb->calledNumber + 1, MAX_SIP_URL_LENGTH);
+        }
+        // If there is no hostname, add proxy address as hostname
+        domainloc = strchr(ccb->ReqURI, '@');
+        if (domainloc == NULL) {
+            domainloc = ccb->ReqURI + strlen(ccb->ReqURI);
+            if ((domainloc - ccb->ReqURI) < (MAX_SIP_URL_LENGTH - 1)) {
+                /*
+                 * We need to check and see if we are already truncating a
+                 * string string that goes into ReqURI.  If we are, then we
+                 * CANNOT add any more characters without overwriting memory
+                 */
+                *domainloc++ = '@';
+                sstrncpy(dest_sip_addr_str, ccb->reg.proxy,
+                        MAX_IPADDR_STR_LEN);
+
+                if (ccb->reg.addr.type == CPR_IP_ADDR_IPV6) {
+                    *domainloc++ = '[';
+                }
+
+                sstrncpy(domainloc, dest_sip_addr_str,
+                         MAX_SIP_URL_LENGTH - (domainloc - ccb->ReqURI));
+
+                if (ccb->reg.addr.type == CPR_IP_ADDR_IPV6) {
+                    *domainloc++ = ']';
+                }
+            }
+        }
+        // Remove trailing '>'
+        domainloc = strchr(ccb->ReqURI, '>');
+        if (domainloc) {
+            *domainloc = '\0';
+        }
+        return TRUE;
+    } else if ((sipMethodCancel == sipmethod) ||
+               ((sipMethodAck == sipmethod) &&
+                (gCallHistory[ccb->index].last_rspcode_rcvd > codeClass2xx))) {
+        /* Use the same REQ URI that was created on the INVITE
+         * Replace the ReqURI with the contents of ReqURIOriginal
+         */
+        if (ccb->ReqURIOriginal[0] != '\0') {
+            sstrncpy(ccb->ReqURI, ccb->ReqURIOriginal, MAX_SIP_URL_LENGTH);
+        }
+        return TRUE;
+    }
+
+    if (sipMethodRegister == sipmethod ||
+        ((sipMethodRefer == sipmethod) &&
+         (ccb->type == SIP_REG_CCB || is_extended_feature(ccb)))) {
+        // If it is REGISTER or a token-registration REFER, the URI is formed
+        // simply by the destination IP address. This is also the case when
+        // sending a REFER for one of the softkey functions for TNP
+        if (ccb->reg.proxy[0] == '\0') {
+            ipaddr2dotted(dest_sip_addr_str, &ccb->dest_sip_addr);
+        } else {
+            sstrncpy(dest_sip_addr_str, ccb->reg.proxy, MAX_IPADDR_STR_LEN);
+        }
+        if (ccb->dest_sip_addr.type == CPR_IP_ADDR_IPV6) {
+
+            snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH, "sip:[%s]",
+                     dest_sip_addr_str); /* proxy */
+        } else {
+            snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH, "sip:%s",
+                     dest_sip_addr_str); /* proxy */
+        }
+
+        return TRUE;
+    } else {
+        if (ccb->record_route_info) {
+            /*
+             * If loose-routing is being used, use the remote contact in
+             * REQ-URI
+             */
+            if (ccb->flags & INCOMING) {
+                i = 0;
+            } else {
+                i = ccb->record_route_info->num_locations - 1;
+            }
+
+            if (ccb->record_route_info->locations[i]->genUrl->schema
+                    == URL_TYPE_SIP) {
+                if (ccb->record_route_info->locations[i]->genUrl->u.sipUrl->lr_flag) {
+                    lr = TRUE;
+                }
+            }
+            if (!lr) {
+                CCSIP_DEBUG_STATE(DEB_F_PREFIX"Strict Routing: Forming Req-URI using "
+                                  "Record Route\n", DEB_F_PREFIX_ARGS(SIP_REQ_URI, fname));
+                if (ccb->record_route_info->locations[i]->genUrl->schema
+                        == URL_TYPE_SIP) {
+                    sipUrl = ccb->record_route_info->locations[i]->genUrl->u.sipUrl;
+                } else {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR),
+                                      fname);
+                    return (FALSE);
+                }
+
+                if (sipUrl->user != NULL) {
+                    if (sipUrl->password) {
+                        if (sipUrl->port_present) {
+                             snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                                 sipUrl->is_phone ?
+                                     "sip:%s:%s@%s:%d;user=phone" :
+                                     "sip:%s:%s@%s:%d",
+                                 sipUrl->user, sipUrl->password,
+                                 sipUrl->host, sipUrl->port);
+                        } else {
+                             snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                                 sipUrl->is_phone ?
+                                     "sip:%s:%s@%s;user=phone" :
+                                     "sip:%s:%s@%s",
+                                 sipUrl->user, sipUrl->password,
+                                 sipUrl->host);
+                        }
+                    } else {
+                        if (sipUrl->port_present) {
+                             snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                                 sipUrl->is_phone ?
+                                     "sip:%s@%s:%d;user=phone" : "sip:%s@%s:%d",
+                                 sipUrl->user, sipUrl->host, sipUrl->port);
+                        } else {
+                             snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                                 sipUrl->is_phone ?
+                                     "sip:%s@%s;user=phone" : "sip:%s@%s",
+                                 sipUrl->user, sipUrl->host);
+                        }
+                    }
+                } else {
+                    if (sipUrl->port_present) {
+                       snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                             sipUrl->is_phone ?
+                                 "sip:%s:%d;user=phone" : "sip:%s:%d",
+                             sipUrl->host, sipUrl->port);
+                     } else {
+                       snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                             sipUrl->is_phone ?
+                                 "sip:%s;user=phone" : "sip:%s",
+                             sipUrl->host);
+                     }
+                }
+                uriAdded = TRUE;
+            }
+        }
+
+        if (!uriAdded) {
+            if ((ccb->contact_info) &&
+                (lr || (ccb->state >= SIP_STATE_SENT_INVITE_CONNECTED))) {
+                // Use Contact info ONLY if loose routing or
+                // if we're fully connected - otherwise use proxy
+                CCSIP_DEBUG_STATE(DEB_F_PREFIX"Forming Req-URI: using Contact\n",
+                                  DEB_F_PREFIX_ARGS(SIP_REQ_URI, fname));
+
+                if ((sipMethodInvite == sipmethod) &&
+                    (ccb->redirect_info) &&
+                    (ccb->state == SIP_STATE_SENT_INVITE)) {
+                    sipUrl = ccb->redirect_info->sipContact->locations
+                        [ccb->redirect_info->next_choice - 1]->genUrl->u.sipUrl;
+                } else if (ccb->contact_info->locations[0]->genUrl->schema
+                        == URL_TYPE_SIP) {
+                    sipUrl = ccb->contact_info->locations[0]->genUrl->u.sipUrl;
+                } else {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR),
+                                      fname);
+                    return (FALSE);
+                }
+                if (sipUrl->user != NULL) {
+                    if (sipUrl->password) {
+                        snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                                 sipUrl->is_phone ?
+                                     "sip:%s:%s@%s:%d;user=phone" :
+                                     "sip:%s:%s@%s:%d",
+                                 sipUrl->user, sipUrl->password,
+                                 sipUrl->host, sipUrl->port);
+                    } else {
+                        snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                                 sipUrl->is_phone ?
+                                     "sip:%s@%s:%d;user=phone" :
+                                     "sip:%s@%s:%d",
+                                 sipUrl->user, sipUrl->host, sipUrl->port);
+                    }
+                } else {
+                    snprintf(ccb->ReqURI, MAX_SIP_URL_LENGTH,
+                             sipUrl->is_phone ?
+                                 "sip:%s:%d;user=phone" :
+                                 "sip:%s:%d",
+                             sipUrl->host, sipUrl->port);
+                }
+            } else {
+                if (ccb->flags & INCOMING) {
+                    CCSIP_DEBUG_STATE(DEB_F_PREFIX"Forming Req-URI (Callee): using "
+                                      "From\n", DEB_F_PREFIX_ARGS(SIP_REQ_URI, fname));
+                    if (ccb->sip_from) {
+                        sstrncpy(hdr_str, ccb->sip_from, MAX_SIP_URL_LENGTH);
+                        sstrncpy(ccb->ReqURI, sippmh_get_url_from_hdr(hdr_str), MAX_SIP_URL_LENGTH);
+                    }
+                } else {
+                    CCSIP_DEBUG_STATE(DEB_F_PREFIX"Forming Req-URI (Caller): using "
+                                      "original Req-URI\n", DEB_F_PREFIX_ARGS(SIP_REQ_URI, fname));
+                    sstrncpy(ccb->ReqURI, ccb->ReqURIOriginal,
+                            MAX_SIP_URL_LENGTH);
+                }
+            }
+        }
+
+        // Check to see the transport parameter specified in sipUrl.
+        // If this is different than UDP, add it to the URI
+        if (sipUrl) {
+            switch (sipUrl->transport) {
+            case TRANSPORT_UDP:
+                sstrncat(ccb->ReqURI, ";transport=udp",
+                        sizeof(ccb->ReqURI) - strlen(ccb->ReqURI));
+                break;
+            case TRANSPORT_TCP:
+                sstrncat(ccb->ReqURI, ";transport=tcp",
+                        sizeof(ccb->ReqURI) - strlen(ccb->ReqURI));
+                break;
+            case TRANSPORT_TLS:
+                sstrncat(ccb->ReqURI, ";transport=tls",
+                        sizeof(ccb->ReqURI) - strlen(ccb->ReqURI));
+                break;
+            case TRANSPORT_SCTP:
+                sstrncat(ccb->ReqURI, ";transport=sctp",
+                        sizeof(ccb->ReqURI) - strlen(ccb->ReqURI));
+                break;
+            default:
+                break;
+            }
+        }
+        if (ccb->record_route_info) {
+            /*
+             * Look for any unknown params that might have been
+             * glued onto the URL
+             */
+            gen = ccb->record_route_info->locations[i]->genUrl;
+            while (j < SIP_MAX_LOCATIONS) {
+                if (gen->other_params[j] != NULL) {
+                    sstrncat(ccb->ReqURI, ";",
+                            sizeof(ccb->ReqURI) - strlen(ccb->ReqURI));
+                    sstrncat(ccb->ReqURI, gen->other_params[j],
+                            sizeof(ccb->ReqURI) - strlen(ccb->ReqURI));
+                    break;
+                }
+                j++;
+            }
+        }
+    }
+    return TRUE;
+}
+
+sipRet_t
+sipSPIAddContactHeader (ccsipCCB_t *ccb, sipMessage_t *request)
+{
+    char        pContactStr[MAX_SIP_URL_LENGTH];
+    char        src_addr_str[MAX_IPADDR_STR_LEN];
+    char        line_name[MAX_LINE_NAME_SIZE];
+    char        reg_user_info[MAX_REG_USER_INFO_LEN];
+    int         rpid_flag = RPID_DISABLED;
+    int         blocking;
+    uint8_t     mac_address[MAC_ADDRESS_LENGTH];
+    char        device_instance[MAX_SIP_TAG_LENGTH];
+    char        contact[MAX_LINE_CONTACT_SIZE];
+    size_t      escaped_char_str_len;
+    const char *transport_type_str;
+    int size;
+    char        sipDeviceName[MAX_REG_USER_INFO_LEN];
+
+    config_get_value(CFGID_REMOTE_PARTY_ID, &rpid_flag, sizeof(rpid_flag));
+
+    config_get_value(CFGID_DEVICE_NAME, sipDeviceName, sizeof(sipDeviceName));
+
+    /* get reg_user_info and pk-id */
+    contact[0] = '\0';
+    config_get_string(CFGID_REG_USER_INFO, reg_user_info,
+                      sizeof(reg_user_info));
+    config_get_line_string(CFGID_LINE_CONTACT, contact, ccb->dn_line,
+                           sizeof(contact));
+
+
+    ipaddr2dotted(src_addr_str, &ccb->src_addr);
+
+    config_get_value(CFGID_CALLERID_BLOCKING, &blocking, sizeof(blocking));
+
+    transport_type_str = sipTransportGetTransportType(ccb->dn_line, FALSE, ccb);
+
+    /*
+     * If caller id blocking is enabled and phone
+     * is configured to use RPID then userinfo should
+     * be random identifier same as that used in from
+     * header
+     */
+    if ((blocking & 1) && (rpid_flag == RPID_ENABLED) &&
+        (ccb->type != SIP_REG_CCB)) {
+        sstrncpy(line_name, SIP_HEADER_ANONYMOUS_STR, MAX_LINE_NAME_SIZE);
+    } else {
+        config_get_line_string(CFGID_LINE_NAME, line_name, ccb->dn_line,
+                               sizeof(line_name));
+    }
+
+    if (ccb->type == SIP_REG_CCB) {
+        snprintf(pContactStr, 6, "<sip:");
+        if ((cpr_strcasecmp(contact, UNPROVISIONED) != 0) &&
+            (contact[0] != '\0')) {
+            escaped_char_str_len =
+                sippmh_convertURLCharToEscChar(contact, strlen(contact),
+                                               pContactStr + 5,
+                                               (MAX_SIP_URL_LENGTH - 5),
+                                               FALSE);
+        } else {
+            escaped_char_str_len =
+                sippmh_convertURLCharToEscChar(line_name,
+                                               strlen(line_name),
+                                               pContactStr + 5,
+                                               (MAX_SIP_URL_LENGTH - 5),
+                                               FALSE);
+        }
+        if (cpr_strcasecmp(reg_user_info, "none") == 0) {
+            snprintf(pContactStr + 5 + escaped_char_str_len,
+                     sizeof(pContactStr) - 5 - escaped_char_str_len,
+                     "@%s:%d;transport=%s>", src_addr_str,
+                     ccb->local_port, transport_type_str);
+        } else {
+            snprintf(pContactStr + 5 + escaped_char_str_len,
+                     sizeof(pContactStr) - 5 - escaped_char_str_len,
+                     "@%s:%d;user=%s;transport=%s>",
+                     src_addr_str, ccb->local_port, reg_user_info,
+                     transport_type_str);
+        }
+
+        // Add the instance ID for unique device identification
+        // The format is as follows:
+        // Contact: <sip:line1@192.168.0.2>
+        // ;+sip.instance="<urn:uuid:00000000-0000-0000-0000-000A95A0E128>"
+        // ;+u.sip!model.ccm.cisco.com="336"
+        // where the 000A95A0E128 is the MAC address of the phone and
+        // the 336 is the model number of the phone
+        // All the 000's are supposed to encode callee capabilities (RFC 3840)
+        // but we are leaving them 000 for now as no-one reads them
+        platform_get_active_mac_address(mac_address);
+        memset(device_instance, '\0', sizeof(device_instance));
+        snprintf(device_instance, MAX_SIP_TAG_LENGTH,
+                 ";+sip.instance=\"<urn:uuid:00000000-0000-0000-0000-%.4x%.4x%.4x>\"",
+                 mac_address[0] * 256 + mac_address[1],
+                 mac_address[2] * 256 + mac_address[3],
+                 mac_address[4] * 256 + mac_address[5]);
+        size = MAX_SIP_URL_LENGTH - strlen(pContactStr);
+        if (size > (int)strlen(device_instance)) {
+            sstrncat(pContactStr, device_instance, size);
+        }
+        // Add the instance ID for unique device identification
+        // The format is as follows:
+        // Contact: <sip:5b1d1cd7-4e1b-99ae-665a-efdf0d96a7a2@172.18.199.196:52046;
+        //transport=tcp>;+sip.instance="<urn:uuid:00000000-0000-0000-0000-0019e89a7f3e>";
+        //+u.sip!devicename.ccm.cisco.com="SEP0019E89A7F3D";
+        //++u.sip!model.ccm.cisco.com="30006"
+        // where the 000A95A0E128 is the MAC address of the phone and
+        // the 336 is the model number of the phone
+        // All the 000's are supposed to encode callee capabilities (RFC 3840)
+        // but we are leaving them 000 for now as no-one reads them
+        platform_get_wired_mac_address(mac_address);
+        memset(device_instance, '\0', sizeof(device_instance));
+        snprintf(device_instance, MAX_SIP_TAG_LENGTH,
+                 ";+sip.instance=\"<urn:uuid:00000000-0000-0000-0000-%.4x%.4x%.4x>\""
+                ";+u.sip!devicename.ccm.cisco.com=\"%s\""
+                ";+u.sip!model.ccm.cisco.com=\"%s\"",
+                 mac_address[0] * 256 + mac_address[1],
+                 mac_address[2] * 256 + mac_address[3],
+                 mac_address[4] * 256 + mac_address[5],
+                 sipDeviceName,
+                 sipPhoneModelNumber);
+        size = MAX_SIP_URL_LENGTH - strlen(pContactStr);
+        if (size > (int)strlen(device_instance)) {
+            sstrncat(pContactStr, device_instance, size);
+        }
+        // add tag cisco-keep-alive for keep alive messages in ccm mode
+        if ((ccb->cc_type == CC_CCM) && (ccb->index >= REG_BACKUP_CCB)) {
+            sipMethod_t method = sipMethodInvalid;
+
+            sipGetRequestMethod(request, &method);
+            if (method == sipMethodRegister) {
+                sstrncat(pContactStr, ";expires=0;cisco-keep-alive",
+                        sizeof(pContactStr) - strlen(pContactStr));
+            }
+        }
+    } else {
+        char *forward_url = NULL;
+
+        forward_url = Basic_is_phone_forwarded(ccb->dn_line);
+        /* only use the forward URL if we are sending a 302 */
+        if ((forward_url) &&
+            (strstr(request->mesg_line, SIP_RED_MOVED_TEMP_PHRASE))) {
+            char *user_info = strchr(forward_url, '@');
+
+            /*
+             * forward_url will always have domain/host address preceded
+             * by @ following the user, returned by
+             * Basic_is_phone_forwarded. so no need to check for
+             * user_info == NULL
+             */
+            snprintf(pContactStr, 6, "<sip:");
+            escaped_char_str_len =
+                sippmh_convertURLCharToEscChar(forward_url,
+                                               user_info - forward_url, // len of user
+                                               pContactStr + 5,
+                                               (MAX_SIP_URL_LENGTH - 5),
+                                               FALSE);
+            snprintf(pContactStr + 5 + escaped_char_str_len,
+                     sizeof(pContactStr) - 5 - escaped_char_str_len, "%s>",
+                     user_info);
+        } else {
+            /*
+             * Use the contact value supplied to us, if available.
+             * If not, use name
+             */
+            snprintf(pContactStr, 6, "<sip:");
+            if ((cpr_strcasecmp(contact, UNPROVISIONED) != 0) &&
+                (contact[0] != '\0')) {
+                escaped_char_str_len =
+                    sippmh_convertURLCharToEscChar(contact, strlen(contact),
+                                                   pContactStr + 5,
+                                                   (MAX_SIP_URL_LENGTH - 5),
+                                                   FALSE);
+            } else {
+                escaped_char_str_len =
+                    sippmh_convertURLCharToEscChar(line_name, strlen(line_name),
+                                                   pContactStr + 5,
+                                                   (MAX_SIP_URL_LENGTH - 5),
+                                                   FALSE);
+            }
+
+            if (cpr_strcasecmp(reg_user_info, "none") == 0) {
+                snprintf(pContactStr + 5 + escaped_char_str_len,
+                         sizeof(pContactStr) - 5 - escaped_char_str_len,
+                         "@%s:%d;transport=%s>", src_addr_str,
+                         ccb->local_port, transport_type_str);
+            } else {
+                snprintf(pContactStr + 5 + escaped_char_str_len,
+                         sizeof(pContactStr) - 5 - escaped_char_str_len,
+                         "@%s:%d;user=%s;transport=%s>",
+                         src_addr_str, ccb->local_port,
+                         reg_user_info, transport_type_str);
+            }
+        }
+    }
+    return (sippmh_add_text_header(request, SIP_HEADER_CONTACT, pContactStr));
+}
+
+/**
+ * Convert phone name to upper case
+ *
+ * @param phone_name - phone name
+ *
+ * @return status none
+ *
+ * @pre none
+ */
+void convert_phone_name_to_upper_case(char *phone_name)
+{
+    while (phone_name && (*phone_name) != '\0') {
+        *phone_name = (char)toupper(*phone_name);
+        phone_name++;
+    }
+}
+/**
+ * Add reason header to SIP message
+ *
+ * eg: Reason: SIP;cause=200;text="cisco:22 Name=SEP000000000000
+ * Load=SIP70.8-2-25 Last=reset-reset
+ *
+ * @param ccb       call control block
+ * @param request   sip message
+ *
+ * @return status
+ *         success: STATUS_SUCCESS
+ *         failure: STATUS_FAILURE
+ *
+ * @pre  (ccb      not_eq NULL)
+ * @pre  (request  not_eq NULL)
+ *
+ */
+sipRet_t
+sipSPIAddReasonHeader (ccsipCCB_t *ccb, sipMessage_t *request)
+{
+    const char *fname = "sipSPIAddReasonHeader";
+    char        pReasonStr[MAX_SIP_HEADER_LENGTH];
+    uint8_t     mac_address[MAC_ADDRESS_LENGTH];
+    char        phone_name[MAX_PHONE_NAME_LEN];
+    char        image_a[MAX_LOAD_ID_STRING];
+    char        image_b[MAX_LOAD_ID_STRING];
+    int         active_partition;
+    int         unreg_reason_code = 0;
+    char        unreg_reason_str[MAX_UNREG_REASON_STR_LEN];
+
+    if (ccb->send_reason_header) {
+        // should only be set when the phone is registering after a restart/reset
+        platform_get_wired_mac_address(mac_address);
+
+        snprintf(phone_name, MAX_PHONE_NAME_LEN, "SEP%04x%04x%04x", mac_address[0] * 256 + mac_address[1],
+                                                                    mac_address[2] * 256 + mac_address[3],
+                                                                    mac_address[4] * 256 + mac_address[5]);
+
+        convert_phone_name_to_upper_case(phone_name);
+
+
+        unreg_reason_code = platGetUnregReason();
+
+        unreg_reason_str[0] = '\0';
+        get_reason_string(unreg_reason_code, unreg_reason_str, MAX_UNREG_REASON_STR_LEN);
+        active_partition = platGetActiveInactivePhoneLoadName(image_a, image_b, MAX_LOAD_ID_STRING);
+        snprintf(pReasonStr, MAX_SIP_HEADER_LENGTH,
+            "SIP;cause=200;text=\"cisco-alarm:%d Name=%s ActiveLoad=%s InactiveLoad=%s Last=%s",
+            unreg_reason_code, phone_name, (active_partition == 1) ?  image_a:image_b,
+            (active_partition == 1) ? image_b:image_a, unreg_reason_str);
+        sstrncat(pReasonStr, "\"",
+                MAX_SIP_HEADER_LENGTH - strlen(pReasonStr) - 1);
+        return (sippmh_add_text_header(request, SIP_HEADER_REASON, pReasonStr));
+    } else {
+        CCSIP_DEBUG_ERROR("%s called with send_reason_header set to false\n", fname);
+        return (STATUS_SUCCESS);
+    }
+}
+
+/**
+ * Return the reason string that is to be returned
+ * corresponding to the unreg reason that is to be sent
+ * in the register message.
+ *
+ * @param unreg_reason    - unreg reason code
+ * @param char *          - reason string corresponding to the code
+ * @return none
+ *
+ */
+void
+get_reason_string (int unreg_reason, char *unreg_reason_str, int len)
+{
+
+    switch(unreg_reason) {
+        case UNREG_REASON_RESET_RESTART:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "reset-restart");
+            break;
+        case UNREG_REASON_RESET_RESET:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "reset-reset");
+            break;
+        case UNREG_REASON_PHONE_INITIALIZED:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "initialized");
+            break;
+        case UNREG_REASON_REG_TIMEOUT:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "reg-timeout");
+            break;
+        case UNREG_REASON_PHONE_KEYPAD:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "phone-keypad");
+            break;
+        case UNREG_REASON_PHONE_REG_REJ:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "phone-reg-rej");
+            break;
+        case UNREG_REASON_FALLBACK:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "fallback");
+            break;
+        case UNREG_REASON_VERSION_STAMP_MISMATCH:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "version-stamp-mismatch(%s)", sipUnregisterReason);
+            break;
+        case UNREG_REASON_VERSION_STAMP_MISMATCH_CONFIG:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "version-stamp-mismatch-config");
+            break;
+        case UNREG_REASON_VERSION_STAMP_MISMATCH_SOFTKEY:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "version-stamp-mismatch-softkey");
+            break;
+        case UNREG_REASON_VERSION_STAMP_MISMATCH_DIALPLAN:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "version-stamp-mismatch-dialplan");
+            break;
+        case UNREG_REASON_CONFIG_RETRY_RESTART:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "config-retry-restart");
+            break;
+        case UNREG_REASON_TLS_ERROR:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "tls-error");
+            break;
+        case UNREG_REASON_TCP_TIMEOUT:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "tcp_timeout");
+            break;
+        case UNREG_REASON_CM_CLOSED_TCP:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "cm-closed-tcp");
+            break;
+        case UNREG_REASON_CM_RESET_TCP:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "cm-reset-tcp");
+            break;
+        case UNREG_REASON_CM_ABORTED_TCP:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "cm-aborted-tcp");
+            break;
+        case UNREG_REASON_APPLY_CONFIG_RESTART:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "apply_config");
+            break;
+        case UNREG_REASON_VOICE_VLAN_CHANGED:
+            snprintf(unreg_reason_str, MAX_UNREG_REASON_STR_LEN, "VLAN-Changed");
+            break;
+        default:
+            unreg_reason_str[0] = '\0';
+            CCSIP_DEBUG_ERROR("Unkown unreg reason code passed\n");
+            break;
+    }
+}
+boolean
+CreateRequest (ccsipCCB_t *ccb, sipMessageFlag_t messageflag,
+               sipMethod_t sipmethod, sipMessage_t *request,
+               boolean initInvite, uint32_t response_cseq_number)
+{
+    sipRet_t tflag = STATUS_FAILURE;
+
+    if (!request) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          "CreateRequest", "GET_SIP_MESSAGE()");
+        return FALSE;
+    }
+
+
+    if (sipMethodResponse != sipmethod) {
+        if (sipSPIAddRequestLine(ccb, request, sipmethod, initInvite)
+                == STATUS_FAILURE) {
+            return FALSE;
+        }
+    }
+
+    ccb->outBoundProxyPort = 0;
+    ccb->outBoundProxyAddr = ip_addr_invalid;
+    if (ccb->ObpSRVhandle != NULL) {
+        dnsFreeSrvHandle(ccb->ObpSRVhandle);
+        ccb->ObpSRVhandle = NULL;
+    }
+
+    tflag = (allocateTrx(ccb, sipmethod)) ? STATUS_SUCCESS : STATUS_FAILURE;
+
+    if (tflag == STATUS_SUCCESS) {
+        tflag = (sipSPIAddLocalVia(request, ccb, sipmethod)) ?
+            STATUS_SUCCESS : STATUS_FAILURE;
+        /* Don't stop adding headers to a Register just because
+         * the VIA line wasn't added. A Register doesn't really
+         * need the VIA line anyways.
+         */
+        if ((HSTATUS_SUCCESS != tflag) && (ccb->type != SIP_REG_CCB)) {
+            return FALSE;
+        }
+    }
+
+    if (tflag == STATUS_SUCCESS) {
+        tflag = sipSPIAddCommonHeaders(ccb, request, FALSE, sipmethod,
+                                       response_cseq_number);
+    }
+
+    if (tflag != HSTATUS_SUCCESS) {
+        return FALSE;
+    }
+
+    tflag = sippmh_add_text_header(request, SIP_HEADER_USER_AGENT,
+                                   sipHeaderUserAgent);
+
+    if (tflag != HSTATUS_SUCCESS) {
+        return FALSE;
+    }
+    return AddGeneralHeaders(ccb, messageflag, request, sipmethod);
+}
+
+boolean
+CreateResponse (ccsipCCB_t *ccb,
+               sipMessageFlag_t messageflag,
+               uint16_t status_code,
+               sipMessage_t *response,
+               const char *reason_phrase,
+               uint16_t status_code_warning,
+               const char *reason_phrase_warning,
+               sipMethod_t method)
+{
+    sipRet_t tflag = HSTATUS_SUCCESS;
+    char    *warning = NULL;
+    uint32_t response_cseq_number = 0;
+
+    if (!ccb) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          "CreateResponse", "ccb");
+        return FALSE;
+    }
+    if (!ccb->last_request) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          "Create Response", "ccb->last_request");
+        return FALSE;
+    }
+    if (!response) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          "CreateResponse", "GET_SIP_MESSAGE()");
+        return FALSE;
+    }
+
+    tflag = sippmh_add_response_line(response, SIP_VERSION, status_code,
+                                     reason_phrase);
+
+    if (tflag != HSTATUS_SUCCESS)
+        return FALSE;
+
+    tflag = (sipSPIAddRequestVia(ccb, response, ccb->last_request, method)) ?
+        STATUS_SUCCESS : STATUS_FAILURE;
+
+    if (tflag != HSTATUS_SUCCESS)
+        return FALSE;
+
+    response_cseq_number = 0;
+
+    tflag = sipSPIAddCommonHeaders(ccb, response, TRUE, method,
+                                   response_cseq_number);
+    if (tflag != HSTATUS_SUCCESS)
+        return FALSE;
+
+    if (reason_phrase_warning) {
+        warning = (char *) cpr_malloc(strlen(reason_phrase_warning) + 5);
+        if (warning) {
+            snprintf(warning, strlen(reason_phrase_warning) + 5,
+                     "%d %s", status_code_warning, reason_phrase_warning);
+            tflag = sippmh_add_text_header(response, SIP_HEADER_WARN, warning);
+            cpr_free(warning);
+            if (tflag != HSTATUS_SUCCESS)
+                return FALSE;
+        }
+    }
+
+    tflag = sippmh_add_text_header(response, SIP_HEADER_SERVER,
+                                   sipHeaderServer);
+    if (tflag != HSTATUS_SUCCESS) {
+        return FALSE;
+    }
+
+    return AddGeneralHeaders(ccb, messageflag, response, method);
+}
+
+/*************************************************************
+ * Function: SendRequest
+ * this function sends out a request pointed to by the request parameter
+ * in ccb's context.
+ *
+ * 'request' will be freed. The caller does not have the responsibility
+ * to free it.
+ **************************************************************/
+boolean
+SendRequest (ccsipCCB_t *ccb, sipMessage_t *request, sipMethod_t method,
+            boolean midcall, boolean reTx, boolean retranTimer)
+{
+    const char     *fname = "SendRequest";
+    cpr_ip_addr_t  cc_remote_ipaddr;
+    uint16_t       cc_remote_port = 0;
+    int            timeout = 0;
+    int            expires_timeout;
+    sipUrl_t       *sipUrl = NULL;
+    boolean        isRegister = FALSE;
+    int16_t        trx_index;
+    int            reldev_stored_msg = RELDEV_NO_STORED_MSG;
+
+    CPR_IP_ADDR_INIT(cc_remote_ipaddr);
+
+    if (sipMethodRegister == method) {
+        if (ccb->reg.proxy[0] == '\0') {
+            cc_remote_ipaddr = ccb->dest_sip_addr;
+            cc_remote_port = (uint16_t) ccb->dest_sip_port;
+        } else {
+            cc_remote_ipaddr = ccb->reg.addr;
+            cc_remote_port = ccb->reg.port;
+        }
+        config_get_value(CFGID_TIMER_T1, &timeout, sizeof(timeout));
+        isRegister = TRUE;
+
+    } else if ((sipMethodInvite == method) && (midcall == FALSE)) {
+        char *host;
+
+        if (!ccb->ReqURI) {
+            free_sip_message(request);
+            return FALSE;
+        }
+        host = strchr(ccb->ReqURI, '@');
+        if (!host) {
+            free_sip_message(request);
+            return FALSE;
+        }
+
+        /* Enable reTx and send */
+        if (TRUE == reTx) {
+            config_get_value(CFGID_TIMER_T1, &timeout, sizeof(timeout));
+        }
+        cc_remote_ipaddr = ccb->dest_sip_addr;
+        cc_remote_port = (uint16_t) ccb->dest_sip_port;
+    } else {
+        if ((ccb->record_route_info) && (sipMethodCancel != method)) {
+            int16_t i;
+
+            if (ccb->flags & INCOMING) {
+                i = 0;
+            } else {
+                i = ccb->record_route_info->num_locations - 1;
+            }
+
+            if (ccb->record_route_info->locations[i]->genUrl->schema
+                    == URL_TYPE_SIP) {
+                sipUrl = ccb->record_route_info->locations[i]->genUrl->u.sipUrl;
+            } else {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR), fname);
+                free_sip_message(request);
+                return (FALSE);
+            }
+
+            cc_remote_port = sipUrl->port;
+
+            if (!sipUrl->port_present) {
+                dns_error_code =
+                    sipTransportGetServerAddrPort(sipSPIUrlDestination(sipUrl),
+                                                  &cc_remote_ipaddr,
+                                                  &cc_remote_port,
+                                                  NULL, FALSE);
+            } else {
+                dns_error_code = dnsGetHostByName(sipSPIUrlDestination(sipUrl),
+                                                   &cc_remote_ipaddr, 100, 1);
+            }
+            if (dns_error_code == 0) {
+                util_ntohl(&cc_remote_ipaddr, &cc_remote_ipaddr);
+            } else {
+                cc_remote_ipaddr = ip_addr_invalid;
+            }
+
+        } else if ((ccb->contact_info) &&
+                   (ccb->state >= SIP_STATE_SENT_INVITE_CONNECTED)) {
+            /*
+             * If the call has been set up, we are free to use the
+             * contact header.  If the call has NOT been set up,
+             * drop through and use the proxy.
+             */
+            if (ccb->contact_info->locations[0]->genUrl->schema
+                    == URL_TYPE_SIP) {
+                sipUrl = ccb->contact_info->locations[0]->genUrl->u.sipUrl;
+            } else {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_URL_ERROR), fname);
+                free_sip_message(request);
+                return (FALSE);
+            }
+
+            cc_remote_port = sipUrl->port;
+
+            if (!sipUrl->port_present) {
+                dns_error_code =
+                    sipTransportGetServerAddrPort(sipSPIUrlDestination(sipUrl),
+                                                  &cc_remote_ipaddr,
+                                                  &cc_remote_port,
+                                                  NULL, FALSE);
+            } else {
+                dns_error_code = dnsGetHostByName(sipSPIUrlDestination(sipUrl),
+                                                   &cc_remote_ipaddr, 100, 1);
+            }
+            if (dns_error_code == 0) {
+
+                util_ntohl(&cc_remote_ipaddr, &cc_remote_ipaddr);
+            } else {
+                cc_remote_ipaddr = ip_addr_invalid;
+            }
+
+        } else {
+            cc_remote_ipaddr = ccb->dest_sip_addr;
+            cc_remote_port = (uint16_t) ccb->dest_sip_port;
+        }
+        if (TRUE == reTx)
+            config_get_value(CFGID_TIMER_T1, &timeout, sizeof(timeout));
+    }
+
+    if (util_check_if_ip_valid(&cc_remote_ipaddr) == FALSE) {
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // Update default destination address and port
+    ccb->dest_sip_addr = cc_remote_ipaddr;
+    ccb->dest_sip_port = cc_remote_port;
+
+    /*
+     * Store the ACK so that it can be retransmitted in response
+     * to any duplicate 200 OK or error responses
+     */
+    trx_index = get_last_request_trx_index(ccb, TRUE);
+    if (trx_index < 0) {
+        CCSIP_DEBUG_ERROR("%s: No Valid Trx found!\n", "SendRequest");
+        return (FALSE);
+    }
+    if (sipMethodAck == method) {
+        reldev_stored_msg =
+            sipRelDevCoupledMessageStore(request, ccb->sipCallID,
+                                         ccb->sent_request[trx_index].cseq_number,
+                                         ccb->sent_request[trx_index].cseq_method,
+                                         TRUE, ccb->last_recvd_response_code,
+                                         &cc_remote_ipaddr, cc_remote_port,
+                                         FALSE /* Do check tag */);
+    }
+    if (sipTransportCreateSendMessage(ccb, request, method,
+                                      &cc_remote_ipaddr, cc_remote_port,
+                                      isRegister, reTx, timeout, NULL,
+                                      reldev_stored_msg) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          "SendRequest", "sipTransportCreateSendMessage()");
+        //dont need message free here..sipTransportCreateSendMessage() will free it
+        return (FALSE);
+    }
+
+    /* Start INVITE expires timer */
+    if (retranTimer) {
+        config_get_value(CFGID_TIMER_INVITE_EXPIRES, &expires_timeout,
+                         sizeof(expires_timeout));
+        if (expires_timeout > 0) {
+            if (sip_platform_expires_timer_start(expires_timeout * 1000,
+                                                 ccb->index,
+                                                 &cc_remote_ipaddr,  //ccb->dest_sip_addr,
+                                                 cc_remote_port)    //ccb->dest_sip_port,
+                != SIP_OK) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  "SendRequest",
+                                  "sip_platform_expires_timer_start()");
+                return (FALSE);
+            }
+        }
+    }
+    // Save Call History
+    if (sipMethodCancel == method || sipMethodBye == method) {
+        gCallHistory[ccb->index].last_bye_cseq_number =
+            ccb->sent_request[trx_index].cseq_number;
+        gCallHistory[ccb->index].proxy_dest_ipaddr = ccb->dest_sip_addr;
+        gCallHistory[ccb->index].dn_line = ccb->dn_line;
+        sstrncpy(gCallHistory[ccb->index].via_branch,
+                 ccb->sent_request[trx_index].u.sip_via_branch,
+                 VIA_BRANCH_LENGTH);
+    }
+    return (TRUE);
+}
+
+boolean
+sendResponse (ccsipCCB_t *ccb,
+              sipMessage_t *response,
+              sipMessage_t *refrequest,
+              boolean retx,
+              sipMethod_t method)
+{
+    sipVia_t       *via = NULL;
+    const char     *request_callid = NULL;
+    sipCseq_t      *request_cseq_structure;
+    cpr_ip_addr_t  cc_remote_ipaddr;
+    uint16_t       cc_remote_port = 0;
+    int            timeout = 0;
+    const char     *pViaHeaderStr = NULL;
+    char           *dest_ip_addr_str = 0;
+    int16_t        trx_index = -1;
+    boolean        port_present = FALSE;
+    int            reldev_stored_msg;
+    int            status_code = 0;
+
+    CPR_IP_ADDR_INIT(cc_remote_ipaddr);
+
+    if (ccb) {
+        request_callid = ccb->sipCallID;
+        trx_index = get_method_request_trx_index(ccb, method, FALSE);
+        if (trx_index >= 0) {
+            pViaHeaderStr = (const char *)
+                (ccb->recv_request[trx_index].u.sip_via_header);
+            request_cseq_structure = (sipCseq_t *)
+                cpr_malloc(sizeof(sipCseq_t));
+            if (!request_cseq_structure) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  "Sendresponse", "malloc failed");
+                free_sip_message(response);
+                return (FALSE);
+            }
+            request_cseq_structure->method =
+                ccb->recv_request[trx_index].cseq_method;
+            request_cseq_structure->number =
+                ccb->recv_request[trx_index].cseq_number;
+        } else {
+            pViaHeaderStr =
+                sippmh_get_cached_header_val(ccb->last_request, VIA);
+            if (getCSeqInfo(ccb->last_request, &request_cseq_structure)
+                == FALSE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  "Sendresponse", "getCSeqInfo returned false");
+                free_sip_message(response);
+                return (FALSE);
+            }
+        }
+    } else {
+        pViaHeaderStr = sippmh_get_cached_header_val(refrequest, VIA);
+        request_callid = sippmh_get_cached_header_val(refrequest, CALLID);
+        if (FALSE == getCSeqInfo(refrequest, &request_cseq_structure)) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              "Sendresponse", "getCSeqInfo returned false");
+            free_sip_message(response);
+            return (FALSE);
+        }
+    }
+
+    via = sippmh_parse_via(pViaHeaderStr);
+    if (!via) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          "Sendresponse", "Bad Via Header in Message!");
+        cpr_free(request_cseq_structure);
+        free_sip_message(response);
+        return (FALSE);
+    }
+
+    if (via->remote_port) {
+        cc_remote_port = via->remote_port;
+        port_present = TRUE;
+    } else {
+        /* Use default 5060 if via does not have port */
+        cc_remote_port = SIP_WELL_KNOWN_PORT;
+    }
+
+    /*
+     * if maddr is present use it
+     */
+    if (via->maddr) {
+        if (!port_present) {
+            dns_error_code = sipTransportGetServerAddrPort(via->maddr,
+                                                           &cc_remote_ipaddr,
+                                                           &cc_remote_port,
+                                                           NULL, FALSE);
+        } else {
+            dns_error_code = dnsGetHostByName(via->maddr,
+                                               &cc_remote_ipaddr, 100, 1);
+        }
+        if (dns_error_code != 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              "Sendresponse",
+                              "sipTransportGetServerAddrPort or dnsGetHostByName");
+        } else {
+            util_ntohl(&cc_remote_ipaddr, &cc_remote_ipaddr);
+        }
+    }
+
+    /*
+     * if maddr isn't present, or the DNS lookup failed, send the
+     * response to the IP address we received the message from.
+     */
+    if (util_check_if_ip_valid(&cc_remote_ipaddr) == FALSE) {
+        if (via->recd_host) {
+            dest_ip_addr_str = via->recd_host;
+        } else {
+            dest_ip_addr_str = via->host;
+        }
+
+        if (!port_present) {
+            dns_error_code = sipTransportGetServerAddrPort(dest_ip_addr_str,
+                                                           &cc_remote_ipaddr,
+                                                           &cc_remote_port,
+                                                           NULL, FALSE);
+        } else {
+            dns_error_code = dnsGetHostByName(dest_ip_addr_str,
+                                               &cc_remote_ipaddr, 100, 1);
+        }
+        if (dns_error_code != 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              "Sendresponse",
+                              "sipTransportGetServerAddrPort or dnsGetHostByName");
+            cpr_free(request_cseq_structure);
+            sippmh_free_via(via);
+            free_sip_message(response);
+            return (FALSE);
+        } else {
+            util_ntohl(&cc_remote_ipaddr , &cc_remote_ipaddr);
+        }
+    }
+
+    sippmh_free_via(via);
+
+    reldev_stored_msg =
+        sipRelDevCoupledMessageStore(response, request_callid,
+                                     request_cseq_structure->number,
+                                     request_cseq_structure->method,
+                                     FALSE, status_code,
+                                     &cc_remote_ipaddr, cc_remote_port,
+                                     /* If responding to call setup don't check tag */
+                                     (boolean)(ccb != NULL ?
+                                          SIP_SM_CALL_SETUP_RESPONDING(ccb) :
+                                          FALSE));
+    cpr_free(request_cseq_structure);
+    /* Enable reTx and send */
+    if (retx) {
+        config_get_value(CFGID_TIMER_T1, &timeout, sizeof(timeout));
+        if (ccb) {
+            ccb->retx_counter = 0;
+        }
+    } else {
+        timeout = 0; /* No reTx timer */
+    }
+
+    if (sipTransportChannelCreateSend(ccb, response, sipMethodResponse,
+                                      &cc_remote_ipaddr, cc_remote_port,
+                                      timeout, reldev_stored_msg) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          "SendResponse", "sipTransportChannelCreateSend()");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static sipRet_t
+CopyLocalSDPintoResponse (sipMessage_t *request,
+                          cc_msgbody_info_t *local_msg_body)
+{
+    sipRet_t          tflag = HSTATUS_SUCCESS;
+    uint32_t          body_index;
+    cc_msgbody_info_t tmp_body, *tmp_body_p;
+    cc_msgbody_t     *part;
+
+    if (local_msg_body->num_parts == 0) {
+        /* content type specified but no msg. body to add */
+        return HSTATUS_FAILURE;
+    }
+
+    /*
+     * Duplicate a msg. body to send out which will be freed
+     * after the full message is created during the send of the
+     * msg.
+     */
+    tmp_body.num_parts = 0; /*initialize to no parts */
+    tmp_body_p = &tmp_body;
+    if (cc_cp_msg_body_parts(tmp_body_p, local_msg_body) != CC_RC_SUCCESS) {
+        /* Unable to duplicate the msg. body */
+        return HSTATUS_FAILURE;
+    }
+    part = &tmp_body_p->parts[0];
+    for (body_index = 0; body_index < tmp_body_p->num_parts; body_index++) {
+        if ((part->body != NULL) && (part->body_length)) {
+            tflag = sippmh_add_message_body(request, part->body,
+                        part->body_length,
+                        cc2siptype(part->content_type),
+                        cc2sipdisp(part->content_disposition.disposition),
+                        part->content_disposition.required_handling,
+                        part->content_id);
+        } else {
+            /* Invalid entry */
+            tflag = HSTATUS_FAILURE;
+            break;
+        }
+    }
+    return tflag;
+}
+
+boolean
+AddGeneralHeaders (ccsipCCB_t *ccb,
+                   sipMessageFlag_t messageflag,
+                   sipMessage_t *request,
+                   sipMethod_t sipmethod)
+{
+    const char  *fname = "AddGeneralHeaders";
+    sipRet_t     tflag = HSTATUS_SUCCESS;
+    unsigned int isOver = messageflag.flags;
+    unsigned int whichflag = 0x0001;
+    unsigned int bit_to_reset = 0; // Test and then reset
+    int16_t      i;
+    int          time_exp;
+    unsigned int info_index;
+    cpr_ip_mode_e ip_mode;
+
+    while (0 != isOver) {
+        bit_to_reset = (whichflag & messageflag.flags);
+        switch (bit_to_reset) {
+        case SIP_HEADER_CONTACT_BIT:
+            tflag = sipSPIAddContactHeader(ccb, request);
+            break;
+
+        case SIP_HEADER_RECORD_ROUTE_BIT:
+            if (ccb->record_route_info) {
+                tflag = (sipSPIAddRequestRecordRoute(request, ccb->last_request)) ?
+                    STATUS_SUCCESS : STATUS_FAILURE;
+            }
+            break;
+
+        case SIP_HEADER_ROUTE_BIT:
+            tflag = (sipSPIAddRouteHeaders(request, ccb, NULL, 0)) ?
+                STATUS_SUCCESS : STATUS_FAILURE;
+            break;
+
+        case SIP_HEADER_UNSUPPORTED_BIT:
+            if (ccb->sip_unsupported[0] != '\0') {
+                tflag = sippmh_add_text_header(request,
+                                               SIP_HEADER_UNSUPPORTED,
+                                               &ccb->sip_unsupported[0]);
+            }
+            break;
+
+        case SIP_HEADER_REQUESTED_BY_BIT:
+            tflag = sippmh_add_text_header(request, SIP_HEADER_REQUESTED_BY,
+                                           ccb->sip_reqby);
+            break;
+
+        case SIP_HEADER_REMOTE_PARTY_ID_BIT:
+            tflag = sippmh_add_text_header(request, SIP_HEADER_REMOTE_PARTY_ID,
+                                           ccb->sip_remote_party_id);
+            break;
+
+        case SIP_HEADER_DIVERSION_BIT:
+            for (i = 0; i < MAX_DIVERSION_HEADERS; i++) {
+                if (ccb->diversion[i]) {
+                    tflag = sippmh_add_text_header(request,
+                                                   SIP_HEADER_DIVERSION,
+                                                   ccb->diversion[i]);
+                    if (tflag != HSTATUS_SUCCESS)
+                        break;
+                }
+            }
+            break;
+
+        case SIP_HEADER_AUTHENTICATION_BIT:
+            tflag = sippmh_add_text_header(request,
+                                           AUTHOR_HDR(ccb->authen.status_code),
+                                           ccb->authen.authorization);
+            break;
+
+        case SIP_HEADER_PROXY_AUTH_BIT:
+            // This happens when we get a refer with a proxy-auth. We just
+            // parrot whatever the proxy told us to do in this invite.
+            tflag = sippmh_add_text_header(request,
+                                           SIP_HEADER_PROXY_AUTHORIZATION,
+                                           ccb->refer_proxy_auth);
+            break;
+
+        case SIP_HEADER_REFER_TO_BIT:
+            break;
+
+        case SIP_HEADER_REFERRED_BY_BIT:
+            tflag = sippmh_add_text_header(request, SIP_HEADER_REFERRED_BY,
+                                           ccb->sip_referredBy);
+            break;
+
+        case SIP_HEADER_REPLACES_BIT:
+            tflag = sippmh_add_text_header(request, SIP_HEADER_REPLACES,
+                                           ccb->sipxfercallid);
+            break;
+
+        case SIP_HEADER_EVENT_BIT:
+            break;
+
+        case SIP_HEADER_EXPIRES_BIT:
+            config_get_value(CFGID_TIMER_INVITE_EXPIRES, &time_exp,
+                             sizeof(time_exp));
+            tflag = sippmh_add_int_header(request, SIP_HEADER_EXPIRES,
+                                          time_exp);
+            break;
+
+        case SIP_HEADER_REASON_BIT:
+            tflag = sipSPIAddReasonHeader(ccb, request);
+            break;
+
+        case SIP_HEADER_CONTENT_LENGTH_BIT:
+            if ((messageflag.flags & SIP_HEADER_CONTENT_TYPE_BIT) ||
+                (messageflag.flags & SIP_HEADER_OPTIONS_CONTENT_TYPE_BIT)) {
+                /*
+                 * Header content length will be set when SDP body is added
+                 * so we do not set it here.
+                 */
+                break;
+            }
+            tflag = sippmh_add_int_header(request, SIP_HEADER_CONTENT_LENGTH, 0);
+            break;
+
+        case SIP_HEADER_CONTENT_TYPE_BIT:
+            tflag = CopyLocalSDPintoResponse(request, &ccb->local_msg_body);
+            if (tflag != HSTATUS_SUCCESS) {
+                /* there is some thing wrong with message body */
+                CCSIP_DEBUG_ERROR("%s: Error adding message body.\n", fname);
+                break;
+            }
+
+            /*
+             * SDP successfully added to message. Update offer/answer state.
+             */
+            if (ccb->oa_state == OA_OFFER_RECEIVED) {
+                ccb->oa_state = OA_IDLE;
+            } else {
+                ccb->oa_state = OA_OFFER_SENT;
+            }
+            break;
+
+        case SIP_HEADER_OPTIONS_CONTENT_TYPE_BIT:
+            tflag = CopyLocalSDPintoResponse(request, &ccb->local_msg_body);
+            if (tflag != HSTATUS_SUCCESS) {
+                /* there is some thing wrong with message body */
+                CCSIP_DEBUG_ERROR("%s: Error adding options message body.\n",
+                                  fname);
+            }
+
+            /*
+             * SDP successfully added to message. OPTIONS response does
+             * not alter offer/answer state as the
+             * SIP_HEADER_CONTENT_TYPE_BIT does.
+             */
+            break;
+
+        case SIP_HEADER_ALLOW_BIT:
+            {
+                char temp[MAX_SIP_HEADER_LENGTH];
+
+                snprintf(temp, MAX_SIP_HEADER_LENGTH,
+                         "%s,%s,%s,%s,%s,%s,%s,%s,%s",
+                         SIP_METHOD_ACK, SIP_METHOD_BYE, SIP_METHOD_CANCEL,
+                         SIP_METHOD_INVITE, SIP_METHOD_NOTIFY,
+                         SIP_METHOD_OPTIONS, SIP_METHOD_REFER,
+                         SIP_METHOD_REGISTER, SIP_METHOD_UPDATE);
+                sstrncat(temp, ",", sizeof(temp) - strlen(temp));
+                sstrncat(temp, SIP_METHOD_SUBSCRIBE, sizeof(temp) - strlen(temp));
+                sstrncat(temp, ",", sizeof(temp) - strlen(temp));
+                sstrncat(temp, SIP_METHOD_INFO, sizeof(temp) - strlen(temp));
+                tflag = sippmh_add_text_header(request, SIP_HEADER_ALLOW, temp);
+            }
+            break;
+
+        case SIP_HEADER_ACCEPT_BIT:
+            tflag = sippmh_add_text_header(request, SIP_HEADER_ACCEPT,
+                                           "application/sdp");
+            break;
+
+        case SIP_HEADER_ACCEPT_ENCODING_BIT:
+            tflag = sippmh_add_text_header(request, SIP_HEADER_ACCEPT_ENCODING,
+                                           "identity");
+            break;
+
+        case SIP_HEADER_ACCEPT_LANGUAGE_BIT:
+            tflag = sippmh_add_text_header(request, SIP_HEADER_ACCEPT_LANGUAGE,
+                                           "en");
+            break;
+
+        case SIP_HEADER_CISCO_GUID_BIT:
+            tflag = (sipSPIAddCiscoGuid(request, ccb)) ?
+                STATUS_SUCCESS : STATUS_FAILURE;
+            break;
+
+        case SIP_HEADER_CALL_INFO_BIT:
+            /*
+             * Include call info header if in CCM mode or
+             * other end indicates that it can support it.
+             */
+            if ((sip_regmgr_get_cc_mode(ccb->dn_line) == REG_MODE_CCM) ||
+                (ccb->supported_tags & cisco_callinfo_tag)) {
+                tflag = (sipRet_t) sippmh_add_call_info(request,
+                                                        ccb->out_call_info);
+            }
+            break;
+
+        case SIP_HEADER_JOIN_INFO_BIT:
+            tflag = (sipRet_t) sippmh_add_join_header(request, ccb->join_info);
+            break;
+
+        case SIP_HEADER_ALLOW_EVENTS_BIT:
+            {
+                char temp[MAX_SIP_HEADER_LENGTH];
+                int  kpml_config;
+
+                // Get kpml configuration
+                config_get_value(CFGID_KPML_ENABLED, &kpml_config,
+                                 sizeof(kpml_config));
+                if (kpml_config) {
+                    snprintf(temp, MAX_SIP_HEADER_LENGTH, "%s,%s",
+                             SIP_EVENT_KPML, SIP_EVENT_DIALOG);
+                } else {
+                    snprintf(temp, MAX_SIP_HEADER_LENGTH, "%s",
+                             SIP_EVENT_DIALOG);
+                }
+                tflag = sippmh_add_text_header(request, SIP_HEADER_ALLOW_EVENTS,
+                                               temp);
+            }
+            break;
+
+        case SIP_HEADER_SUPPORTED_BIT:
+            {
+                const char *opt_tags;
+
+                opt_tags = sipGetSupportedOptionList(ccb, sipmethod);
+
+                tflag = sippmh_add_text_header(request,
+                                               SIP_HEADER_SUPPORTED,
+                                               opt_tags);
+                               }
+            break;
+
+        case SIP_HEADER_REQUIRE_BIT:
+            ip_mode = platform_get_ip_address_mode();
+            if (ip_mode == CPR_IP_MODE_DUAL) {
+                tflag = sippmh_add_text_header(request, SIP_HEADER_REQUIRE, "sdp-anat");
+            }
+            break;
+
+        case SIP_HEADER_RETRY_AFTER_BIT:
+            tflag = sippmh_add_int_header(request,
+                                          SIP_HEADER_RETRY_AFTER,
+                                          abs((cpr_rand() % 11)));
+            break;
+
+        case SIP_HEADER_RECV_INFO_BIT:
+            for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) {
+                if (g_registered_info[info_index] != NULL) {
+                    tflag = sippmh_add_text_header(request, SIP_HEADER_RECV_INFO,
+                                                   g_registered_info[info_index]);
+                    if (tflag != HSTATUS_SUCCESS) {
+                        break;
+                    }
+                }
+            }
+            break;
+
+        default:
+            //tflag = HSTATUS_FAILURE;
+            break;
+        }
+
+        if (tflag != HSTATUS_SUCCESS) {
+            return FALSE;
+        }
+        whichflag = whichflag << 1;
+        /*
+         * Reset this bit so if nothing else is there we do not need to test
+         */
+        isOver &= ~bit_to_reset;
+    }
+    return TRUE;
+}
+
+
+/*************************************************************
+ * Function: sipSPISendUpdate
+ * This function creates, formats, and sends an UPDATE message
+ * on an existing (early) dialog
+ **************************************************************/
+boolean
+sipSPISendUpdate (ccsipCCB_t *ccb)
+{
+    const char      *fname = "sipSPISendUpdate";
+    sipMessageFlag_t messageflag;
+    sipMessage_t    *request = NULL;
+    sipRet_t         flag = STATUS_SUCCESS;
+
+
+    // UPDATE requests mandates the use of the following headers:
+    // Allow, Call-ID, Contact, CSeq, From, Max-Forwards, To, and Via
+
+    messageflag.flags = 0;
+    messageflag.flags |= SIP_HEADER_ALLOW_BIT |
+                         SIP_HEADER_CONTACT_BIT |
+                         SIP_HEADER_ROUTE_BIT;
+
+    if (ccb->local_msg_body.num_parts) {
+        messageflag.flags |= SIP_HEADER_CONTENT_TYPE_BIT;
+    } else {
+        messageflag.flags |= SIP_HEADER_CONTENT_LENGTH_BIT;
+    }
+
+    request = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    // CreateRequest adds the request line, Via, Date, CSeq, User-Agent
+    // and all the headers in the messageflags above. It will also write
+    // the new SDP which it picks from the ccb (ccb->sip_sdp)
+    if (CreateRequest(ccb, messageflag, sipMethodUpdate, request, FALSE, 0)) {
+        flag = STATUS_SUCCESS;
+    } else {
+        flag = STATUS_FAILURE;
+    }
+
+    if (flag != STATUS_SUCCESS) {
+        free_sip_message(request);
+        CCSIP_DEBUG_ERROR("%s: Error: UPDATE message build unsuccessful.\n",
+                          fname);
+        clean_method_request_trx(ccb, sipMethodUpdate, TRUE);
+        return (FALSE);
+    }
+    // Send the request
+    ccb->retx_counter = 0;
+    if (SendRequest(ccb, request, sipMethodUpdate, TRUE, TRUE, FALSE) == FALSE) {
+        clean_method_request_trx(ccb, sipMethodUpdate, TRUE);
+        return (FALSE);
+    } else {
+        return (TRUE);
+    }
+}
+
+/*************************************************************
+ * Function: sipSPISendUpdateResponse
+ * This function creates, formats, and sends a response to an
+ * UPDATE message received on an early dialog
+ **************************************************************/
+boolean
+sipSPISendUpdateResponse (ccsipCCB_t *ccb,
+                          boolean send_sdp,
+                          cc_causes_t cause,
+                          boolean retx)
+{
+    const char      *fname = "SIPSPISendUpdateResponse";
+    sipMessage_t    *response = NULL;
+    sipRet_t         flag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    int              statusCode;
+    char            *reason_phrase;
+    boolean          result;
+
+    // Determine the statusCode and reason_phrase from the cause value
+    statusCode = ccsip_cc_to_sip_cause(cause, &reason_phrase);
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_RESPONSE),
+                      fname, statusCode);
+
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_CONTACT_BIT |
+                        SIP_HEADER_RECORD_ROUTE_BIT |
+                        SIP_HEADER_ALLOW_BIT;
+
+    if (send_sdp) {
+        messageflag.flags |= SIP_HEADER_CONTENT_TYPE_BIT;
+    } else {
+        messageflag.flags |= SIP_HEADER_CONTENT_LENGTH_BIT;
+    }
+
+    if (statusCode == SIP_CLI_ERR_EXTENSION) {
+        messageflag.flags |= SIP_HEADER_UNSUPPORTED_BIT;
+    }
+    if (statusCode == SIP_SERV_ERR_INTERNAL) {
+        messageflag.flags |= SIP_HEADER_RETRY_AFTER_BIT;
+    }
+    response = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateResponse(ccb, messageflag, (uint16_t)statusCode,
+                       response, reason_phrase, 0, NULL, sipMethodUpdate)) {
+        flag = HSTATUS_SUCCESS;
+    } else {
+        flag = HSTATUS_FAILURE;
+    }
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (response)
+            free_sip_message(response);
+        clean_method_request_trx(ccb, sipMethodUpdate, FALSE);
+        return (FALSE);
+    }
+
+    result = sendResponse(ccb, response, ccb->last_request, retx,
+                          sipMethodUpdate);
+    clean_method_request_trx(ccb, sipMethodUpdate, FALSE);
+    return result;
+}
+
+boolean
+sipSPISendNotifyResponse (ccsipCCB_t *ccb, cc_causes_t cause)
+{
+    const char      *fname = "SIPSPISendNotifyResponse";
+    sipMessage_t    *response = NULL;
+    sipRet_t         flag = STATUS_SUCCESS;
+    sipMessageFlag_t messageflag;
+    int              sip_response_code;
+    char            *sip_response_phrase;
+    boolean          result;
+
+    sip_response_code = ccsip_cc_to_sip_cause(cause, &sip_response_phrase);
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_RESPONSE),
+                      fname, sip_response_code);
+
+    messageflag.flags = 0;
+    messageflag.flags = SIP_HEADER_CONTACT_BIT |
+                        SIP_HEADER_RECORD_ROUTE_BIT |
+                        SIP_HEADER_CONTENT_LENGTH_BIT;
+
+    /* Add Content Length */
+
+    response = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateResponse(ccb, messageflag, (unsigned short)sip_response_code,
+                       response, sip_response_phrase, 0, NULL, sipMethodNotify)) {
+        flag = HSTATUS_SUCCESS;
+    } else {
+        flag = HSTATUS_FAILURE;
+    }
+
+    /* If build error detected, cleanup and do not send message */
+    if (flag != STATUS_SUCCESS) {
+        /* !!! Clean up */
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_BUILDFLAG_ERROR), fname);
+        if (response)
+            free_sip_message(response);
+        clean_method_request_trx(ccb, sipMethodNotify, FALSE);
+        return (FALSE);
+    }
+
+    result = sendResponse(ccb, response, ccb->last_request, FALSE,
+                          sipMethodNotify);
+    clean_method_request_trx(ccb, sipMethodNotify, FALSE);
+    return result;
+}
+
+/*
+ * sipSPIGenerateReferredByHeader
+ *
+ * This function is called to generate SIP Referred-By Header
+ * when SIP REFER request is sent, to Transfer the call
+ *
+ * @param[in,out] ccb   CCB call info structure
+ *
+ * @return              TRUE if the header is successfully
+ *                      created; FALSE otherwise.
+ *
+ * @pre                 (ccb not_eqs NULL)
+ *
+ */
+boolean
+sipSPIGenerateReferredByHeader (ccsipCCB_t *ccb)
+{
+    char        line_name[MAX_LINE_NAME_SIZE];
+    char        escaped_line_name[MAX_ESCAPED_USER_LEN];
+    char        dest_sip_addr_str[MAX_IPADDR_STR_LEN];
+    char        pReferByStr[MAX_SIP_URL_LENGTH];
+    boolean     retval = FALSE;
+    cpr_ip_type ip_type;
+
+    /* Initialize */
+    line_name[0] = '\0';
+    escaped_line_name[0] = '\0';
+    dest_sip_addr_str[0] = '\0';
+    pReferByStr[0] = '\0';
+
+    /*
+     * get Server Ip Addr, line_name and form AOR to populate referredBy
+     */
+    config_get_line_string(CFGID_LINE_NAME, line_name, ccb->dn_line,
+                           sizeof(line_name));
+
+    if (line_name[0] != '\0') {
+        (void) sippmh_convertURLCharToEscChar(line_name, strlen(line_name),
+                                              escaped_line_name,
+                                              sizeof(escaped_line_name),
+                                              TRUE);
+    }
+
+    ip_type = sipTransportGetPrimServerAddress(ccb->dn_line, dest_sip_addr_str);
+
+    if (escaped_line_name[0] != '\0') {
+        if (ip_type == CPR_IP_ADDR_IPV6) {
+            snprintf(pReferByStr, MAX_SIP_URL_LENGTH, "<sip:%s@[%s]>",
+                     escaped_line_name, dest_sip_addr_str);
+        } else {
+            snprintf(pReferByStr, MAX_SIP_URL_LENGTH, "<sip:%s@%s>",
+                     escaped_line_name, dest_sip_addr_str);
+        }
+    }
+
+    if (pReferByStr[0] != '\0') {
+        ccb->sip_referredBy = strlib_update(ccb->sip_referredBy, pReferByStr);
+        retval = TRUE;
+    }
+
+    return (retval);
+}
+
+/*
+ *  Function: sipSPIBuildRegisterHeaders
+ *
+ *  Parameters:
+ *      ccsipCCB_t * - pointer to the ccb used for registration
+ *      const char * user - used to build the headers
+ *      int expires_int - registration expiry time
+ *
+ *  Description:  The function builds the register message
+ *
+ *  Returns:
+ *      sipMessage_t * - pointer to the sip request
+ *
+ */
+sipMessage_t *
+sipSPIBuildRegisterHeaders(ccsipCCB_t *ccb,
+                    const char *user,
+                    int expires_int)
+{
+    const char       fname[] = "sipSPIBuildRegisterHeaders";
+    char            *sip_from_temp;
+    char            *sip_to_temp;
+    sipRet_t         flag  = STATUS_SUCCESS;
+    sipRet_t         tflag = STATUS_SUCCESS;
+    char             src_addr_str[MAX_IPADDR_STR_LEN];
+    char             dest_sip_addr_str[MAX_IPADDR_STR_LEN];
+    char             expires[MAX_EXPIRES_LEN];
+    sipMessageFlag_t messageflag;
+    char             reg_user_info[MAX_REG_USER_INFO_LEN];
+    char             escaped_user[MAX_ESCAPED_USER_LEN];
+    char            *sip_from_tag;
+    sipMessage_t    *request = NULL;
+
+    (void) sippmh_convertURLCharToEscChar(user, strlen(user),
+                                          escaped_user, sizeof(escaped_user),
+                                          TRUE);
+    /* get reg_user_info */
+    config_get_string(CFGID_REG_USER_INFO, reg_user_info,
+                      sizeof(reg_user_info));
+    ipaddr2dotted(src_addr_str, &ccb->src_addr);
+
+    sstrncpy(dest_sip_addr_str, ccb->reg.proxy, MAX_IPADDR_STR_LEN);
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST),
+                      fname, "REGISTER");
+
+    // Create a new From header only if the previous one in the CCB is blank.
+    // It will not be blank if we received a 401/407 response to a prior
+    // REGISTER request where we reuse the CCB that we first used without
+    // cleaning it.
+
+    if (ccb->sip_from[0] == '\0') {
+        sip_from_temp = strlib_open(ccb->sip_from, MAX_SIP_URL_LENGTH);
+        if (sip_from_temp) {
+            if (ccb->reg.addr.type == CPR_IP_ADDR_IPV6) {
+                snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, "<sip:%s@[%s]>",
+                         escaped_user, dest_sip_addr_str); /* proxy */
+            } else {
+                snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, "<sip:%s@%s>",
+                         escaped_user, dest_sip_addr_str); /* proxy */
+            }
+            /* Now add tag to the From header */
+            sip_from_tag = strlib_open(ccb->sip_from_tag, MAX_SIP_URL_LENGTH);
+            if (sip_from_tag) {
+                sip_util_make_tag(sip_from_tag);
+                sstrncat(sip_from_temp, ";tag=",
+                        MAX_SIP_URL_LENGTH - strlen(sip_from_temp));
+                sstrncat(sip_from_temp, sip_from_tag,
+                        MAX_SIP_URL_LENGTH - strlen(sip_from_temp));
+            }
+            ccb->sip_from_tag = strlib_close(sip_from_tag);
+        }
+        ccb->sip_from = strlib_close(sip_from_temp);
+    }
+    sip_to_temp = strlib_open(ccb->sip_to, MAX_SIP_URL_LENGTH);
+    if (ccb->reg.addr.type == CPR_IP_ADDR_IPV6) {
+        snprintf(sip_to_temp, MAX_SIP_URL_LENGTH, "<sip:%s@[%s]>",
+                 escaped_user, dest_sip_addr_str); /* proxy */
+    } else {
+        snprintf(sip_to_temp, MAX_SIP_URL_LENGTH, "<sip:%s@%s>",
+                 escaped_user, dest_sip_addr_str); /* proxy */
+    }
+    ccb->sip_to = strlib_close(sip_to_temp);
+    /*
+     * Build A request from Message Factory using following Flags...
+     * You do not need to specify common headers they will automatically
+     * get added
+     */
+
+    messageflag.flags = 0;
+
+    messageflag.flags |= SIP_HEADER_CONTACT_BIT |
+                         SIP_HEADER_SUPPORTED_BIT |
+                         SIP_HEADER_CISCO_GUID_BIT;
+
+    messageflag.flags |=  SIP_HEADER_CONTENT_LENGTH_BIT;
+
+
+    if (ccb->authen.authorization != NULL) {
+        messageflag.flags |= SIP_HEADER_AUTHENTICATION_BIT;
+    }
+
+    if (ccb->send_reason_header) {
+        messageflag.flags |= SIP_HEADER_REASON_BIT;
+    }
+
+
+    request = GET_SIP_MESSAGE();
+    messageflag.extflags = 0;
+    if (CreateRequest(ccb, messageflag, sipMethodRegister, request, FALSE, 0)) {
+        tflag = HSTATUS_SUCCESS;
+    } else {
+        tflag = HSTATUS_FAILURE;
+    }
+    UPDATE_FLAGS(flag, tflag);
+
+    snprintf(expires, sizeof(expires), "%d", expires_int);
+    tflag = sippmh_add_text_header(request, SIP_HEADER_EXPIRES, expires);
+
+    UPDATE_FLAGS(flag, tflag);
+
+    if (flag != STATUS_SUCCESS) {
+        free_sip_message(request);
+        CCSIP_DEBUG_ERROR("%s: Error: REGISTER message build unsuccessful.\n",
+                          fname);
+        clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+        return (NULL);
+    }
+
+    return (request);
+}
+
diff --git a/libs/sipcc/core/sipstack/ccsip_platform.c b/libs/sipcc/core/sipstack/ccsip_platform.c
new file mode 100644 (file)
index 0000000..de75215
--- /dev/null
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "phone.h"
+#include "phone_debug.h"
+#include "ccsip_register.h"
+#include "ccsip_task.h"
+#include "ccsip_pmh.h"
+#include "config.h"
+#include "sip_common_transport.h"
+#include "sip_csps_transport.h"
+#include "uiapi.h"
+#include "sip_interface_regmgr.h"
+
+#include "platform_api.h"
+
+extern void platform_sync_cfg_vers(char *cfg_ver, char *dp_ver, char *softkey_ver);
+extern void platform_reg_failover_ind(void *data);
+extern void platform_reg_fallback_ind(void *data);
+extern int platGetUnregReason();
+extern void ccsip_add_wlan_classifiers();
+void ccsip_remove_wlan_classifiers();
+
+/*
+ *  Function: sip_platform_init()
+ *
+ *  Parameters: None
+ *
+ *  Description: Performs platform initialization stuff.  Should probably be
+ *               renamed or moved to make it more "generic".
+ *
+ *  Returns: None
+ *
+ */
+void
+sip_platform_init (void)
+{
+
+    // Since we have all our configuration information now
+    // we want to do a final check to see if our network media
+    // type has changed
+
+    /* Unregister the phone */
+    ccsip_register_cancel(FALSE, TRUE);
+    ccsip_register_reset_proxy();
+
+    /*
+     * Make sure that the IP stack is up before trying to connect
+     */
+    if (PHNGetState() > STATE_IP_CFG) {
+
+        ccsip_add_wlan_classifiers();
+        /*
+         * regmgr - The SIPTaskDisconnectFromSipProxies and
+         * SIPTaskConnectToSipProxies calls will be called
+         * as part of the transport interface init and regmgr
+         * inits.
+         */
+
+        ccsip_register_all_lines();
+        ui_sip_config_done();
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX "IP Stack Not Initialized.\n", "sip_platform_init");
+    }
+}
+
+
+/*
+ * Send StationReset message
+ * Parameters supplied by application:
+ *    none
+ * Parameters supplied in the message
+ *     - reset type (RESTART)
+ */
+int
+sip_platform_ui_restart (void)
+{
+    phone_reset(DEVICE_RESTART);
+    return TRUE;
+}
+
+
+void
+sip_platform_handle_service_control_notify (sipServiceControl_t *scp)
+{
+    switch (scp->action) {
+
+    case SERVICE_CONTROL_ACTION_RESET:
+        platform_reset_req(DEVICE_RESET);
+        break;
+
+    case SERVICE_CONTROL_ACTION_RESTART:
+        platform_reset_req(DEVICE_RESTART);
+        break;
+
+    case SERVICE_CONTROL_ACTION_CHECK_VERSION:
+        platform_sync_cfg_vers(scp->configVersionStamp,
+                               scp->dialplanVersionStamp,
+                               scp->softkeyVersionStamp);
+        break;
+
+    case SERVICE_CONTROL_ACTION_APPLY_CONFIG:
+        // call the function to process apply config NOTIFY message.
+        platform_apply_config(scp->configVersionStamp,
+                               scp->dialplanVersionStamp,
+                               scp->fcpVersionStamp,
+                               scp->cucm_result,
+                               scp->firmwareLoadId,
+                               scp->firmwareInactiveLoadId,
+                               scp->loadServer,
+                               scp->logServer,
+                               scp->ppid);
+        break;
+    default:
+        break;
+
+    }
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_platform_tcp.c b/libs/sipcc/core/sipstack/ccsip_platform_tcp.c
new file mode 100644 (file)
index 0000000..bbc09c3
--- /dev/null
@@ -0,0 +1,1220 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_ipc.h"
+#include "cpr_errno.h"
+#include "cpr_socket.h"
+#include "cpr_in.h"
+#include "ccsip_core.h"
+#include "ccsip_task.h"
+#include "sip_platform_task.h"
+#include "ccsip_platform_udp.h"
+#include "sip_common_transport.h"
+#include "sip_common_regmgr.h"
+#include "phone_debug.h"
+#include "util_string.h"
+#include "ccsip_platform_tcp.h"
+#include "ccsip_platform_timers.h"
+#include "text_strings.h"
+#include "ccsip_register.h"
+#include "phntask.h"
+#include "plat_api.h"
+#include "sip_socket_api.h"
+
+
+/*
+ * Externs
+ */
+extern cc_config_table_t CC_Config_Table[];
+extern ccm_act_stdby_table_t CCM_Active_Standby_Table;
+extern cpr_sockaddr_t *sip_set_sockaddr(cpr_sockaddr_storage *psock_storage, uint16_t family,
+                                 cpr_ip_addr_t ip_addr, uint16_t port, uint16_t *addr_len);
+extern void ccsip_dump_recv_msg_info(sipMessage_t *pSIPMessage,
+                               cpr_ip_addr_t *cc_remote_ipaddr,
+                               uint16_t cc_remote_port);
+
+#define MAX_CHUNKS 12
+#define MAX_PAYLOAD_SIZE  (MAX_CHUNKS*CPR_MAX_MSG_SIZE)
+/*
+ * Globals
+ */
+static uint32_t sip_tcp_incomplete_msg = 0;
+static uint32_t sip_tcp_fail_network_msg = 0;
+int max_tcp_send_msg_q_size = 0;
+int max_tcp_send_msg_q_connid = 0;
+cpr_ip_addr_t max_tcp_send_msg_q_ipaddr = {0,{0}};
+ushort max_tcp_send_msg_q_port = 0;
+
+/*
+ * The following routine that set the socket option has been
+ * ported over from IOS. So not renaming.
+ */
+static ccsipRet_e
+ccsipSocketSetNonblock (cpr_socket_t fd, int optval)
+{
+    const char  *fname = "ccsipSocketSetNonblock";
+
+    if (cprSetSockNonBlock(fd)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to set non-blocking socket mode %d\n",
+                            fname, cpr_errno);
+        return SIP_INTERNAL_ERR;
+    }
+    return SIP_SUCCESS;
+}
+
+/*
+ * The following routine that set the socket option has been
+ * ported over from IOS. So not renaming.
+ */
+static ccsipRet_e
+ccsipSocketSetKeepAlive (cpr_socket_t fd, int optval)
+{
+    const char  *fname = "ccsipSocketSetKeepAlive";
+
+    if (cprSetSockOpt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optval,
+                      sizeof(optval))) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to set KEEP ALIVE on a socket %d\n",
+                                fname, cpr_errno);
+        return SIP_INTERNAL_ERR;
+    }
+
+    return SIP_SUCCESS;
+}
+
+/*
+ * The following routine that set the socket option has been
+ * ported over from IOS. So not renaming.
+ */
+#ifdef NOT_AVAILABLE_WIN32
+static ccsipRet_e
+ccsipSocketSetTCPtos (cpr_socket_t fd, uint8_t optval)
+{
+    const char  *fname = "ccsipSocketSetTCPtos";
+
+    if (cprSetSockOpt(fd, SOL_TCP, TCP_TOS, (void *)&optval,
+                      sizeof(optval))) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to set TCP TOS on a socket %d\n",
+                                fname, cpr_errno);
+        return SIP_INTERNAL_ERR;
+    }
+
+    return SIP_SUCCESS;
+}
+
+/*
+ * The following routine that set the socket option has been
+ * ported over from IOS. So not renaming.
+ */
+static ccsipRet_e
+ccsipSocketSetPushBit (cpr_socket_t fd, int optval)
+{
+    const char  *fname = "ccsipSocketSetPushBit";
+
+    if (cprSetSockOpt(fd, SOL_TCP, TCP_ALWAYSPUSH, (void *)&optval,
+                      sizeof(optval))) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to set PUSH BIT on a socket %d\n",
+                                fname, cpr_errno);
+        return SIP_INTERNAL_ERR;
+    }
+
+    return SIP_SUCCESS;
+}
+#endif /* NOT_AVAILABLE_WIN32 */
+
+static boolean ccsipIsSecureType(sipSPIConnId_t connid)
+{
+    if (sip_tcp_conn_tab[connid].soc_type == SIP_SOC_TLS) {
+        return TRUE;
+    }
+    return FALSE;
+}
+/**
+ *
+ * sip_tcp_attach_socket
+ *
+ * Attach the socket to the select call
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: SIP_OK or SIP_ERROR
+ *
+ * Remarks: No check is made to see if socket is already attached
+ *
+ */
+int
+sip_tcp_attach_socket (cpr_socket_t s)
+{
+    int i;
+
+    /*
+     * Attach socket to select call
+     */
+    for (i = 0; i < MAX_SIP_CONNECTIONS; i++) {
+        if (sip_conn.read[i] == INVALID_SOCKET) {
+            sip_conn.read[i] = s;
+            FD_SET(s, &read_fds);
+            nfds = MAX(nfds, (uint32_t)s);
+            sip_conn.write[i] = s;
+            FD_SET(s, &write_fds);
+            break;
+        }
+    }
+
+    /*
+     * Are there already too many connections?
+     */
+    if (i == MAX_SIP_CONNECTIONS) {
+        return SIP_ERROR;
+    }
+    return SIP_OK;
+}
+
+/**
+ *
+ * sip_tcp_detach_socket
+ *
+ * Attach the socket to the select call
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: SIP_OK or SIP_ERROR
+ *
+ * Remarks: No check is made to see if socket is already attached
+ *
+ */
+static int
+sip_tcp_detach_socket (cpr_socket_t s)
+{
+    int i;
+    const char *fname = "sip_tcp_detach_socket";
+
+    if (s == INVALID_SOCKET) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Invalid socket\n", fname);
+        return SIP_ERROR;
+    }
+    /*
+     * Attach socket to select call
+     */
+    for (i = 0; i < MAX_SIP_CONNECTIONS; i++) {
+        if (sip_conn.read[i] == s) {
+            sip_conn.read[i] = INVALID_SOCKET;
+            FD_CLR(s, &read_fds);
+            nfds = MAX(nfds, (uint32_t)s);
+            sip_conn.write[i] = INVALID_SOCKET;
+            FD_CLR(s, &write_fds);
+            break;
+        }
+    }
+
+    /*
+     * Are there already too many connections?
+     */
+    if (i == MAX_SIP_CONNECTIONS) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Max TCP connections reached.\n", fname);
+        return SIP_ERROR;
+    }
+    return SIP_OK;
+}
+
+/**
+ *
+ * sip_tcp_set_sock_options
+ *
+ * Attach the socket to the select call
+ *
+ * Parameters:   fd - the file descriptor
+ *
+ * Return Value: SIP_OK or SIP_ERROR
+ *
+ * Remarks: No check is made to see if socket is already attached
+ *
+ */
+boolean
+sip_tcp_set_sock_options (int fd)
+{
+    int optval;
+    ccsipRet_e status = SIP_SUCCESS;
+
+    optval = 1;
+
+    /* Set non-blocking mode */
+    status = ccsipSocketSetNonblock(fd, optval);
+    if (status != SIP_SUCCESS) {
+        return FALSE;
+    }
+
+    /* Set the keepalive option */
+    status = ccsipSocketSetKeepAlive(fd, optval);
+    if (status != SIP_SUCCESS) {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/**
+ *
+ * sip_tcp_fd_to_connid
+ *
+ * returns the tcp conn table index for a particular socket
+ *
+ * Parameters:   fd - the file descriptor
+ *
+ * Return Value: sip_tcp_conn_tab index
+ *
+ */
+int
+sip_tcp_fd_to_connid (cpr_socket_t fd)
+{
+    int i;
+
+    for (i = 0; i < MAX_CONNECTIONS; ++i) {
+        if (sip_tcp_conn_tab[i].fd == fd) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+/*
+ * sip_tcp_get_free_conn_entry ()
+ *
+ * Description  : This procedure returns the first free entry from the TCP
+ *                conn table.
+ *
+ * Input Params : None.
+ *
+ * Returns      : Index to the Connection table (if SUCCESSFUL)
+ *                -1 in case of failure.
+ */
+int
+sip_tcp_get_free_conn_entry (void)
+{
+    int i;
+    const char *fname = "sip_tcp_get_free_conn_entry";
+
+    for (i = 0; i < MAX_CONNECTIONS; ++i) {
+        if (sip_tcp_conn_tab[i].fd == -1) {
+            /* Zero the connection table entry */
+            memset((sip_tcp_conn_tab + i), 0, sizeof(sip_tcp_conn_t));
+            sip_tcp_conn_tab[i].state = SOCK_IDLE;
+            sip_tcp_conn_tab[i].dirtyFlag = FALSE;
+            sip_tcp_conn_tab[i].error_cause = SOCKET_NO_ERROR;
+            return i;
+        }
+    }
+
+    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"TCP Connection table full\n", fname);
+
+    return -1;
+}
+
+/*
+ * sip_tcp_init_conn_table()
+ * Description : Cleans up the entry associated with the connid
+ *               from the sip_tcp_conn_tab
+ *
+ * Input : void
+ *
+ * Output : void
+ *
+ */
+void
+sip_tcp_init_conn_table (void)
+{
+    static boolean initial_call = TRUE;
+    int idx;
+
+    if (initial_call) {
+        /*
+         * Initialize the tcp conn table
+         */
+        for (idx = 0; idx < MAX_CONNECTIONS; ++idx) {
+            sip_tcp_conn_tab[idx].fd = -1;
+        }
+        initial_call = FALSE;
+    }
+}
+
+/*
+ * sip_tcp_purge_entry()
+ * Description : Cleans up the entry associated with the connid
+ *               from the sip_tcp_conn_tab
+ *
+ * Input : connid
+ *
+ * Output : None
+ *
+ */
+void
+sip_tcp_purge_entry (sipSPIConnId_t connid)
+{
+    sip_tcp_conn_t *entry = sip_tcp_conn_tab + connid;
+    const char *fname= "sip_tcp_purge_entry";
+    boolean secure;
+
+    if (!VALID_CONNID(connid)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Invalid TCP connection Id=%ld.\n",
+                            fname, connid);
+        return;
+    }
+    secure = ccsipIsSecureType(connid);
+
+    (void) sip_tcp_detach_socket(entry->fd);
+    (void) sipSocketClose(entry->fd, secure);
+    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Socket fd: %d closed for connid %ld with "
+                        "address: %i, remote port: %u\n",
+                        DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname), entry->fd, connid, entry->ipaddr, entry->port);
+
+    entry->fd = -1;  /* Free the connection table entry in the BEGINNING ! */
+    sipTcpFlushRetrySendQueue(entry);
+    entry->ipaddr = ip_addr_invalid;
+    entry->port = 0;
+    entry->context = NULL;
+    entry->dirtyFlag = FALSE;
+    if (entry->prev_bytes) {
+        cpr_free(entry->prev_msg);
+    }
+    return;
+}
+
+
+/*
+ * sip_tcp_create_connection()
+ * Description : This routine is called is response to a create connection
+ * request from SIP_SPI to SIP_TCP.
+ *
+ * Input : spi_msg - Pointer to sipSPIMessage_t containing the create conn
+ * parameters.
+ *
+ * Output : Nothing
+ */
+cpr_socket_t
+sip_tcp_create_connection (sipSPIMessage_t *spi_msg)
+{
+    const char* fname = "sip_tcp_create_connection";
+    int idx;
+    cpr_socket_t new_fd;
+    cpr_sockaddr_storage *local_addr_ptr;
+    sipSPICreateConnection_t *create_msg;
+    cpr_sockaddr_t local_addr;
+    cpr_socklen_t local_addr_len = sizeof(cpr_sockaddr_t);
+    int tos_dscp_val = 0; // set to default if there is no config. for dscp
+#ifdef IPV6_STACK_ENABLED
+
+    int ip_mode = CPR_IP_MODE_IPV4;
+#endif
+    uint16_t af_listen = AF_INET6;
+    cpr_sockaddr_storage sock_addr;
+    uint16_t       addr_len;
+    cpr_sockaddr_storage local_sock_addr;
+    cpr_ip_addr_t  local_ipaddr;
+
+    sip_tcp_init_conn_table();
+    create_msg = &(spi_msg->createConnMsg);
+    CPR_IP_ADDR_INIT(local_ipaddr);
+
+#ifdef IPV6_STACK_ENABLED
+
+    config_get_value(CFGID_IP_ADDR_MODE, &ip_mode, sizeof(ip_mode));
+
+    /*
+     * Create a socket
+     */
+    if (ip_mode == CPR_IP_MODE_IPV6 ||
+        ip_mode == CPR_IP_MODE_DUAL) {
+        af_listen = AF_INET6;
+    } else {
+#endif
+        af_listen = AF_INET;
+#ifdef IPV6_STACK_ENABLED
+    }
+#endif
+
+    /* Create New connection to the (addr,port) pair */
+    new_fd = cprSocket(af_listen, SOCK_STREAM, 0 /* IPPROTO_TCP */);
+    if (new_fd < 0) {
+        /* Send create connection failed message to SIP_SPI */
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Socket creation failed %d.\n",
+                            fname, cpr_errno);
+        return INVALID_SOCKET;
+    }
+    idx = sip_tcp_get_free_conn_entry();
+    if (idx == -1) {
+        /* Send create connection failed message to SIP_SPI */
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No Free connection entry.\n",
+                            fname);
+        (void) sipSocketClose(new_fd, FALSE);
+        return INVALID_SOCKET;
+    }
+
+    if (sip_tcp_set_sock_options(new_fd) != TRUE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Socket set option failed.\n",
+                            fname);
+    }
+
+    sip_config_get_net_device_ipaddr(&local_ipaddr);
+
+    memset(&local_sock_addr, 0, sizeof(local_sock_addr));
+
+    (void) sip_set_sockaddr(&local_sock_addr, af_listen, local_ipaddr, 0, &addr_len);
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"local_ipaddr.u.ip4=%x\n",
+            DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname), local_ipaddr.u.ip4);
+
+    if (cprBind(new_fd, (cpr_sockaddr_t *)&local_sock_addr, addr_len)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"TCP bind failed with error %d\n", fname,
+                              cpr_errno);
+        (void) sipSocketClose(new_fd, FALSE);
+        sip_tcp_conn_tab[idx].fd = INVALID_SOCKET;
+        return INVALID_SOCKET;
+    }
+
+    memset(&sock_addr, 0, sizeof(sock_addr));
+
+    (void) sip_set_sockaddr(&sock_addr, af_listen, create_msg->addr,
+                            (uint16_t)(create_msg->port), &addr_len);
+
+    sip_tcp_conn_tab[idx].fd = new_fd;
+    sip_tcp_conn_tab[idx].ipaddr = create_msg->addr;
+    sip_tcp_conn_tab[idx].port = create_msg->port;
+    sip_tcp_conn_tab[idx].context = spi_msg->context;
+    sip_tcp_conn_tab[idx].dirtyFlag = FALSE;
+    sip_tcp_conn_tab[idx].addr = sock_addr;
+
+    if (cprConnect(new_fd, (cpr_sockaddr_t *)&sock_addr, addr_len)
+            == CPR_FAILURE) {
+        if (errno == EWOULDBLOCK || errno == EINPROGRESS) {
+            char ipaddr_str[MAX_IPADDR_STR_LEN];
+
+            ipaddr2dotted(ipaddr_str, &create_msg->addr);
+
+            /* connect in progress. Include this socket in select */
+            sip_tcp_conn_tab[idx].state = SOCK_CONNECT_PENDING;
+
+            CCSIP_DEBUG_MESSAGE(SIP_F_PREFIX"socket connection in progress errno:%d"
+                                "ipaddr: %s, port: %d\n",
+                                fname, errno, ipaddr_str, create_msg->port);
+        } else {
+            char ipaddr_str[MAX_IPADDR_STR_LEN];
+
+            ipaddr2dotted(ipaddr_str, &create_msg->addr);
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"socket connect failed errno: %d "
+                              "ipaddr: %s, port: %d\n",
+                              fname, errno, ipaddr_str, create_msg->port);
+            sip_tcp_purge_entry(idx);
+            return INVALID_SOCKET;
+        }
+    } else {
+        /* Even for this non-blocking socket, the connection was
+         * completed immediately. Is that a possibility ??
+         * I am not sure. Just send a connectioncreated msg to SIP_SPI
+         */
+        sip_tcp_conn_tab[idx].state = SOCK_CONNECTED;
+    }
+
+    if (cprGetSockName(new_fd, &local_addr, &local_addr_len) != CPR_FAILURE) {
+        local_addr_ptr = (cpr_sockaddr_storage *)&local_addr;
+
+        if (local_addr_ptr->ss_family == AF_INET6) {
+
+            create_msg->local_listener_port = ntohs(((cpr_sockaddr_in6_t *)local_addr_ptr)->sin6_port);
+
+        } else {
+
+            create_msg->local_listener_port = ntohs(((cpr_sockaddr_in_t *)local_addr_ptr)->sin_port);
+        }
+
+        (void) sip_tcp_attach_socket(new_fd);
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error getting local port info.\n",
+                            fname);
+        sip_tcp_purge_entry(idx);
+        return INVALID_SOCKET;
+    }
+
+    // set IP tos/dscp value for SIP messaging
+    config_get_value(CFGID_DSCP_FOR_CALL_CONTROL, (int *)&tos_dscp_val,
+                     sizeof(tos_dscp_val));
+    if (cprSetSockOpt(new_fd, SOL_IP, IP_TOS, (void *)&tos_dscp_val,
+                      sizeof(tos_dscp_val)) == CPR_FAILURE) {
+        // do NOT take hard action; just log the error and move on
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to set IP TOS %d on TCP socket. cpr_errno = %d",
+                          fname, tos_dscp_val, cpr_errno);
+    }
+
+    return (new_fd);
+}
+
+/*
+ * sip_tcp_newmsg_to_spi()
+ * Description : This routine is called to parse a tcp packet
+ * and send it to the spi for further processing.
+ *
+ * Input : buf: pointer to the message
+ *         nbytes: length of the message
+ *         connID: connid over which the message was received
+ *
+ * Output : success / failure in processing
+ */
+static int
+sip_tcp_newmsg_to_spi (char *buf, unsigned long nbytes, int connID)
+{
+    static const char *fname = "sip_tcp_newmsg_to_spi";
+    sipMessage_t     *sip_msg;
+    ccsipRet_e        val;
+    cpr_sockaddr_storage   from;
+    char             *disply_msg_buff = NULL;
+    char            **display_msg_buff_p;
+    boolean           error;
+    cpr_ip_addr_t   ip_addr;
+
+    CPR_IP_ADDR_INIT(ip_addr);
+
+    /* Set up display msg. if debug msg. is enabled */
+    if (SipDebugMessage) {
+        display_msg_buff_p = &disply_msg_buff;
+    } else {
+        /* No display msg. is needed */
+        display_msg_buff_p = NULL;
+    }
+
+    do {
+        disply_msg_buff = NULL;
+        error = FALSE;
+        val = ccsip_process_network_message(&sip_msg, &buf, &nbytes,
+                                            display_msg_buff_p);
+
+        switch (val) {
+        case SIP_SUCCESS:
+            /*
+             * Print the received TCP packet info
+             */
+            if (disply_msg_buff != NULL) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"RCV: TCP message=\n", fname);
+                platform_print_sip_msg(disply_msg_buff);
+            }
+            from = sip_tcp_conn_tab[connID].addr;
+
+            util_extract_ip(&ip_addr, &from);
+            ccsip_dump_recv_msg_info(sip_msg, &ip_addr, 0);
+            /* Process SIP message */
+            SIPTaskProcessTCPMessage(sip_msg, from);
+            break;
+
+        case SIP_MSG_INCOMPLETE_ERR:
+
+            sip_tcp_conn_tab[connID].prev_msg = cpr_strdup(buf);
+            if (sip_tcp_conn_tab[connID].prev_msg) {
+                sip_tcp_conn_tab[connID].prev_bytes = nbytes;
+            }
+            sip_tcp_incomplete_msg++;
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SIP Incomplete message.%d\n",fname,
+                sip_tcp_incomplete_msg);
+            error = TRUE;
+            break;
+
+        case SIP_MSG_PARSE_ERR:
+            /*
+             * Print the received TCP packet info
+             */
+            if (disply_msg_buff != NULL) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"RCV: TCP message=\n", fname);
+                platform_print_sip_msg(disply_msg_buff);
+            }
+            sip_tcp_fail_network_msg++;
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SIP Message Parse error %d.\n", fname,
+                sip_tcp_fail_network_msg);
+            error = TRUE;
+            break;
+
+        case SIP_MSG_CREATE_ERR:
+        default:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SIP Message create error.\n", fname);
+            error = TRUE;
+            break;
+        }
+
+        /* Free display msg. buffer if it is allocated */
+        if (disply_msg_buff != NULL) {
+            cpr_free(disply_msg_buff);
+            disply_msg_buff = NULL;
+        }
+
+        if (error) {
+            /* There was an error encountered, exit */
+            return -1;
+        }
+    } while (nbytes > 0);
+    return 0;
+}
+
+void
+sip_tcp_createconnfailed_to_spi (cpr_ip_addr_t *ipaddr,
+                                 uint16_t port,
+                                 void *context,
+                                 ccsipSockErrCodes_e errcode,
+                                 int connid)
+{
+    static const char *fname = "sip_tcp_createconnfailed_to_spi";
+    ccsipCCB_t *ccb = NULL;
+    ti_config_table_t *ccm_active_table_entry = NULL,
+                      *ccm_standby_table_entry = NULL,
+                      *ccm_table_entry = NULL;
+    ti_common_t *active_ti_common = NULL;
+    ti_common_t *standby_ti_common = NULL;
+    uint32_t retx_value;
+    char            ip_addr_str[MAX_IPADDR_STR_LEN];
+
+
+    /*
+     * Use LINE1 for now as there is only one type of cc
+     * supported, no mixed mode. Will have to change when
+     * we add mixed mode cc support on the phone.
+     */
+    if (CC_Config_Table[LINE1].cc_type == CC_CCM) {
+
+        /*
+         * Check and see which tcp link went down, active /
+         * standby and get the appropriate reg ccb.
+         * Using ipaddr:port combination to find if the active
+         * or the standby went down. To use fd we will have to
+         * write a routine to return connid of the tcp conn table
+         * for an ipaddr:port combination.
+         */
+        ccm_active_table_entry = CCM_Active_Standby_Table.active_ccm_entry;
+        ccm_standby_table_entry = CCM_Active_Standby_Table.standby_ccm_entry;
+        if (ccm_active_table_entry) {
+            active_ti_common = &ccm_active_table_entry->ti_common;
+        }
+        if (ccm_standby_table_entry) {
+            standby_ti_common = &ccm_standby_table_entry->ti_common;
+        }
+
+        ipaddr2dotted(ip_addr_str, ipaddr);
+        if (active_ti_common && util_compare_ip(&(active_ti_common->addr), ipaddr) &&
+            active_ti_common->port == port) {
+            /*
+             * Active link has gone down
+             */
+               int last_cpr_err = cpr_errno;
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Active server going down due to "
+                "%s. ip_addr:%s\n", DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname),
+                last_cpr_err == CPR_ETIMEDOUT ? "ETIMEDOUT":
+                last_cpr_err == CPR_ECONNABORTED ? "ECONNABORTED":
+                last_cpr_err == CPR_ECONNRESET ? "CM_RESET_TCP": "CM_CLOSED_TCP",
+                 ip_addr_str);
+
+            ccb = sip_sm_get_ccb_by_index(REG_CCB_START);
+            ccm_table_entry = ccm_active_table_entry;
+
+        } else if (standby_ti_common &&
+                    util_compare_ip(&(standby_ti_common->addr), ipaddr) &&
+                   standby_ti_common->port == port) {
+            /*
+             * Standby link has gone down
+             */
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Standby server going down "
+                "ip_addr=%s\n",
+                DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname),
+                 ip_addr_str);
+
+            ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+            ccm_table_entry = ccm_standby_table_entry;
+        } else {
+            /*
+             * Neither of the links, just return.
+             * This could happen with fall back ccm
+             */
+            ccsipCCB_t *ccb_of_fallback = NULL;
+
+            // Find fallback ccb and set the socket handle INVALID
+            if (sip_regmgr_find_fallback_ccb_by_addr_port(ipaddr, port,
+                                                          &ccb_of_fallback)) {
+                if (ccb_of_fallback && (ccb_of_fallback->cc_cfg_table_entry)) {
+                    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Fallback server going "
+                        " down ip_addr=%s\n",
+                        DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname),
+                         ip_addr_str);
+                    sip_tcp_purge_entry(connid);
+                    sipTransportSetServerHandleAndPort(INVALID_SOCKET, 0,
+                        (ti_config_table_t *)ccb_of_fallback->cc_cfg_table_entry);
+                }
+            } else {
+                sipTransportClearServerHandle(ipaddr, port, connid);
+            }
+            return;
+        }
+        /*
+         * In this case we need to make sure any pending
+         * calls are cleared as well... Need to make sure
+         * we clear that, because we are setting the local port
+         * to zero. So any one-time udp message that needs to get
+         * sent will have the port as 0 in the contact header.
+         */
+        if (ccm_table_entry) {
+            sip_tcp_purge_entry(connid);
+            sipTransportSetServerHandleAndPort(INVALID_SOCKET, 0,
+                        (ti_config_table_t *) (ccm_table_entry));
+        } else {
+            sipTransportClearServerHandle(ipaddr, port, connid);
+        }
+        /*
+         * Set the retx_counter to the configured retx_value
+         * so no more retries happen
+         */
+        if (ccb != NULL) {
+                       config_get_value(CFGID_SIP_RETX, &retx_value, sizeof(retx_value));
+                   ccb->retx_counter = retx_value + 1;
+                   CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"send a SIP_TMR_REG_RETRY"
+                       "message so this cucm ip:%s can be put in fallback list \n",
+                       DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname), ip_addr_str);
+                   if (ccsip_register_send_msg(SIP_TMR_REG_RETRY, ccb->index)
+                           != SIP_REG_OK) {
+                       CCSIP_DEBUG_ERROR(SIP_F_PREFIX"REG send message failed.\n", fname);
+                       ccsip_register_cleanup(ccb, TRUE);
+                   }
+
+        }
+    }
+}
+
+/*
+ * sip_tcp_read_socket()
+ * Description : After we come out of select call, for every valid socket in
+ * the connection table it
+ *         1. Checks it for readability
+ *         2. If it is readable, either accept the incoming connect (for master
+ *            port) or receive data from network
+ *         3. Processes the data received from the network.
+ *
+ * Input : Value of read mask after call to socket_select()
+ *
+ * Output : Nothing
+ */
+void
+sip_tcp_read_socket (cpr_socket_t this_fd)
+{
+    int nbytes;
+    char *sip_tcp_buf;
+    char temp;
+    int connid;
+    const char *fname="sip_tcp_read_socket";
+    boolean secure;
+
+    connid = sip_tcp_fd_to_connid(this_fd);
+    if (connid == -1) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Read failed for unknown socket %d.\n",
+                        fname, this_fd);
+        return;
+    }
+    secure = ccsipIsSecureType(connid);
+
+    if (sip_tcp_conn_tab[connid].state == SOCK_CONNECT_PENDING) {
+        int bytes_read = 0;
+
+        /* This socket is now readable, connection complete.
+         * Inform SIP SPI
+         * Do a dummy read, if it is successful, change state
+         */
+        bytes_read = sipSocketRecv(this_fd, &temp, 0, 0, secure);
+        if ((bytes_read != -1) || (errno == EWOULDBLOCK)) {
+            sip_tcp_conn_tab[connid].state = SOCK_CONNECTED;
+        } else if (errno == ENOTCONN) {
+            sip_tcp_conn_tab[connid].dirtyFlag = TRUE;
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"socket error=%d=\n", fname, errno);
+            sip_tcp_createconnfailed_to_spi(&(sip_tcp_conn_tab[connid].ipaddr),
+                                            sip_tcp_conn_tab[connid].port,
+                                            sip_tcp_conn_tab[connid].context,
+                                            SOCKET_CONNECT_ERROR, connid);
+            return;
+        }
+    } else {
+        unsigned long offset = sip_tcp_conn_tab[connid].prev_bytes;
+
+        if (offset) {
+            sip_tcp_buf = (char *) cpr_realloc(sip_tcp_conn_tab[connid].prev_msg,
+                                               (offset + CPR_MAX_MSG_SIZE + 1));
+            sip_tcp_conn_tab[connid].prev_bytes = 0;
+
+            if (sip_tcp_buf == NULL) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to realloc tcp_msg buffer memory.\n",
+                                    fname);
+                cpr_free(sip_tcp_conn_tab[connid].prev_msg);
+                sip_tcp_conn_tab[connid].prev_msg = NULL;
+                return;
+            }
+
+            nbytes = sipSocketRecv(this_fd, &sip_tcp_buf[offset],
+                             CPR_MAX_MSG_SIZE, 0, secure);
+
+            /* Ensure that we dont have too much buffered which may
+             * be due to some error conditions.
+             */
+            if ((nbytes + offset) > (MAX_PAYLOAD_SIZE + 1)) {
+
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Total SIP message size of %d "
+                                  "bytes exceeds maximum of %d bytes",
+                                  fname, (nbytes + offset),
+                                  (MAX_PAYLOAD_SIZE + 1));
+
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Dropping SIP message %s",
+                                    fname, sip_tcp_buf);
+                cpr_free(sip_tcp_buf);
+                return;
+            }
+        } else {
+            sip_tcp_buf = (char *) cpr_malloc(CPR_MAX_MSG_SIZE + 1);
+            if (sip_tcp_buf == NULL) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to malloc tcp_msg buffer memory.\n",
+                                    fname);
+                return;
+            }
+            nbytes = sipSocketRecv(this_fd, sip_tcp_buf, CPR_MAX_MSG_SIZE, 0, secure);
+        }
+
+        if (nbytes > 0) {
+            nbytes += offset;
+            sip_tcp_buf[nbytes] = 0;
+            (void) sip_tcp_newmsg_to_spi(sip_tcp_buf, nbytes, connid);
+        } else if ((nbytes == 0) ||
+                   ((nbytes == -1) && (errno != EWOULDBLOCK))) {
+            /*
+             * Remote connection closure or broken pipe - post a message
+             * to sip transport and wait for connection close command.
+             */
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"CUCM closed TCP connection.\n",
+                DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname));
+            sip_tcp_conn_tab[connid].error_cause = SOCKET_REMOTE_CLOSURE;
+
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"socket error=%d=\n", fname, errno);
+
+            sip_tcp_createconnfailed_to_spi(&(sip_tcp_conn_tab[connid].ipaddr),
+                                        sip_tcp_conn_tab[connid].port,
+                                        sip_tcp_conn_tab[connid].context,
+                                        SOCKET_CONNECT_ERROR, connid);
+            // Clear proxy handle
+            if (CC_Config_Table[LINE1].cc_type != CC_CCM) {
+                sipTransportCSPSClearProxyHandle(&(sip_tcp_conn_tab[connid].ipaddr),
+                                                 sip_tcp_conn_tab[connid].port,
+                                                 this_fd);
+                sip_tcp_purge_entry(connid);
+            }
+        }
+        cpr_free(sip_tcp_buf);
+    }
+}
+
+/*
+ ** sip_tcp_find_msg
+ *
+ *  FILENAME: ip_phone\sip\ccsip_platform_tcp.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS:
+ *
+ */
+sll_match_e
+sip_tcp_find_msg (void *find_by_p, void *data_p)
+{
+    return (SLL_MATCH_FOUND);
+}
+
+/* Socket is not writable. Queue the data to be sent.
+ *
+ * Inputs:
+ *   total_len  - total length of message
+ *   connid     - connection id
+ *   buf        - buffer of data to be written (this is the entire
+ *                contents not just the remaining data. We do this
+ *                so we can display the entire message when the last
+ *                chunk is written.
+ *  send_msg_display - boolean indicating whether the sent message
+ *                      is to be displayed for debugging etc.
+ */
+static void
+sipTcpQueueSendData (int total_len, int connid,
+                     char *buf, void *context, boolean send_msg_display,
+                     uint8_t ip_sig_tos)
+{
+    static const char *fname = "sipTcpQueueSendData";
+    ccsipTCPSendData_t *sendData;
+    sip_tcp_conn_t *entry;
+    int send_msg_q_size = 0;
+
+    entry = sip_tcp_conn_tab + connid;
+    if (entry->sendQueue == NULL) {
+        entry->sendQueue = sll_create(sip_tcp_find_msg);
+        if (entry->sendQueue == NULL) {
+            CCSIP_DEBUG_ERROR("%s Failed to create sendQueue to buffer data!\n", fname);
+            return;
+        }
+    }
+
+    sendData = (ccsipTCPSendData_t *) cpr_malloc(sizeof(ccsipTCPSendData_t));
+    if (sendData == NULL) {
+        CCSIP_DEBUG_ERROR("%s Failed to allocate memory for sendData!\n", fname);
+        return;
+    }
+    memset(sendData, 0, sizeof(ccsipTCPSendData_t));
+
+    sendData->data = (char *) cpr_malloc(total_len + 1);
+
+    if (sendData->data) {
+        sstrncpy(sendData->data, buf, total_len);
+    } else {
+        CCSIP_DEBUG_ERROR("%s Failed to allocate memory for sendData->data!\n", fname);
+        cpr_free(sendData);
+        return;
+    }
+
+    sendData->bytesSent = 0;
+    sendData->bytesLeft = (uint16_t) total_len;
+    sendData->context = context;
+    sendData->msg_display = send_msg_display;
+    sendData->ip_sig_tos = ip_sig_tos;
+    (void) sll_append(entry->sendQueue, sendData);
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Data queued length %d\n",
+                          DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname), total_len);
+
+    if (send_msg_q_size > max_tcp_send_msg_q_size) {
+        max_tcp_send_msg_q_size   = send_msg_q_size;
+        max_tcp_send_msg_q_connid = connid;
+        max_tcp_send_msg_q_ipaddr = entry->ipaddr;
+        max_tcp_send_msg_q_port   = entry->port;
+    }
+}
+
+/*
+ * Free memory for any queued write data.
+ */
+void
+sipTcpFlushRetrySendQueue (sip_tcp_conn_t *entry)
+{
+    ccsipTCPSendData_t *sendData = NULL;
+
+    if (entry->sendQueue) {
+        sendData = (ccsipTCPSendData_t *) sll_next(entry->sendQueue, sendData);
+        while (sendData) {
+            cpr_free(sendData->data);
+            (void) sll_remove(entry->sendQueue, sendData);
+            cpr_free(sendData);
+            sendData = (ccsipTCPSendData_t *) sll_next(entry->sendQueue, NULL);
+        }
+        (void) sll_destroy(entry->sendQueue);
+        entry->sendQueue = NULL;
+    }
+}
+
+void
+sip_tcp_resend (int connid)
+{
+    static const char *fname = "sip_tcp_resend";
+    ccsipTCPSendData_t *qElem = NULL;
+    sip_tcp_conn_t *entry;
+    int bytes_sent;
+    boolean secure;
+
+    if (!VALID_CONNID(connid)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Resend failed for unknown socket %d.\n",
+                        fname, connid);
+        return;
+    }
+
+    secure = ccsipIsSecureType(connid);
+
+    entry = sip_tcp_conn_tab + connid;
+    if (entry->sendQueue) {
+        qElem = (ccsipTCPSendData_t *) sll_next(entry->sendQueue, NULL);
+//        ccsipSocketSetIPtos(entry->fd, qElem->ip_sig_tos);
+        while (qElem) {
+            while (qElem->bytesLeft) {
+                bytes_sent = sipSocketSend(entry->fd, &qElem->data[qElem->bytesSent],
+                                     qElem->bytesLeft, 0, secure);
+                if (bytes_sent > 0) {
+                    qElem->bytesSent += bytes_sent;
+                    qElem->bytesLeft -= bytes_sent;
+                } else {
+                    if (errno == EWOULDBLOCK) {
+                        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Socket blocked requeue data\n",
+                                              DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname));
+                    } else {
+                        entry->error_cause = SOCKET_SEND_ERROR;
+                        sipTcpFlushRetrySendQueue(entry);
+                        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"socket error=%d=\n", fname, errno);
+                        sip_tcp_createconnfailed_to_spi(
+                            &(sip_tcp_conn_tab[connid].ipaddr),
+                            sip_tcp_conn_tab[connid].port,
+                            sip_tcp_conn_tab[connid].context,
+                            SOCKET_CONNECT_ERROR, connid);
+                        CCSIP_DEBUG_ERROR("%s: Socket send error."
+                                          "Purge queued entry data.\n", fname);
+                    }
+                    return;
+                }
+            } /* while (qElem->bytesLeft) */
+
+            cpr_free(qElem->data);
+            (void) sll_remove(entry->sendQueue, qElem);
+            cpr_free(qElem);
+            CCSIP_DEBUG_REG_STATE("%s: sent out successfully, dequeue an entry.\n", fname);
+
+            qElem = (ccsipTCPSendData_t *) sll_next(entry->sendQueue, NULL);
+        } /* while qElem */
+    }
+}
+
+
+/*
+ * sip_tcp_channel_send()
+ * Description : This routine is called to send out a tcp message
+ *
+ *
+ * Input : s: socket to send the message over
+ *         buf: message to send
+ *         len: length of the message
+ *
+ * Output : success / failure in processing
+ */
+int
+sip_tcp_channel_send (cpr_socket_t s, char *buf, uint32_t len)
+{
+    static const char *fname = "sip_tcp_channel_send";
+    int bytesSent = 0, totalBytesSent = 0;
+    int connid = 0;
+    sip_tcp_conn_t *entry;
+    boolean secure;
+
+    /* convert socket to connid */
+    connid = sip_tcp_fd_to_connid(s);
+    if (!VALID_CONNID(connid)) {
+        CCSIP_DEBUG_ERROR("%s: Couldn't map socket to a valid connid!\n", fname);
+        return SIP_TCP_SEND_ERROR;
+    }
+    /* use connid we can get this socket's entry in sip_tcp_conn_tab[] */
+    entry = sip_tcp_conn_tab + connid;
+
+    /* secd requires that the socket should be in connected state
+     * to send message. Return if the status is pending.
+     * Currently this gets control in fallback state. The retry timer
+     * event in fallback state will resend the message.
+     */
+    if ((sip_tcp_conn_tab[connid].soc_type == SIP_SOC_TLS) &&
+        (sip_tcp_conn_tab[connid].state == SOCK_CONNECT_PENDING)) {
+        plat_soc_connect_status_e conn_status;
+        conn_status = platSecSockIsConnected(s);
+        if (conn_status == PLAT_SOCK_CONN_OK) {
+            sip_tcp_conn_tab[connid].state = SOCK_CONNECTED;
+
+        } else if (conn_status == PLAT_SOCK_CONN_WAITING) {
+
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"tls socket waiting %d\n", DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname), s);
+            return SIP_TCP_SEND_OK;
+
+        } else if (conn_status == PLAT_SOCK_CONN_FAILED) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"socket error=%d=\n", fname, errno);
+            sip_tcp_createconnfailed_to_spi(&(sip_tcp_conn_tab[connid].ipaddr),
+                                            sip_tcp_conn_tab[connid].port,
+                                            sip_tcp_conn_tab[connid].context,
+                                            SOCKET_CONNECT_ERROR, connid);
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"TLS socket connect failed %d\n",
+                              fname, s);
+            return SIP_TCP_SEND_ERROR;
+        }
+    }
+
+    /*
+     * Check not exceeding max allowed payload size
+     */
+    if (len >= MAX_PAYLOAD_SIZE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_TCP_PAYLOAD_TOO_LARGE),
+                          fname, len, CPR_MAX_MSG_SIZE);
+        return SIP_TCP_SIZE_ERROR;
+    }
+
+    /*
+     * Check to see if the send q is empty. If it is not, then
+     * queue the current message in the sendqueue so that it
+     * gets sent in order when the socket is ready.
+     */
+    if (entry->sendQueue && sll_count(entry->sendQueue)) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"%d Socket waiting on EWOULDBLOCK, "
+                              " queueing data\n", DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname), connid);
+        sipTcpQueueSendData(len, connid, buf, NULL, TRUE, 0x0);
+        return SIP_TCP_SEND_OK;
+    }
+
+    secure = ccsipIsSecureType(connid);
+
+    while (len > 0) {
+        bytesSent = sipSocketSend(s, (void *)buf, (size_t) len, 0, secure);
+        if (bytesSent == SOCKET_ERROR) {
+            if (cpr_errno == CPR_EWOULDBLOCK) {
+                    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"%d Socket EWOULDBLOCK while "
+                                          "sending, queueing data\n", DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname),
+                                          connid);
+                    sipTcpQueueSendData(len, connid, buf, NULL,
+                                        TRUE, 0x0);
+                    break;
+            }
+            if (cpr_errno != CPR_ENOTCONN) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"socket error=%d=\n", fname, errno);
+                sip_tcp_createconnfailed_to_spi(&(sip_tcp_conn_tab[connid].ipaddr),
+                                                sip_tcp_conn_tab[connid].port,
+                                                sip_tcp_conn_tab[connid].context,
+                                                SOCKET_CONNECT_ERROR, connid);
+            }
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "sipSocketSend", cpr_errno);
+            return (cpr_errno== CPR_ENOTCONN? cpr_errno: SIP_TCP_SEND_ERROR);
+        }
+        len -= bytesSent;
+        totalBytesSent += bytesSent;
+        buf += bytesSent;
+    }
+    return SIP_TCP_SEND_OK;
+}
+
+/*
+ * Free memory for any queued write data.
+ */
+void
+sipTcpFreeSendQueue (int connid)
+{
+    static const char *fname = "sipTcpFreeSendQueue";
+    sip_tcp_conn_t *entry;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Free TCP send queue for connid %d \n",
+                        DEB_F_PREFIX_ARGS(SIP_TCP_MSG, fname), connid);
+    if (!VALID_CONNID(connid)) {
+        return;
+    }
+    entry = sip_tcp_conn_tab + connid;
+    sipTcpFlushRetrySendQueue(entry);
+}
+
+/*
+ * sip_tcp_create_conn_using_blocking_socket()
+ * Description : This routine is called to create blocking socket
+ * request from SIP_SPI to SIP_TCP.
+ *
+ * Input : spi_msg - Pointer to sipSPIMessage_t containing the create conn
+ * parameters.
+ *
+ * Output : socket fd
+ */
+cpr_socket_t
+sip_tcp_create_conn_using_blocking_socket (sipSPIMessage_t *spi_msg) {
+
+       cpr_socket_t server_conn_handle = INVALID_SOCKET;
+
+       server_conn_handle = sip_tcp_create_connection(spi_msg);
+
+       return server_conn_handle;
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_platform_timers.c b/libs/sipcc/core/sipstack/ccsip_platform_timers.c
new file mode 100644 (file)
index 0000000..7de215d
--- /dev/null
@@ -0,0 +1,989 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_string.h"
+#include "cpr_memory.h"
+#include "phntask.h"
+#include "text_strings.h"
+#include "ccsip_platform.h"
+#include "phone_debug.h"
+#include "ccsip_task.h"
+#include "ccsip_pmh.h"
+#include "ccsip_register.h"
+#include "ccsip_core.h"
+#include "ccsip_subsmanager.h"
+
+/*
+ * Constants
+ */
+
+/*
+ * Globals TODO: hang off of a single SIP global
+ */
+sipPlatformUITimer_t sipPlatformUISMTimers[MAX_CCBS];
+sipPlatformUIExpiresTimer_t sipPlatformUISMExpiresTimers[MAX_CCBS];
+sipPlatformUIExpiresTimer_t sipPlatformUISMRegExpiresTimers[MAX_CCBS];
+sipPlatformUIExpiresTimer_t sipPlatformUISMLocalExpiresTimers[MAX_CCBS];
+// This timer will kick in after 1xx in releasing state so if 2xx gets lost
+// we will be able to kill the ccb
+sipPlatformSupervisionTimer_t sipPlatformSupervisionTimers[MAX_TEL_LINES];
+
+sipPlatformUITimer_t sipPlatformUISMSubNotTimers[MAX_SCBS];
+sipPlatformSupervisionTimer_t sipPlatformSubNotPeriodicTimer;
+static cprTimer_t sipPlatformRegAllFailedTimer;
+static cprTimer_t sipPlatformNotifyTimer;
+static cprTimer_t sipPlatformStandbyKeepaliveTimer;
+static cprTimer_t sipPlatformUnRegistrationTimer;
+static cprTimer_t sipPassThroughTimer;
+int
+sip_platform_timers_init (void)
+{
+    static const char fname[] = "sip_platform_timers_init";
+    static const char sipMsgTimerName[] = "sipMsg";
+    static const char sipExpireTimerName[] = "sipExp";
+    static const char sipRegTimeOutTimerName[] = "sipRegTimeout";
+    static const char sipRegExpireTimerName[] = "sipRegExp";
+    static const char sipLocalExpireTimerName[] = "sipLocalExp";
+    static const char sipSupervisionTimerName[] = "sipSupervision";
+    static const char sipSubNotTimerName[] = "sipSubNot";
+    static const char sipSubNotPeriodicTimerName[] = "sipSubNotPeriodic";
+    static const char sipRegAllFailedTimerName[] = "sipRegAllFailed";
+    static const char sipNotifyTimerName[] = "sipNotify";
+    static const char sipStandbyKeepaliveTimerName[] = "sipStandbyKeepalive";
+    static const char sipUnregistrationTimerName[] = "sipUnregistration";
+       static const char sipPassThroughTimerName[] = "sipPassThrough";
+
+    int i;
+
+    for (i = 0; i < MAX_CCBS; i++) {
+        sipPlatformUISMTimers[i].timer =
+            cprCreateTimer(sipMsgTimerName,
+                           SIP_MSG_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+
+        sipPlatformUISMTimers[i].reg_timer =
+            cprCreateTimer(sipRegTimeOutTimerName,
+                           SIP_REG_TIMEOUT_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+
+        sipPlatformUISMExpiresTimers[i].timer =
+            cprCreateTimer(sipExpireTimerName,
+                           SIP_EXPIRES_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+
+        sipPlatformUISMRegExpiresTimers[i].timer =
+            cprCreateTimer(sipRegExpireTimerName,
+                           SIP_REG_EXPIRES_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+
+        sipPlatformUISMLocalExpiresTimers[i].timer =
+            cprCreateTimer(sipLocalExpireTimerName,
+                           SIP_LOCAL_EXPIRES_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+
+        if (!sipPlatformUISMTimers[i].timer ||
+            !sipPlatformUISMTimers[i].reg_timer ||
+            !sipPlatformUISMExpiresTimers[i].timer ||
+            !sipPlatformUISMRegExpiresTimers[i].timer ||
+            !sipPlatformUISMLocalExpiresTimers[i].timer) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                              "Failed to create one or more"
+                              " UISM timers: %d\n", fname, i);
+            return SIP_ERROR;
+        }
+    }
+    for (i = 0; i < MAX_TEL_LINES; i++) {
+        sipPlatformSupervisionTimers[i].timer =
+            cprCreateTimer(sipSupervisionTimerName,
+                           SIP_SUPERVISION_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+    }
+    for (i = 0; i < MAX_SCBS; i++) {
+        sipPlatformUISMSubNotTimers[i].timer =
+            cprCreateTimer(sipSubNotTimerName,
+                           SIP_SUBNOT_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+
+        if (!sipPlatformUISMSubNotTimers[i].timer) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                              "Failed to create Sub/Not"
+                              " UISM timers: %d\n", fname, i);
+            return SIP_ERROR;
+        }
+    }
+    sipPlatformSubNotPeriodicTimer.timer =
+        cprCreateTimer(sipSubNotPeriodicTimerName,
+                       SIP_SUBNOT_PERIODIC_TIMER,
+                       TIMER_EXPIRATION,
+                       sip_msgq);
+
+    if (!sipPlatformSubNotPeriodicTimer.timer) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "Failed to create supervision timer: %d\n",
+                          fname, i);
+        return SIP_ERROR;
+    }
+
+    sipPlatformRegAllFailedTimer =
+        cprCreateTimer(sipRegAllFailedTimerName,
+                       SIP_REGALLFAIL_TIMER,
+                       TIMER_EXPIRATION,
+                       sip_msgq);
+    if (!sipPlatformRegAllFailedTimer) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "Failed to create RegAllFailed timer\n", fname);
+        return SIP_ERROR;
+    }
+    /*
+     * Create the standby cc keepalive timer used by the
+     * registration Manager.
+     */
+    sipPlatformStandbyKeepaliveTimer =
+            cprCreateTimer(sipStandbyKeepaliveTimerName,
+                           SIP_KEEPALIVE_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+
+    if (!sipPlatformStandbyKeepaliveTimer) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "Failed to create Standby"
+                          " keepalive timer\n", fname);
+        return SIP_ERROR;
+    }
+    sipPlatformUnRegistrationTimer =
+            cprCreateTimer(sipUnregistrationTimerName,
+                           SIP_UNREGISTRATION_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+    if (!sipPlatformUnRegistrationTimer) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "Failed to create Stanby keepalive timer\n",
+                          fname);
+        return SIP_ERROR;
+    }
+    sipPlatformNotifyTimer =
+            cprCreateTimer(sipNotifyTimerName,
+                           SIP_NOTIFY_TIMER,
+                           TIMER_EXPIRATION,
+                           sip_msgq);
+    if (!sipPlatformNotifyTimer) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "Failed to create Notify timer\n", fname);
+        return SIP_ERROR;
+    }
+
+       sipPassThroughTimer =
+               cprCreateTimer(sipPassThroughTimerName,
+                                          SIP_PASSTHROUGH_TIMER,
+                                          TIMER_EXPIRATION,
+                                          sip_msgq);
+       if (!sipPassThroughTimer) {
+               CCSIP_DEBUG_ERROR("%s: failed to create sip PassThrough timer\n", fname);
+               return SIP_ERROR;
+       }
+
+    return SIP_OK;
+}
+
+void
+sip_platform_timers_shutdown (void)
+{
+    int i;
+
+    for (i = 0; i < MAX_CCBS; i++) {
+        sip_platform_msg_timer_stop(i);
+        (void) cprDestroyTimer(sipPlatformUISMTimers[i].timer);
+        sipPlatformUISMTimers[i].timer = NULL;
+        (void) cprDestroyTimer(sipPlatformUISMTimers[i].reg_timer);
+        sipPlatformUISMTimers[i].reg_timer = NULL;
+
+        (void) sip_platform_expires_timer_stop(i);
+        (void) cprDestroyTimer(sipPlatformUISMExpiresTimers[i].timer);
+        sipPlatformUISMExpiresTimers[i].timer = NULL;
+
+        (void) sip_platform_register_expires_timer_stop(i);
+        (void) cprDestroyTimer(sipPlatformUISMRegExpiresTimers[i].timer);
+        sipPlatformUISMRegExpiresTimers[i].timer = NULL;
+
+        (void) sip_platform_localexpires_timer_stop(i);
+        (void) cprDestroyTimer(sipPlatformUISMLocalExpiresTimers[i].timer);
+        sipPlatformUISMLocalExpiresTimers[i].timer = NULL;
+    }
+
+    for (i = 0; i < MAX_TEL_LINES; i++) {
+        (void) sip_platform_supervision_disconnect_timer_stop(i);
+        (void) cprDestroyTimer(sipPlatformSupervisionTimers[i].timer);
+        sipPlatformSupervisionTimers[i].timer = NULL;
+    }
+
+    for (i = 0; i < MAX_SCBS; i++) {
+        sip_platform_msg_timer_subnot_stop(&sipPlatformUISMSubNotTimers[i]);
+        (void) cprDestroyTimer(sipPlatformUISMSubNotTimers[i].timer);
+        sipPlatformUISMSubNotTimers[i].timer = NULL;
+    }
+    (void) sip_platform_subnot_periodic_timer_stop();
+    (void) cprDestroyTimer(sipPlatformSubNotPeriodicTimer.timer);
+    sipPlatformSubNotPeriodicTimer.timer = NULL;
+    (void) sip_platform_reg_all_fail_timer_stop();
+    (void) cprDestroyTimer(sipPlatformRegAllFailedTimer);
+    sipPlatformRegAllFailedTimer = NULL;
+    (void) sip_platform_standby_keepalive_timer_stop();
+    (void) cprDestroyTimer(sipPlatformStandbyKeepaliveTimer);
+    sipPlatformStandbyKeepaliveTimer = NULL;
+    (void) sip_platform_unregistration_timer_stop();
+    (void) cprDestroyTimer(sipPlatformUnRegistrationTimer);
+    sipPlatformUnRegistrationTimer = NULL;
+    (void) sip_platform_notify_timer_stop();
+    (void) cprDestroyTimer(sipPlatformNotifyTimer);
+    sipPlatformNotifyTimer = NULL;
+       (void) sip_platform_pass_through_timer_stop();
+       (void) cprDestroyTimer(sipPassThroughTimer);
+       sipPassThroughTimer = NULL;
+}
+
+/********************************************************
+ *
+ * Message timer support functions for SIP SM
+ *
+ ********************************************************/
+void
+sip_platform_msg_timers_init (void)
+{
+    static const char fname[] = "sip_platform_msg_timers_init";
+    static long timer_init_complete = 0;
+    int i;
+    cprTimer_t timer, reg_timer;
+
+    for (i = 0; i < MAX_CCBS; i++) {
+        if (timer_init_complete) {
+            if ((cprCancelTimer(sipPlatformUISMTimers[i].timer)
+                    == CPR_FAILURE) ||
+                (cprCancelTimer(sipPlatformUISMTimers[i].reg_timer)
+                    == CPR_FAILURE)) {
+                CCSIP_DEBUG_STATE(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "cprCancelTimer");
+            }
+        }
+        timer = sipPlatformUISMTimers[i].timer;
+        reg_timer = sipPlatformUISMTimers[i].reg_timer;
+
+        if (sipPlatformUISMTimers[i].message_buffer != NULL) {
+            cpr_free(sipPlatformUISMTimers[i].message_buffer);
+            sipPlatformUISMTimers[i].message_buffer = NULL;
+            sipPlatformUISMTimers[i].message_buffer_len = 0;
+        }
+
+        memset(&sipPlatformUISMTimers[i], 0, sizeof(sipPlatformUITimer_t));
+        sipPlatformUISMTimers[i].timer = timer;
+        sipPlatformUISMTimers[i].reg_timer = reg_timer;
+    }
+    timer_init_complete = 1;
+    return;
+}
+
+
+int
+sip_platform_msg_timer_start (uint32_t msec,
+                              void *data,
+                              int idx,
+                              char *message_buffer,
+                              int message_buffer_len,
+                              int message_type,
+                              cpr_ip_addr_t *ipaddr,
+                              uint16_t port,
+                              boolean isRegister)
+{
+    static const char fname[] = "sip_platform_msg_timer_start";
+    cprTimer_t timer;
+
+    /* validate index */
+    if ((idx < MIN_TEL_LINES) || (idx >= MAX_CCBS)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_LINE_NUMBER_INVALID),
+                          fname, idx);
+        return SIP_ERROR;
+    }
+
+    /* validate length */
+    if (message_buffer_len >= SIP_UDP_MESSAGE_SIZE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_MSG_BUFFER_TOO_BIG),
+                          fname, message_buffer_len);
+        return SIP_ERROR;
+    }
+
+    /* stop the timer if it is running */
+    if (cprCancelTimer(sipPlatformUISMTimers[idx].timer) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+    if (cprCancelTimer(sipPlatformUISMTimers[idx].reg_timer) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+
+    if (sipPlatformUISMTimers[idx].message_buffer == NULL) {
+        sipPlatformUISMTimers[idx].message_buffer = (char *)cpr_malloc(message_buffer_len+1);
+        if (sipPlatformUISMTimers[idx].message_buffer == NULL) return SIP_ERROR;
+    }
+    else if (message_buffer != sipPlatformUISMTimers[idx].message_buffer) {
+        cpr_free(sipPlatformUISMTimers[idx].message_buffer);
+        sipPlatformUISMTimers[idx].message_buffer = (char *)cpr_malloc(message_buffer_len+1);
+        if (sipPlatformUISMTimers[idx].message_buffer == NULL) return SIP_ERROR;
+    }
+
+    sipPlatformUISMTimers[idx].message_buffer_len = message_buffer_len;
+    sipPlatformUISMTimers[idx].message_buffer[message_buffer_len] = '\0';
+    memcpy(sipPlatformUISMTimers[idx].message_buffer, message_buffer,
+           message_buffer_len);
+    sipPlatformUISMTimers[idx].message_type = (sipMethod_t) message_type;
+    sipPlatformUISMTimers[idx].ipaddr = *ipaddr;
+    sipPlatformUISMTimers[idx].port = port;
+
+    /* start the timer */
+    if (isRegister) {
+        timer = sipPlatformUISMTimers[idx].reg_timer;
+    } else {
+        timer = sipPlatformUISMTimers[idx].timer;
+    }
+
+    if (cprStartTimer(timer, msec, data) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprStartTimer");
+        cpr_free(sipPlatformUISMTimers[idx].message_buffer);
+        sipPlatformUISMTimers[idx].message_buffer = NULL;
+        sipPlatformUISMTimers[idx].message_buffer_len = 0;
+        return SIP_ERROR;
+    }
+    sipPlatformUISMTimers[idx].outstanding = TRUE;
+    return SIP_OK;
+}
+
+
+void
+sip_platform_msg_timer_stop (int idx)
+{
+    static const char fname[] = "sip_platform_msg_timer_stop";
+
+    if ((idx < MIN_TEL_LINES) || (idx >= MAX_CCBS)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_LINE_NUMBER_INVALID),
+                          fname, idx);
+        return;
+    }
+
+    if ((cprCancelTimer(sipPlatformUISMTimers[idx].timer) == CPR_FAILURE) ||
+        (cprCancelTimer(sipPlatformUISMTimers[idx].reg_timer) == CPR_FAILURE)) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprCancelTimer");
+        return;
+    }
+    sipPlatformUISMTimers[idx].outstanding = FALSE;
+}
+
+
+boolean
+sip_platform_msg_timer_outstanding_get (int idx)
+{
+    return sipPlatformUISMTimers[idx].outstanding;
+}
+
+
+void
+sip_platform_msg_timer_outstanding_set (int idx, boolean value)
+{
+    sipPlatformUISMTimers[idx].outstanding = value;
+}
+
+
+int
+sip_platform_msg_timer_update_destination (int idx,
+                                           cpr_ip_addr_t *ipaddr,
+                                           uint16_t port)
+{
+    static const char fname[] = "sip_platform_msg_timer_update_destination";
+
+    if ((idx < TEL_CCB_START) || (idx > REG_BACKUP_CCB)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_LINE_NUMBER_INVALID),
+                          fname, idx);
+        return SIP_ERROR;
+    }
+
+    if (ipaddr == NULL) {
+        sipPlatformUISMExpiresTimers[idx].ipaddr = ip_addr_invalid;
+    } else {
+        sipPlatformUISMTimers[idx].ipaddr = *ipaddr;
+    }
+
+    sipPlatformUISMTimers[idx].port = port;
+
+    return SIP_OK;
+}
+
+/********************************************************
+ *
+ * Expires timer support functions for SIP SM
+ *
+ ********************************************************/
+int
+sip_platform_expires_timer_start (uint32_t msec,
+                                  int idx,
+                                  cpr_ip_addr_t *ipaddr,
+                                  uint16_t port)
+{
+    static const char fname[] = "sip_platform_expires_timer_start";
+
+    if (sip_platform_expires_timer_stop(idx) == SIP_ERROR) {
+        return SIP_ERROR;
+    }
+
+    if (ipaddr == NULL) {
+        sipPlatformUISMExpiresTimers[idx].ipaddr = ip_addr_invalid;
+    } else {
+        sipPlatformUISMExpiresTimers[idx].ipaddr = *ipaddr;
+    }
+
+    sipPlatformUISMExpiresTimers[idx].port = port;
+
+    //sip_platform_expires_timer_callback
+    if (cprStartTimer(sipPlatformUISMExpiresTimers[idx].timer, msec,
+                      (void *) (long)idx) == CPR_FAILURE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprStartTimer");
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+}
+
+
+int
+sip_platform_expires_timer_stop (int idx)
+{
+    static const char fname[] = "sip_platform_expires_timer_stop";
+
+    if ((idx < MIN_TEL_LINES) || (idx >= MAX_CCBS)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_LINE_NUMBER_INVALID),
+                          fname, idx);
+        return SIP_ERROR;
+    }
+
+    if (cprCancelTimer(sipPlatformUISMExpiresTimers[idx].timer)
+            == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+}
+
+
+int
+sip_platform_register_expires_timer_start (uint32_t msec, int idx)
+{
+    static const char fname[] = "sip_platform_register_expires_timer_start";
+
+    if (sip_platform_register_expires_timer_stop(idx) == SIP_ERROR) {
+        return SIP_ERROR;
+    }
+
+    if (cprStartTimer(sipPlatformUISMRegExpiresTimers[idx].timer, msec,
+                      (void *)(long) idx) == CPR_FAILURE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprStartTimer");
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+}
+
+
+int
+sip_platform_register_expires_timer_stop (int idx)
+{
+    static const char fname[] = "sip_platform_register_expires_timer_stop";
+
+    if ((idx < MIN_TEL_LINES) || (idx >= MAX_CCBS)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_LINE_NUMBER_INVALID),
+                          fname, idx);
+        return SIP_ERROR;
+    }
+
+    if (cprCancelTimer(sipPlatformUISMRegExpiresTimers[idx].timer)
+            == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+}
+
+/********************************************************
+ *
+ * Local Expires timer support functions for SIP SM
+ *
+ ********************************************************/
+int
+sip_platform_localexpires_timer_start (uint32_t msec,
+                                       int idx,
+                                       cpr_ip_addr_t *ipaddr,
+                                       uint16_t port)
+{
+    static const char fname[] = "sip_platform_localexpires_timer_start";
+
+    if (sip_platform_localexpires_timer_stop(idx) == SIP_ERROR) {
+        return SIP_ERROR;
+    }
+
+    sipPlatformUISMLocalExpiresTimers[idx].ipaddr = *ipaddr;
+    sipPlatformUISMLocalExpiresTimers[idx].port = port;
+
+    //sip_platform_localexpires_timer_callback
+    if (cprStartTimer(sipPlatformUISMLocalExpiresTimers[idx].timer, msec,
+                      (void *)(long) idx) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprStartTimer");
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+}
+
+
+int
+sip_platform_localexpires_timer_stop (int idx)
+{
+    static const char fname[] = "sip_platform_localexpires_timer_stop";
+
+    if ((idx < MIN_TEL_LINES) || (idx >= MAX_CCBS)) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_LINE_NUMBER_INVALID),
+                          fname, idx);
+        return SIP_ERROR;
+    }
+
+    if (cprCancelTimer(sipPlatformUISMLocalExpiresTimers[idx].timer)
+            == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+}
+
+
+sipMethod_t
+sip_platform_msg_timer_messageType_get (int idx)
+{
+    if ((idx >= TEL_CCB_START) && (idx <= REG_BACKUP_CCB)) {
+        if (sipPlatformUISMTimers[idx].outstanding) {
+            return sipPlatformUISMTimers[idx].message_type;
+        }
+    }
+    return sipMethodUnknown;
+}
+
+/*
+ * Call disconnect timer
+ */
+int
+sip_platform_supervision_disconnect_timer_start (uint32_t msec, int idx)
+{
+    static const char fname[] = "sip_platform_supervision_disconnect_timer_start";
+
+    if (sip_platform_supervision_disconnect_timer_stop(idx) == SIP_ERROR) {
+        return SIP_ERROR;
+    }
+
+    if (cprStartTimer(sipPlatformSupervisionTimers[idx].timer, msec,
+                      (void *)(long) idx) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprStartTimer");
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+}
+
+
+int
+sip_platform_supervision_disconnect_timer_stop (int idx)
+{
+    static const char fname[] = "sip_platform_supervision_disconnect_timer_stop";
+
+    if ((idx < TEL_CCB_START) || (idx > TEL_CCB_END)) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_LINE_NUMBER_INVALID), fname, idx);
+        return SIP_ERROR;
+    }
+
+    if (cprCancelTimer(sipPlatformSupervisionTimers[idx].timer)
+            == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          idx, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+}
+
+void
+sip_platform_post_timer (uint32_t cmd, void *data)
+{
+    static const char fname[] = "sip_platform_post_timer";
+    uint32_t *timer_msg = NULL;
+
+    /* grab msg buffer */
+    timer_msg = (uint32_t *) SIPTaskGetBuffer(sizeof(uint32_t));
+    if (!timer_msg) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SYSBUF_UNAVAILABLE), fname);
+        return;
+    }
+    *timer_msg = (long) data;
+
+    /* Put it on the SIP message queue */
+    if (SIPTaskSendMsg(cmd, (cprBuffer_t) timer_msg, sizeof(uint32_t), NULL)
+            == CPR_FAILURE) {
+        cpr_free(timer_msg);
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX "Send msg failed.\n", fname);
+    }
+    return;
+}
+
+
+/****************************************************
+ * Timer functions for Subscribe / Notify operations
+ ****************************************************/
+
+int
+sip_platform_msg_timer_subnot_start (uint32_t msec,
+                                     sipPlatformUITimer_t *timer_p,
+                                     uint32_t id,
+                                     char *message_buffer,
+                                     int message_buffer_len,
+                                     int message_type,
+                                     cpr_ip_addr_t *ipaddr,
+                                     uint16_t port)
+{
+    static const char fname[] = "sip_platform_msg_timer_start_subnot";
+
+    sip_platform_msg_timer_subnot_stop(timer_p);
+
+    if (message_buffer_len > SIP_UDP_MESSAGE_SIZE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_MSG_BUFFER_TOO_BIG),
+                          fname, message_buffer_len);
+        return SIP_ERROR;
+    }
+
+    if (timer_p->message_buffer == NULL) {
+        timer_p->message_buffer = (char *)cpr_malloc(message_buffer_len+1);
+        if (timer_p->message_buffer == NULL) return SIP_ERROR;
+    }
+    else if (timer_p->message_buffer != message_buffer) {
+        cpr_free(timer_p->message_buffer);
+        timer_p->message_buffer = (char *)cpr_malloc(message_buffer_len+1);
+        if (timer_p->message_buffer == NULL) return SIP_ERROR;
+    }
+
+    timer_p->message_buffer_len = message_buffer_len;
+    timer_p->message_buffer[message_buffer_len] = '\0';
+    memcpy(timer_p->message_buffer, message_buffer,
+           message_buffer_len);
+    timer_p->message_type =
+        (sipMethod_t) message_type;
+    timer_p->ipaddr = *ipaddr;
+    timer_p->port = port;
+
+    if (cprStartTimer(timer_p->timer, msec, (void *)(long)id) == CPR_FAILURE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX "%s failed\n",
+                          fname, "cprStartTimer");
+        cpr_free(timer_p->message_buffer);
+        timer_p->message_buffer = NULL;
+        timer_p->message_buffer_len = 0;
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+
+}
+
+void
+sip_platform_msg_timer_subnot_stop (sipPlatformUITimer_t *timer_p)
+{
+    static const char fname[] = "sip_platform_msg_timer_stop_subnot";
+
+    if (timer_p->message_buffer != NULL) {
+        cpr_free(timer_p->message_buffer);
+        timer_p->message_buffer = NULL;
+    }
+    if (cprCancelTimer(timer_p->timer) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX "%s failed\n",
+                          DEB_F_PREFIX_ARGS(SIP_TIMER, fname), "cprCancelTimer");
+        return;
+    }
+}
+
+void
+sip_platform_subnot_msg_timer_callback (void *data)
+{
+    sip_platform_post_timer(SIP_TMR_MSG_RETRY_SUBNOT, data);
+}
+
+int
+sip_platform_subnot_periodic_timer_start (uint32_t msec)
+{
+    static const char fname[] = "sip_platform_subnot_periodic_timer_start";
+
+    if (sip_platform_subnot_periodic_timer_stop() == SIP_ERROR) {
+        return SIP_ERROR;
+    }
+
+    if (cprStartTimer(sipPlatformSubNotPeriodicTimer.timer, msec, (void *) 0)
+            == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          -1, 0, fname, "cprStartTimer");
+        return SIP_ERROR;
+    }
+    sipPlatformSubNotPeriodicTimer.started = TRUE;
+    return SIP_OK;
+}
+
+int
+sip_platform_subnot_periodic_timer_stop (void)
+{
+    static const char fname[] = "sip_platform_subnot_periodic_timer_stop";
+
+    if (sipPlatformSubNotPeriodicTimer.started == TRUE) {
+        if (cprCancelTimer(sipPlatformSubNotPeriodicTimer.timer)
+                == CPR_FAILURE) {
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              -1, 0, fname, "cprCancelTimer");
+            return SIP_ERROR;
+        }
+    }
+    sipPlatformSubNotPeriodicTimer.started = FALSE;
+    return SIP_OK;
+}
+
+void
+sip_platform_subnot_periodic_timer_callback (void *data)
+{
+    sip_platform_post_timer(SIP_TMR_PERIODIC_SUBNOT, data);
+}
+
+/**
+ ** sip_platform_reg_all_fail_timer_start
+ *  Starts a timer when all registrations fail.
+ *
+ *  @param  msec Value of the timer to be started
+ *
+ *  @return SIP_OK if timer could be started; else  SIP_ERROR
+ *
+ */
+int
+sip_platform_reg_all_fail_timer_start (uint32_t msec)
+{
+
+    static const char fname[] = "sip_platform_reg_all_fail_timer_start";
+    if (sip_platform_reg_all_fail_timer_stop() == SIP_ERROR) {
+        return SIP_ERROR;
+    }
+
+    if (cprStartTimer(sipPlatformRegAllFailedTimer, msec, NULL) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          0, 0, fname, "cprStartTimer");
+        return SIP_ERROR;
+    }
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX
+        "Timer started for %lu msecs\n", DEB_F_PREFIX_ARGS(SIP_TIMER, fname), msec);
+    return SIP_OK;
+}
+
+/**
+ ** sip_platform_reg_all_fail_timer_stop
+ *  Stops the Reg-All Fail timer
+ *
+ *  @param  none
+ *
+ *  @return SIP_OK if timer could be stopped; else  SIP_ERROR
+ *
+ */
+int
+sip_platform_reg_all_fail_timer_stop (void)
+{
+    static const char fname[] = "sip_platform_reg_all_fail_timer_stop";
+
+    if (cprCancelTimer(sipPlatformRegAllFailedTimer) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          0, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+    return SIP_OK;
+}
+
+/****************************************************
+ * Timer functions for standby cc keepalive operations
+ ****************************************************/
+int
+sip_platform_standby_keepalive_timer_start (uint32_t msec)
+{
+    static const char fname[] = "sip_platform_standby_keepalive_timer_start";
+
+    if (sip_platform_standby_keepalive_timer_stop() == SIP_ERROR) {
+        return SIP_ERROR;
+    }
+
+    if (cprStartTimer(sipPlatformStandbyKeepaliveTimer, msec, NULL)
+            == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          0, 0, fname, "cprStartTimer");
+        return SIP_ERROR;
+    }
+    CCSIP_DEBUG_STATE(DEB_F_PREFIX
+        "Timer started for %lu msecs\n", DEB_F_PREFIX_ARGS(SIP_TIMER, fname), msec);
+    return SIP_OK;
+}
+
+int
+sip_platform_standby_keepalive_timer_stop ()
+{
+    static const char fname[] = "sip_platform_standby_keepalive_timer_stop";
+
+    if (cprCancelTimer(sipPlatformStandbyKeepaliveTimer) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          0, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+    return SIP_OK;
+}
+
+int
+sip_platform_unregistration_timer_start (uint32_t msec, boolean external)
+{
+    static const char fname[] = "sip_platform_unregistration_timer_start";
+
+    if (sip_platform_unregistration_timer_stop() == SIP_ERROR) {
+        return SIP_ERROR;
+    }
+
+    if (cprStartTimer(sipPlatformUnRegistrationTimer, msec, (void *)(long)external)
+            == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          0, 0, fname, "cprStartTimer");
+        return SIP_ERROR;
+    }
+    CCSIP_DEBUG_STATE(DEB_F_PREFIX
+        "Timer started for %lu msecs\n", DEB_F_PREFIX_ARGS(SIP_TIMER, fname), msec);
+    return SIP_OK;
+}
+
+int
+sip_platform_unregistration_timer_stop ()
+{
+    static const char fname[] = "sip_platform_unregistration_timer_stop";
+
+    if (cprCancelTimer(sipPlatformUnRegistrationTimer) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          0, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+    return SIP_OK;
+}
+
+void
+sip_platform_unregistration_callback (void *data)
+{
+    sip_platform_post_timer(SIP_TMR_SHUTDOWN_PHASE2, data);
+}
+
+int
+sip_platform_notify_timer_start (uint32_t msec)
+{
+    static const char fname[] = "sip_platform_notify_timer_start";
+
+    if (sip_platform_notify_timer_stop() == SIP_ERROR) {
+        return SIP_ERROR;
+    }
+
+    if (cprStartTimer(sipPlatformNotifyTimer, msec, NULL) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          0, 0, fname, "cprStartTimer");
+        return SIP_ERROR;
+    }
+    CCSIP_DEBUG_STATE(DEB_F_PREFIX
+        "Timer started for %lu msecs\n", DEB_F_PREFIX_ARGS(SIP_TIMER, fname), msec);
+    return SIP_OK;
+}
+
+int
+sip_platform_notify_timer_stop ()
+{
+    static const char fname[] = "sip_platform_notify_timer_stop";
+
+    if (cprCancelTimer(sipPlatformNotifyTimer) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          0, 0, fname, "cprCancelTimer");
+        return SIP_ERROR;
+    }
+    return SIP_OK;
+}
+
+/***********************************************
+  *         PassThrough Timer
+ ***********************************************
+  ** sip_platform_pass_through_timer_start
+  *  Starts a timer when all registrations fail.
+  *
+  *  @param  sec Value of the timer to be started
+  *
+  *  @return SIP_OK if timer could be started; else  SIP_ERROR
+  *
+  */
+int
+sip_platform_pass_through_timer_start (uint32_t sec)
+{
+       static const char fname[] = "sip_platform_pass_through_timer_start";
+
+       if (sip_platform_pass_through_timer_stop() == SIP_ERROR) {
+               return SIP_ERROR;
+       }
+       if (cprStartTimer(sipPassThroughTimer, sec*1000, NULL) == CPR_FAILURE) {
+               CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                                 0, 0, fname, "cprStartTimer");
+               return SIP_ERROR;
+       }
+
+       CCSIP_DEBUG_REG_STATE("%s: Regmgr Pass Through Timer started for %lu secs\n", fname, sec);
+       return SIP_OK;
+}
+
+ /**
+  ** sip_platform_pass_through_timer_stop
+  *  Stops the Pass Through timer
+  *
+  *  @param  none
+  *
+  *  @return SIP_OK if timer could be stopped; else  SIP_ERROR
+  *
+  */
+int
+sip_platform_pass_through_timer_stop (void)
+{
+       static const char fname[] = "sip_platform_pass_through_timer_stop";
+
+       if (cprCancelTimer(sipPassThroughTimer) == CPR_FAILURE) {
+               CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                                 0, 0, fname, "cprCancelTimer");
+               return SIP_ERROR;
+       }
+       return SIP_OK;
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_platform_tls.c b/libs/sipcc/core/sipstack/ccsip_platform_tls.c
new file mode 100644 (file)
index 0000000..7183841
--- /dev/null
@@ -0,0 +1,176 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_ipc.h"
+#include "cpr_errno.h"
+#include "cpr_socket.h"
+#include "cpr_in.h"
+#include "ccsip_core.h"
+#include "ccsip_task.h"
+#include "sip_platform_task.h"
+#include "ccsip_platform_udp.h"
+#include "sip_common_transport.h"
+#include "sip_common_regmgr.h"
+#include "phone_debug.h"
+#include "util_string.h"
+#include "ccsip_platform_tcp.h"
+#include "text_strings.h"
+#include "ccsip_register.h"
+#include "phntask.h"
+#include "plat_api.h"
+#include "sip_socket_api.h"
+
+#define HOST_SIZE 64
+
+
+cpr_socket_t sip_tls_create_connection(sipSPIMessage_t *spi_msg,
+                                       boolean blocking,
+                                       sec_level_t sec);
+extern cpr_sockaddr_t *sip_set_sockaddr(cpr_sockaddr_storage *psock_storage, uint16_t family,
+                                 cpr_ip_addr_t ip_addr, uint16_t port, uint16_t *addr_len);
+
+/*
+ * sip_tls_create_connection()
+ * Description : This routine is called is response to a create connection
+ * request from SIP_SPI to SIP_TLS.
+ *
+ * Input : spi_msg - Pointer to sipSPIMessage_t containing the create conn
+ * parameters.
+ *        blocking - connection mode; FALSE - nonblock; TRUE - block
+ *
+ * Output : Nothing
+ */
+cpr_socket_t
+sip_tls_create_connection (sipSPIMessage_t *spi_msg, boolean blocking,
+        sec_level_t sec)
+{
+    const char fname[] = "sip_tls_create_connection";
+    int idx;
+    sipSPICreateConnection_t *create_msg;
+    char ipaddr_str[MAX_IPADDR_STR_LEN];
+    plat_soc_status_e ret;
+    cpr_socket_t sock = INVALID_SOCKET;
+    uint16_t sec_port = 0;
+    plat_soc_connect_status_e conn_status;
+    int tos_dscp_val = 0; // set to default if there is no config. for dscp
+#ifdef IPV6_STACK_ENABLED
+    int ip_mode = CPR_IP_MODE_IPV4;
+#endif
+    uint16_t af_listen = AF_INET6;
+    cpr_sockaddr_storage sock_addr;
+    uint16_t       addr_len;
+    int ip_mode = 0; // currently hardcoded to ipv4
+    plat_soc_connect_mode_e conn_mode;
+
+#ifdef IPV6_STACK_ENABLED
+
+    config_get_value(CFGID_IP_ADDR_MODE, &ip_mode, sizeof(ip_mode));
+
+    /*
+     * Create a socket
+     */
+    if (ip_mode == CPR_IP_MODE_IPV6 ||
+        ip_mode == CPR_IP_MODE_DUAL) {
+        af_listen = AF_INET6;
+    } else {
+#endif
+        af_listen = AF_INET;
+#ifdef IPV6_STACK_ENABLED
+    }
+#endif
+
+    sip_tcp_init_conn_table();
+    create_msg = &(spi_msg->createConnMsg);
+    ipaddr2dotted(ipaddr_str, &create_msg->addr);
+    ret = platSecIsServerSecure();
+    if (ret != PLAT_SOCK_SECURE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "Secure connection is not created because"
+                          " there is no secure servers\n", fname);
+        return INVALID_SOCKET;
+    }
+    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX
+                        "Creating secure connection\n", DEB_F_PREFIX_ARGS(SIP_TLS, fname));
+    /* connect securely via TLS */
+    config_get_value(CFGID_DSCP_FOR_CALL_CONTROL, (int *)&tos_dscp_val,
+                     sizeof(tos_dscp_val));
+
+    if (sec == AUTHENTICATED) {
+        conn_mode = PLAT_SOCK_AUTHENTICATED;
+    } else if (sec == ENCRYPTED) {
+        conn_mode = PLAT_SOCK_ENCRYPTED;
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "Secure connection is not created. Security mode was"
+                          " not encrypyted or authenticated.\n", fname);
+        conn_mode = PLAT_SOCK_NON_SECURE;
+    }
+    sock = platSecSocConnect(ipaddr_str,          /* host  */
+                            create_msg->port,    /* port */
+                            ip_mode,             /* ip mode, ipv4 = 0, ipv6 = 1, dual = 2 */
+                            blocking,            /* 1 - block  */
+                            tos_dscp_val, /* TOS value  */
+                            conn_mode,   /* The mode (Auth/Encry/None) */
+                            &sec_port);          /* local port */
+
+    if (sock < 0) {
+
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "Secure connect failed!!\n",fname);
+        return INVALID_SOCKET;
+    }
+    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX "Secure connect ok\n", DEB_F_PREFIX_ARGS(SIP_TLS, fname));
+    if (!blocking) {
+        /* should not call this api in blocking mode */
+        conn_status = platSecSockIsConnected(sock);
+        if (conn_status == PLAT_SOCK_CONN_FAILED) {
+            (void)sipSocketClose(sock, TRUE);
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                              "Establish non-blocking mode secure"
+                              " connection failed!!\n", fname);
+            return INVALID_SOCKET;
+        }
+    } else {
+        conn_status = PLAT_SOCK_CONN_OK;
+    }
+    if (sip_tcp_set_sock_options(sock) != TRUE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX "Socket set option failure\n",
+                          fname);
+    }
+
+    idx = sip_tcp_get_free_conn_entry();
+    if (idx == -1) {
+        /* Send create connection failed message to SIP_SPI */
+        (void)sipSocketClose(sock, TRUE);
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                          "Get free TCP connection entry failed\n",
+                           fname);
+        return INVALID_SOCKET;
+    }
+
+    memset(&sock_addr, 0, sizeof(sock_addr));
+    (void) sip_set_sockaddr(&sock_addr, af_listen, create_msg->addr,
+                            (uint16_t)(create_msg->port), &addr_len);
+
+    sip_tcp_conn_tab[idx].fd = sock;
+    sip_tcp_conn_tab[idx].ipaddr = create_msg->addr;
+    sip_tcp_conn_tab[idx].port = create_msg->port;
+    sip_tcp_conn_tab[idx].context = spi_msg->context;
+    sip_tcp_conn_tab[idx].dirtyFlag = FALSE;
+    sip_tcp_conn_tab[idx].addr = sock_addr;
+    sip_tcp_conn_tab[idx].soc_type = SIP_SOC_TLS;
+
+    if (conn_status == PLAT_SOCK_CONN_OK) {
+        sip_tcp_conn_tab[idx].state = SOCK_CONNECTED;
+    } else {
+        sip_tcp_conn_tab[idx].state = SOCK_CONNECT_PENDING;
+    }
+    create_msg->local_listener_port = (uint16) sec_port;
+    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX
+                        "Local listening port=%d\n", DEB_F_PREFIX_ARGS(SIP_TLS, fname),
+                        create_msg->local_listener_port);
+    (void)sip_tcp_attach_socket(sock);
+    return (sock);
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_platform_udp.c b/libs/sipcc/core/sipstack/ccsip_platform_udp.c
new file mode 100644 (file)
index 0000000..0b09bbc
--- /dev/null
@@ -0,0 +1,455 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_socket.h"
+#include "cpr_memory.h"
+#include "cpr_ipc.h"
+#include "cpr_in.h"
+#include "cpr_errno.h"
+#include "cpr_string.h"
+#include "util_string.h"
+#include "text_strings.h"
+#include "ccsip_core.h"
+#include "ccsip_task.h"
+#include "ccsip_platform_udp.h"
+#include "phone.h"
+#include "phone_debug.h"
+#include "sip_common_transport.h"
+#include "sip_platform_task.h"
+#include "sip_socket_api.h"
+
+// FIXME include does not exist on windows
+//#include <net/if.h>
+
+static uint16_t af_family_listen = AF_INET6;
+static uint16_t af_family_connect = AF_INET6;
+
+/*
+ *  To set the appropriate socket strcutre based on the family. The called
+ *  of the function is responsible for allocating the memory.
+ *
+ *  @param psock_storage  pointer to cpar_sockaddr_storage
+ *         family         network family AF_INET or AF_INET6
+ *         ip_addr        ip address
+ *         port
+ *         addr_len       legth returned based on family
+ *
+ *
+ *  @return  pointer to cpr_sockaddr, which is also psock_storage
+ *
+ *  @pre     none
+ *
+ */
+
+cpr_sockaddr_t *sip_set_sockaddr (cpr_sockaddr_storage *psock_storage, uint16_t family,
+                                 cpr_ip_addr_t ip_addr, uint16_t port, uint16_t *addr_len)
+{
+    static const char fname[] = "sip_set_sockaddr";
+
+    cpr_sockaddr_in6_t *pin6_addr;
+    cpr_sockaddr_in_t *pin_addr;
+    cpr_sockaddr_t *psock_addr;
+    uint32_t   tmp_ip;
+    int i,j;
+    unsigned char tmp;
+
+    switch (family) {
+    case AF_INET6:
+
+        pin6_addr = (cpr_sockaddr_in6_t *)psock_storage;
+        memset(pin6_addr, 0, sizeof(cpr_sockaddr_in6_t));
+        pin6_addr->sin6_family      = family;
+        pin6_addr->sin6_port        = htons(port);
+
+        if (ip_addr.type == CPR_IP_ADDR_IPV4) {
+
+            if (ip_addr.u.ip4 != INADDR_ANY) {
+                pin6_addr->sin6_addr.addr.base16[5] = 0xffff;
+            }
+            tmp_ip = ntohl(ip_addr.u.ip4);
+            memcpy((void *)&(pin6_addr->sin6_addr.addr.base16[6]), (void *)&tmp_ip, 4);
+
+        } else {
+
+            for (i=0, j=15; i<16; i++, j--) {
+                tmp  = pin6_addr->sin6_addr.addr.base8[j];
+                pin6_addr->sin6_addr.addr.base8[j] = ip_addr.u.ip6.addr.base8[i];
+                ip_addr.u.ip6.addr.base8[i] = tmp;
+            }
+
+        }
+
+        *addr_len = sizeof(cpr_sockaddr_in6_t);
+        return(psock_addr=(cpr_sockaddr_t *)pin6_addr);
+
+    case AF_INET:
+        if (ip_addr.type == CPR_IP_ADDR_IPV6) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Setting ipv6 address in AF_INET\n",fname);
+            break;
+        }
+        pin_addr = (cpr_sockaddr_in_t *)psock_storage;
+        memset(pin_addr, 0, sizeof(cpr_sockaddr_in_t));
+        pin_addr->sin_family      = family;
+        pin_addr->sin_addr.s_addr = htonl(ip_addr.u.ip4);
+        pin_addr->sin_port        = htons(port);
+
+        *addr_len = sizeof(cpr_sockaddr_in_t);
+        return(psock_addr=(cpr_sockaddr_t *)pin_addr);
+
+    default:
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to set sockaddr.\n",fname);
+        break;
+    }
+
+    return(NULL);
+}
+
+int
+sip_platform_udp_channel_listen (cpr_ip_mode_e ip_mode, cpr_socket_t *s,
+                                 cpr_ip_addr_t *local_ipaddr,
+                                 uint16_t local_port)
+{
+    static const char fname[] = "sip_platform_udp_channel_listen";
+    cpr_sockaddr_storage sock_addr;
+    uint16_t       addr_len;
+
+    /*
+     * If socket passed is is not INVALID_SOCKET close it first
+     */
+
+    if (*s != INVALID_SOCKET) {
+        if (sipSocketClose(*s, FALSE) != CPR_SUCCESS) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "sipSocketClose", cpr_errno);
+        }
+        sip_platform_task_reset_listen_socket(*s);
+    }
+
+    /*
+     * Create a socket
+     */
+    if (ip_mode == CPR_IP_MODE_IPV6 ||
+        ip_mode == CPR_IP_MODE_DUAL) {
+        af_family_listen = AF_INET6;
+    } else {
+        af_family_listen = AF_INET;
+    }
+
+    *s = cprSocket(af_family_listen, SOCK_DGRAM, 0);
+    if (*s == INVALID_SOCKET) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                          fname, "cprSocket unable to open socket", cpr_errno);
+        if (ip_mode == CPR_IP_MODE_DUAL) {
+
+            af_family_listen = AF_INET;
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Socket open failed for IPv6 using IPv4 address.",
+                             DEB_F_PREFIX_ARGS(SIP_SDP, fname));
+
+            *s = cprSocket(af_family_listen, SOCK_DGRAM, 0);
+            if (*s == INVALID_SOCKET) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "cprSocket unable to open socket for IPv4",
+                                        cpr_errno);
+                return SIP_ERROR;
+            }
+        }
+    }
+
+    (void) sip_set_sockaddr(&sock_addr, af_family_listen, *local_ipaddr,
+                            local_port, &addr_len);
+
+    if (cprBind(*s,  (cpr_sockaddr_t *)&sock_addr, addr_len) == CPR_FAILURE) {
+        (void) sipSocketClose(*s, FALSE);
+        *s = INVALID_SOCKET;
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                          fname, "cprBind", cpr_errno);
+        return SIP_ERROR;
+    }
+    sip_platform_task_set_listen_socket(*s);
+
+    return SIP_OK;
+}
+
+int
+sip_platform_udp_channel_create (cpr_ip_mode_e ip_mode, cpr_socket_t *s,
+                                 cpr_ip_addr_t *remote_ipaddr,
+                                 uint16_t remote_port,
+                                 uint32_t local_udp_port)
+{
+    static const char *fname = "sip_platform_udp_channel_create";
+    cpr_sockaddr_storage sock_addr;
+    uint16_t       addr_len;
+    cpr_sockaddr_storage local_sock_addr;
+    cpr_ip_addr_t local_signaladdr;
+
+    int tos_dscp_val = 0; // set to default if there is no config. for dscp
+
+    CPR_IP_ADDR_INIT(local_signaladdr);
+
+    if (*s != INVALID_SOCKET) {
+        (void) sipSocketClose(*s, FALSE);
+    }
+
+    if (ip_mode == CPR_IP_MODE_IPV6 ||
+        ip_mode == CPR_IP_MODE_DUAL) {
+        af_family_connect = AF_INET6;
+    } else {
+        af_family_connect = AF_INET;
+    }
+    /*
+     * Create socket
+     */
+    *s = cprSocket(af_family_connect, SOCK_DGRAM, 0);
+    if (*s == INVALID_SOCKET) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                          fname, "cprSocket unable to open socket",
+                          cpr_errno);
+        /* Try opening ipv4 socket */
+        if (ip_mode == CPR_IP_MODE_DUAL) {
+
+            CCSIP_DEBUG_TASK("%s: cprSocket Open failed for IPv6 trying IPv4",
+                            fname);
+            af_family_connect = AF_INET;
+            *s = cprSocket(af_family_connect, SOCK_DGRAM, 0);
+            if (*s == INVALID_SOCKET) {
+
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "cprSocket unable to open AF_INET socket",
+                             cpr_errno);
+                return SIP_ERROR;
+            }
+        }
+    }
+
+    sip_config_get_net_device_ipaddr(&local_signaladdr);
+    memset(&local_sock_addr, 0, sizeof(local_sock_addr));
+
+    (void) sip_set_sockaddr(&local_sock_addr, af_family_connect, local_signaladdr, 0, &addr_len);
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"local_signaladdr.u.ip4=%x\n",
+        DEB_F_PREFIX_ARGS(SIP_SDP, fname), local_signaladdr.u.ip4);
+
+    if(cprBind(*s, (cpr_sockaddr_t *)&local_sock_addr, addr_len)){
+       CCSIP_DEBUG_ERROR(SIP_F_PREFIX"UDP bind failed with errno %d\n", fname, cpr_errno);
+       (void) sipSocketClose(*s, FALSE);
+       *s = INVALID_SOCKET;
+       return SIP_ERROR;
+    }
+
+    /*
+     * Connect to remote address
+     */
+    (void) sip_set_sockaddr(&sock_addr, af_family_connect, *remote_ipaddr,
+                            remote_port, &addr_len);
+
+ /*   if (cprConnect(*s, (cpr_sockaddr_t *)&sock_addr, addr_len) == CPR_FAILURE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                          fname, "cprConnect", cpr_errno);
+        (void) sipSocketClose(*s, FALSE);
+        *s = INVALID_SOCKET;
+          return SIP_ERROR;
+    }
+*/
+    // set IP tos/dscp value for SIP messaging
+    config_get_value(CFGID_DSCP_FOR_CALL_CONTROL, (int *)&tos_dscp_val,
+                     sizeof(tos_dscp_val));
+
+    if (cprSetSockOpt(*s, SOL_IP, IP_TOS, (void *)&tos_dscp_val,
+                      sizeof(tos_dscp_val)) == CPR_FAILURE) {
+        // do NOT take hard action; just log the error and move on
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to set IP TOS %d on UDP socket. "
+                          "cpr_errno = %d\n", fname, tos_dscp_val, cpr_errno);
+    }
+    return SIP_OK;
+}
+
+
+int
+sip_platform_udp_channel_destroy (cpr_socket_t s)
+{
+    static const char fname[] = "sip_platform_udp_channel_destroy";
+
+    if (s != INVALID_SOCKET) {
+        if (sipSocketClose(s, FALSE) == CPR_FAILURE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "sipSocketClose", cpr_errno);
+            return SIP_ERROR;
+        }
+    }
+    return SIP_OK;
+}
+
+int
+sip_platform_udp_channel_read (cpr_socket_t s,
+                               cprBuffer_t buf,
+                               uint16_t *len,
+                               cpr_sockaddr_t *soc_addr,
+                               cpr_socklen_t *soc_addr_len)
+{
+    static const char *fname = "sip_platform_udp_channel_read";
+    int bytes_read;
+    // NOT USED: cpr_sockaddr_in_t *addr = (cpr_sockaddr_in_t *)soc_addr;
+
+    bytes_read = cprRecvFrom(s, buf, CPR_MAX_MSG_SIZE, 0, soc_addr,
+                             soc_addr_len);
+
+    switch (bytes_read) {
+    case SOCKET_ERROR:
+        /*
+         * If no data is available to read (CPR_EWOULDBLOCK),
+         * for non-blocking socket, it is not an error.
+         */
+        cpr_free(buf);
+        *len = 0;
+        if (cpr_errno != CPR_EWOULDBLOCK) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"fd[%d]\n", fname, s);
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "cprRecvFrom", cpr_errno);
+            return SIP_ERROR;
+        }
+        /*
+         * Will continue reading when data arrives at socket
+         */
+        break;
+    case 0:
+        /*
+         * Return value 0 is OK.  This does NOT mean the connection
+         * has closed by the peer, as with TCP sockets.  With UDP
+         * sockets, there is no such thing as closing a connection.
+         */
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"No data on fd %d\n", DEB_F_PREFIX_ARGS(SIP_SDP, fname), s);
+        cpr_free(buf);
+        *len = 0;
+        break;
+    default:
+        /* PKT has been read */
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Recvd on fd %d\n", DEB_F_PREFIX_ARGS(SIP_SDP, fname), s);
+        *len = (uint16_t) bytes_read;
+        break;
+    }
+
+    return SIP_OK;
+}
+
+int
+sip_platform_udp_channel_send (cpr_socket_t s, char *buf, uint16_t len)
+{
+    static const char *fname = "sip_platform_udp_channel_send";
+    ssize_t bytesSent;
+
+    /*
+     * Check not exceeding max allowed payload size
+     */
+    if (len >= PKTBUF_SIZ) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_UDP_PAYLOAD_TOO_LARGE),
+                          fname, len, PKTBUF_SIZ);
+        return SIP_ERROR;
+    }
+
+    while (len > 0) {
+        bytesSent = sipSocketSend(s, (void *)buf, (size_t)len, 0, FALSE);
+        if (bytesSent == SOCKET_ERROR) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "cprSend", cpr_errno);
+            return SIP_ERROR;
+        }
+
+        len -= bytesSent;
+        buf += bytesSent;
+    }
+
+    return SIP_OK;
+}
+
+/**
+ *
+ * sip_platform_udp_read_socket
+ *
+ * Read from the socket to extract received message
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_udp_read_socket (cpr_socket_t s)
+{
+    cprBuffer_t buf;
+    uint16_t len = 0;
+    cpr_sockaddr_storage from;
+    cpr_socklen_t from_len;
+    const char *fname = "sip_platform_udp_read_socket";
+
+    if (af_family_listen == AF_INET6) {
+        from_len = sizeof(cpr_sockaddr_in6_t);
+    } else {
+        from_len = sizeof(cpr_sockaddr_in_t);
+    }
+
+    buf = SIPTaskGetBuffer(CPR_MAX_MSG_SIZE);
+    if (buf) {
+        if ((sip_platform_udp_channel_read(s, buf, &len,
+                (cpr_sockaddr_t *)&from, &from_len) == SIP_OK) &&
+            (len != 0)) {
+            (void) SIPTaskProcessUDPMessage(buf, len, from);
+        }
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No buffers available to read UDP socket.\n",
+                            fname);
+    }
+}
+
+int
+sip_platform_udp_channel_sendto (cpr_socket_t s, char *buf, uint32_t len,
+                                 cpr_ip_addr_t *dst_ipaddr, uint16_t dst_port)
+{
+    static const char *fname = "sip_platform_udp_channel_sendto";
+    ssize_t bytesSent;
+    cpr_sockaddr_storage sock_addr;
+    uint16_t       addr_len;
+    cpr_ip_addr_t  dest_ip_addr;
+
+    /*
+     * Connect to remote address
+     */
+    dest_ip_addr = *dst_ipaddr;
+    (void) sip_set_sockaddr(&sock_addr, af_family_connect, dest_ip_addr,
+                            dst_port, &addr_len);
+
+
+    /*
+     * Check not exceeding max allowed payload size
+     */
+    if (len >= PKTBUF_SIZ) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_UDP_PAYLOAD_TOO_LARGE),
+                          fname, len, PKTBUF_SIZ);
+        return SIP_ERROR;
+    }
+
+    while (len > 0) {
+        bytesSent = cprSendTo(s, (void *)buf, (size_t)len, 0,
+                              (cpr_sockaddr_t *)&sock_addr, addr_len);
+
+        if ((bytesSent == SOCKET_ERROR) && (cpr_errno == CPR_ECONNREFUSED)) {
+            /*
+             * Will get socket error ECONNREFUSED after an ICMP message
+             * resend the message
+             */
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"UDP send to error %d\n", DEB_F_PREFIX_ARGS(SIP_SOCK, fname), cpr_errno);
+            bytesSent = cprSendTo(s, (void *)buf, (size_t)len, 0,
+                                  (cpr_sockaddr_t *)&sock_addr, addr_len);
+        }
+        if (bytesSent == SOCKET_ERROR) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "cprSendTo", cpr_errno);
+            return SIP_ERROR;
+        }
+
+        len -= bytesSent;
+        buf += bytesSent;
+    }
+
+    return SIP_OK;
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_pmh.c b/libs/sipcc/core/sipstack/ccsip_pmh.c
new file mode 100644 (file)
index 0000000..e298993
--- /dev/null
@@ -0,0 +1,5751 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Routines that parse SIP messages into individual components
+ * defined in ccsip_pmh.h. Used by the code that receives the
+ * messages to decipher them and then effect callstate as
+ * appropriate.
+ * Some SIP messages are exactly the same as HTTP/1.1 messages,
+ * and these are parsed using wrapper functions to those in
+ * httpish.c
+ */
+
+#include <errno.h>
+#include <limits.h>
+
+#include "plstr.h"
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_in.h"
+#include "cpr_memory.h"
+#include "ccsip_pmh.h"
+#include "phone_debug.h"
+#include "ccapi.h"
+#include "text_strings.h"
+#include "util_string.h"
+#include "ccsip_spi_utils.h"
+#include "ccsip_callinfo.h"
+#include "ccsip_core.h"
+
+/* Skip linear whitespace */
+#define SKIP_LWS(p)    while (*p == SPACE || *p == TAB) { \
+                           p++; \
+                       }
+
+#define SKIP_WHITE_SPACE(p)    while (*p == SPACE || *p == TAB || \
+                                      *p == '\n') { \
+                                      p++; \
+                               }
+
+#define AUTHENTICATION_BASIC      "Basic"
+#define AUTHENTICATION_DIGEST     "Digest"
+#define AUTHENTICATION_REALM      "realm"
+#define AUTHENTICATION_NONCE      "nonce"
+#define AUTHENTICATION_URI        "uri"
+#define AUTHENTICATION_DOMAIN     "domain"
+#define AUTHENTICATION_ALGORITHM  "algorithm"
+#define AUTHENTICATION_OPAQUE     "opaque"
+#define AUTHENTICATION_USERNAME   "username"
+#define AUTHENTICATION_RESPONSE   "response"
+
+
+/* Trim trailing space from end of string */
+static void
+trim_right (char *pstr)
+{
+    char *pend;
+
+    if (!pstr) {
+        return;
+    }
+
+    pend = (pstr + (strlen(pstr) - 1));
+    while (pend > pstr) {
+        if (!isspace((int) *pend)) {
+            break;
+        }
+        *pend = '\0';
+        pend--;
+    }
+}
+
+/*
+ *  Remove square brackets from IPv6 address
+ *
+ *  @param pstr  address string
+ *
+ *  @return  none
+ *
+ *  @pre     none
+ *
+ */
+
+static void trim_ipv6_host(char *pstr)
+{
+    if (!pstr) {
+        return;
+    }
+
+    if (*pstr == '[') {
+        pstr++;
+    }
+
+    if (*(pstr + strlen(pstr)-1) == ']') {
+        *(pstr + strlen(pstr)-1) = '\0';
+    }
+}
+
+int parse_errno = 0;
+
+/* The order of these error messages corresponds to parse error codes
+ * defined in ccsip_pmh.h
+ */
+static char *parse_errors[] = { "",
+    ERROR_1,
+    ERROR_2,
+    ERROR_3,
+    ERROR_4,
+    ERROR_5,
+    ERROR_6,
+    ERROR_7,
+    ERROR_8
+};
+
+/*
+ * These headers are defined as per the values of Header indexes ( in
+ * ccsip_protocol.h. To include a header in the cached list, increase
+ * the HEADER_CACHE_SIZE in httpish.h and add its entry here. Its index
+ * should be consistent with the index value in ccsip_protocol.h.
+ * For example, #define VIA 2, implies sip_cached_headers[2] has Via header
+ * related stuff.
+ */
+sip_header_t sip_cached_headers[] = {
+/* 0 */  {"From", "f"},
+/* 1 */  {"To", "t"},
+/* 2 */  {HTTPISH_HEADER_VIA, "v"},
+/* 3 */  {"Call-ID", "i"},
+/* 4 */  {"CSeq", NULL},
+/* 5 */  {"Contact", "m"},
+/* 6 */  {HTTPISH_HEADER_CONTENT_LENGTH, HTTPISH_C_HEADER_CONTENT_LENGTH},
+/* 7 */  {HTTPISH_HEADER_CONTENT_TYPE, "c"},
+/* 8 */  {"Record-Route", NULL},
+/* 9 */  {"Require", NULL},
+/* 10 */ {"Route", NULL},
+/* 11 */ {"Supported", "k"}
+};
+
+static boolean
+is_dtmf_or_pause (char *digit)
+{
+    if (!(*digit)) {
+        return FALSE;
+    }
+    switch (*digit) {
+    case 'A':
+    case 'B':
+    case 'C':
+    case 'D':
+    case '*':
+    case '#':
+    case 'p':
+    case 'w':
+        return TRUE;
+    default:
+        return FALSE;
+    }
+}
+
+/* Inefficient ? Somewhat... */
+sipMethod_t
+sippmh_get_method_code (const char *method)
+{
+    sipMethod_t ret = sipMethodInvalid;
+
+    if (method) {
+
+        ret = sipMethodUnknown;
+
+        if (strcmp(method, SIP_METHOD_INVITE) == 0)
+            ret = sipMethodInvite;
+        else if (strcmp(method, SIP_METHOD_BYE) == 0)
+            ret = sipMethodBye;
+        else if (strcmp(method, SIP_METHOD_ACK) == 0)
+            ret = sipMethodAck;
+        else if (strcmp(method, SIP_METHOD_PRACK) == 0)
+            ret = sipMethodPrack;
+        else if (strcmp(method, SIP_METHOD_COMET) == 0)
+            ret = sipMethodComet;
+        else if (strcmp(method, SIP_METHOD_OPTIONS) == 0)
+            ret = sipMethodOptions;
+        else if (strcmp(method, SIP_METHOD_CANCEL) == 0)
+            ret = sipMethodCancel;
+        else if (strcmp(method, SIP_METHOD_NOTIFY) == 0)
+            ret = sipMethodNotify;
+        else if (strcmp(method, SIP_METHOD_REFER) == 0)
+            ret = sipMethodRefer;
+        else if (strcmp(method, SIP_METHOD_SUBSCRIBE) == 0)
+            ret = sipMethodSubscribe;
+        else if (strcmp(method, SIP_METHOD_REGISTER) == 0)
+            ret = sipMethodRegister;
+        else if (strcmp(method, SIP_METHOD_UPDATE) == 0)
+            ret = sipMethodUpdate;
+        else if (strcmp(method, SIP_METHOD_INFO) == 0)
+            ret = sipMethodInfo;
+        else if (strcmp(method, SIP_METHOD_PUBLISH) == 0)
+            ret = sipMethodPublish;
+        else if (strcmp(method, SIP_METHOD_MESSAGE) == 0)
+            ret = sipMethodMessage;
+        else if (strcmp(method, SIP_METHOD_INFO) == 0)
+            ret = sipMethodInfo;
+    }
+
+    return ret;
+}
+
+#define SKIP_SIP_TOKEN(inp_str) \
+    while (isalnum((int)*inp_str) || *inp_str == DASH || \
+           *inp_str == DOT || *inp_str == EXCLAMATION || \
+           *inp_str == PERCENT || *inp_str == STAR || \
+           *inp_str == UNDERSCORE || *inp_str == PLUS || \
+           *inp_str == '`' || *inp_str == SINGLE_QUOTE || \
+           *inp_str == COLON || *inp_str == TILDA || \
+           *inp_str == AT_SIGN) { \
+        inp_str++; \
+    }
+
+static char *
+parse_generic_param (char *param, char **param_val)
+{
+    boolean match_found;
+    char   *tok_start;
+
+    /*
+     * param is pointing to the first character after the name of the
+     * parameter. It could be
+     * white space   or
+     * equal sign    or
+     * semi-colon    or
+     * comma         or
+     * end of string
+     */
+    if (*param == SPACE || *param == TAB) {
+        *param++ = 0;
+        SKIP_LWS(param);
+    }
+
+    if (*param != EQUAL_SIGN) {
+        /*
+         * Parameter exists but its value is empty
+         */
+        *param_val = "";
+        return param;
+    }
+
+    *param++ = 0;
+    SKIP_LWS(param);
+    *param_val = param;
+    if (*param == DOUBLE_QUOTE) {
+        param++;
+        match_found = FALSE;
+        while (*param) {
+            if (*param == DOUBLE_QUOTE && *(param - 1) != ESCAPE_CHAR) {
+                param++;
+                match_found = TRUE;
+                break;
+            }
+            param++;
+        }
+        if (match_found == FALSE) {
+            return NULL;
+        }
+    } else {
+        tok_start = param;
+        SKIP_SIP_TOKEN(param);
+        if (param == tok_start) {
+            return NULL;
+        }
+    }
+    return param;
+}
+
+/*
+ * The syntax of other-param in SIP URL is same as extension-attribute in
+ * Contact header. A pointer to the start of the token=value or
+ * token="value" string is returned in other_param_value. This allows
+ * the calling code to store this value if needed such as in the
+ * case of supporting contact-extensions. The function returns
+ * a pointer to the next character to be parsed.
+ */
+static char *
+parse_other_param (char *inp_str, char **other_param_value)
+{
+    char  temp;
+    char *token_string;
+
+
+    SKIP_LWS(inp_str);
+    token_string = inp_str;
+    SKIP_SIP_TOKEN(inp_str);
+    switch (*inp_str) {
+    case COMMA:
+    case '\0':
+    case SEMI_COLON:
+        /*
+         * Either we have encountered end of string or another
+         * parameter follows this other-param. In either case we
+         * are done with parsing other-param. Since this is
+         * not in the form of token=value or token="value" discard
+         * it.
+         */
+        *other_param_value = NULL;
+
+        /* check for loose routing specifier */
+        temp = *inp_str;
+        *inp_str = 0;
+        if (!cpr_strcasecmp(token_string, "lr")) {
+            *other_param_value = (char *) cpr_malloc(4);
+            if (*other_param_value != NULL) {
+                sstrncpy(*other_param_value, token_string, 4);
+            }
+        }
+        *inp_str = temp;
+        break;
+
+    case EQUAL_SIGN:
+        /* It is either token=token form or token=quoted-string form */
+        inp_str++;
+        if (*inp_str == DOUBLE_QUOTE) {
+            /* quoted-string follows */
+            inp_str++; /* skip the " char */
+            while (*inp_str) {
+                if (*inp_str == DOUBLE_QUOTE && *(inp_str - 1) != ESCAPE_CHAR) {
+                    inp_str++;
+                    break;
+                }
+                inp_str++;
+            }
+        } else {
+            /* token follows */
+            // SKIP_SIP_TOKEN(inp_str);
+            // Continue until another semicolon is found or end of string is
+            // reached
+            while (*inp_str != '\0' && *inp_str != SEMI_COLON) {
+                inp_str++;
+            }
+        }
+
+        /*
+         * Terminate the string that token_string is pointing
+         * to while keeping the value of *inp_str unchanged.
+         * Then assign the null terminated string to other_param_value
+         */
+        *other_param_value = (char *) cpr_malloc(SIP_MAX_OTHER_PARAM_LENGTH + 1 * sizeof(char));
+        if (*other_param_value != NULL) {
+            temp = *inp_str;
+            *inp_str = 0;
+            sstrncpy(*other_param_value, token_string, SIP_MAX_OTHER_PARAM_LENGTH);
+            *inp_str = temp;
+        }
+        break;
+
+    default:
+        CCSIP_DEBUG_ERROR(ERROR_3, "parse_other_param", inp_str);
+        *other_param_value = NULL;
+        return NULL;
+    }
+    return inp_str;
+}
+
+/*
+ * This function creates an array of url headers. Each
+ * element of the array has two member pointers:
+ *      char pointer to header name,
+ *      char pointer to header value
+ */
+static int
+url_add_headers_to_list (char *url_strp, sipUrl_t *sip_url)
+{
+    char *tmp_ptr = NULL;
+    char  num_head = 1;
+    char *lasts = NULL;
+
+    if (!url_strp) {
+        return (PARSE_ERR_NULL_PTR);
+    }
+
+    tmp_ptr = strchr(url_strp, AMPERSAND);
+    while (tmp_ptr != NULL) {
+        num_head++;
+        tmp_ptr++;
+        tmp_ptr = strchr(tmp_ptr, AMPERSAND);
+    }
+
+    sip_url->headerp = (attr_value_pair_t *)
+        cpr_malloc(sizeof(attr_value_pair_t) * num_head);
+    if (!sip_url->headerp) {
+        return (PARSE_ERR_NO_MEMORY);
+    }
+
+    sip_url->num_headers = num_head;
+    num_head = 0;
+    url_strp = PL_strtok_r(url_strp, "&?", &lasts);
+    while ((url_strp != NULL) && (num_head < sip_url->num_headers)) {
+
+        tmp_ptr = strchr(url_strp, EQUAL_SIGN);
+        if (!tmp_ptr) {
+            return (PARSE_ERR_SYNTAX);
+        }
+        *tmp_ptr++ = 0;
+
+        sip_url->headerp[(uint16_t)num_head].attr  = url_strp;
+        sip_url->headerp[(uint16_t)num_head].value = tmp_ptr;
+
+        num_head++;
+        url_strp = PL_strtok_r(NULL, "&", &lasts);
+    }
+
+    return 0;
+}
+
+static int
+parseUrlParams (char *url_param, sipUrl_t *sipUrl, genUrl_t *genUrl)
+{
+    static const char fname[] = "parseUrlParams";
+    char *param_val;
+    char *url_other_param = NULL;
+    uint16_t i;
+    uint32_t ttl_val;
+    unsigned long strtoul_result;
+    char *strtoul_end;
+
+
+    /*
+     * url-parameters  = *( ";" url-parameter )
+     * url-parameter   = transport-param | user-param | method-param
+     *            | ttl-param | maddr-param | other-param
+     * transport-param = "transport=" ( "udp" | "tcp" | "sctp" | "tls"
+     *                                   | other-transport )
+     * ttl-param       = "ttl=" ttl
+     * ttl             = 1*3DIGIT       ; 0 to 255
+     * maddr-param     = "maddr=" host
+     * user-param      = "user=" ( "phone" | "ip" )
+     * method-param    = "method=" Method
+     * tag-param       = "tag=" UUID
+     * lr-param        = "lr"
+     * UUID            = 1*( hex | "-" )
+     * other-param     = ( token | ( token "=" ( token | quoted-string )))
+     * other-transport = token
+     */
+
+    /*
+     * url_param is pointing to the first character after the ';'
+     * This routine only prints error message for SYNTAX error, since we
+     * want to pinpoint the error location. For other errors it simply
+     * returns the error code.
+     */
+    SKIP_LWS(url_param);
+    if (*url_param == 0) {
+        return PARSE_ERR_UNEXPECTED_EOS;
+    }
+    while (1) {
+        if (cpr_strncasecmp(url_param, "transport=", 10) == 0) {
+            url_param += 10;
+            SKIP_LWS(url_param);
+            if (cpr_strncasecmp(url_param, "tcp", 3) == 0) {
+                sipUrl->transport = TRANSPORT_TCP;
+                url_param += 3;
+            } else if (cpr_strncasecmp(url_param, "tls", 3) == 0) {
+                sipUrl->transport = TRANSPORT_TLS;
+                url_param += 3;
+            } else if (cpr_strncasecmp(url_param, "sctp", 4) == 0) {
+                sipUrl->transport = TRANSPORT_SCTP;
+                url_param += 4;
+            } else if (cpr_strncasecmp(url_param, "udp", 3) == 0) {
+                sipUrl->transport = TRANSPORT_UDP;
+                url_param += 3;
+            } else {
+                SKIP_SIP_TOKEN(url_param);
+                SKIP_LWS(url_param);
+                if (*url_param != SEMI_COLON && *url_param != 0) {
+                    CCSIP_DEBUG_ERROR(ERROR_3, fname, url_param);
+                    return PARSE_ERR_SYNTAX;
+                }
+            }
+        } else if (cpr_strncasecmp(url_param, "user=", 5) == 0) {
+            url_param += 5;
+            SKIP_LWS(url_param);
+            if (cpr_strncasecmp(url_param, "phone", 5) == 0) {
+                sipUrl->is_phone = 1;
+                url_param += 5;
+            } else if (cpr_strncasecmp(url_param, "ip", 2) == 0) {
+                url_param += 2;
+            } else {
+                CCSIP_DEBUG_ERROR(ERROR_3, fname, url_param);
+                return PARSE_ERR_SYNTAX;
+            }
+        } else if (cpr_strncasecmp(url_param, "method=", 7) == 0) {
+            url_param += 7;
+            SKIP_LWS(url_param);
+            param_val = url_param;
+            while (isalpha((int) *url_param)) {
+                url_param++;
+            }
+            if (param_val == url_param) {
+                return PARSE_ERR_UNEXPECTED_EOS;
+            }
+            sipUrl->method = param_val;
+            /* Note that we have not terminated the method str yet */
+        } else if (cpr_strncasecmp(url_param, "ttl=", 4) == 0) {
+            char save_ch;
+
+            url_param += 4;
+            SKIP_LWS(url_param);
+            param_val = url_param;
+            while (isdigit((int) *url_param)) {
+                url_param++;
+                /* Atmost 3 digits allowed in ttl value */
+                if ((url_param - param_val) > 3) {
+                    CCSIP_DEBUG_ERROR(ERROR_3, fname, url_param);
+                    return PARSE_ERR_SYNTAX;
+                }
+            }
+            if (url_param == param_val) {
+                /* Did not find any digit after "ttl=" */
+                return PARSE_ERR_UNEXPECTED_EOS;
+            }
+            save_ch = *url_param;
+            *url_param = 0;  /* Terminate the string for strtoul */
+
+            errno = 0;
+            strtoul_result = strtoul(param_val, &strtoul_end, 10);
+
+            if (errno || param_val == strtoul_end || strtoul_result > MAX_TTL_VAL) {
+                CCSIP_DEBUG_ERROR(ERROR_7, fname, sipUrl->ttl_val);
+                return PARSE_ERR_INVALID_TTL_VAL;
+            }
+            sipUrl->ttl_val = (unsigned char) strtoul_result;
+            *url_param = save_ch;  /* Restore string state */
+        } else if (cpr_strncasecmp(url_param, "maddr=", 6) == 0) {
+            url_param += 6;
+            SKIP_LWS(url_param);
+            param_val = url_param; /* maddr now points to a host */
+            while (isalnum((int) *url_param) || *url_param == DOT ||
+                   *url_param == DASH) {
+                url_param++;
+            }
+            if (url_param == param_val) {
+                /* Empty value of maddr */
+                return PARSE_ERR_UNEXPECTED_EOS;
+            }
+            sipUrl->maddr = param_val;
+        } else if (cpr_strncasecmp(url_param, "phone-context=", 14) == 0) {
+            url_param += 14;
+            SKIP_LWS(url_param);
+            param_val = url_param;
+
+            while (isalpha((int) *url_param)) {
+                url_param++;
+            }
+            if (param_val == url_param) {
+                return PARSE_ERR_SYNTAX;
+            }
+
+            /* If the next character is a space, we want to zero the
+             * string, otherwise the next character is EOF or ; in
+             * which case the code below will handle these cases.
+             */
+            if (isspace((int) *url_param)) {
+                *url_param++ = 0;
+            }
+            genUrl->phone_context = param_val;
+        } else if (cpr_strncasecmp(url_param, "lr", 2) == 0) {
+            // skip to the following SEMI_COLON or end
+            while (*url_param && *url_param != SEMI_COLON) {
+                url_param++;
+            }
+            sipUrl->lr_flag = TRUE;
+        } else {
+            /* other-param */
+            url_param = parse_other_param(url_param, &url_other_param);
+
+            if (url_param == NULL) {
+                return PARSE_ERR_SYNTAX;
+            }
+
+            if (url_other_param != NULL) {
+                /* Store it in first free slot */
+                i = 0;
+                while (i < SIP_MAX_LOCATIONS) {
+                    if (genUrl->other_params[i] == NULL) {
+                        break;
+                    }
+                    i++;
+                }
+
+                if (i == SIP_MAX_LOCATIONS) {
+                    cpr_free(url_other_param);
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"parseUrlParams: Too many unknown parameters"
+                               " in URL of Contact header", fname);
+                } else {
+                    genUrl->other_params[i] = url_other_param;
+                    url_other_param = NULL;
+                }
+            }
+        }
+
+        SKIP_LWS(url_param);
+        /* We either expect - end of string or ';' */
+        if (*url_param == 0) {
+            return 0;
+        }
+        if (*url_param != SEMI_COLON) {
+            CCSIP_DEBUG_ERROR(ERROR_3, fname, url_param);
+            return PARSE_ERR_SYNTAX;
+        }
+        *url_param++ = 0; /* Zero the ';' and advance pointer */
+        SKIP_LWS(url_param);
+        if (*url_param == 0) {
+            return PARSE_ERR_UNEXPECTED_EOS;
+        }
+    }
+}
+
+
+
+
+/*
+ * This routine is based on a very simple and valid assumption. SIP URL can be
+ * of the following forms (before the parameters, which start with ';')
+ * My understanding is that it is NOT a context-free grammar
+ * sip:host
+ * sip:host:port
+ * sip:user@host
+ * sip:user@host:port
+ * sip:user:password@host
+ * sip:user:password@host:port
+ * sip:user:password@[ipv6host]:port
+ *
+ * e.g.  "1218@[2001:db8:c18:1:211:11ff:feb1:fb65]"
+ *
+ * Parse the SIP URL and zero the separators ( @ : ; etc)
+ * Point fields of sipUrl to appropriate places in the duplicated string
+ * This saves multiple mallocs for different fields of the sipUrl
+ * For a SIP URL of the form sip:user:password@host:port;params...
+ * loc_ptr[0] = Beginning of user part
+ * loc_ptr[1] = Beginning of password
+ * loc_ptr[2] = Beginning of host
+ * loc_ptr[3] = Beginning of port
+ * loc_ptr[4] = Beginning of parameters
+ */
+static int
+parseSipUrl (char *url_start, genUrl_t *genUrl)
+{
+    static const char fname[] = "parseSipUrl";
+    sipUrl_t *sipUrl = genUrl->u.sipUrl;
+    char ch = 0;
+    int token_cnt, separator_cnt;
+    char *url_main;
+    char *tokens[4];
+    char separator[4];
+    char *endptr;
+    uint16_t port;
+    boolean parsing_user_part;
+    char *temp_url;
+    boolean ipv6_addr = FALSE;
+
+    /* initializing separator */
+    separator[0] = '\0';
+
+    /*
+     * SIP-URL         = "sip:" [ userinfo "@" ] hostport
+     *              url-parameters [ headers ]
+     * userinfo        = user [ ":" password ]
+     * user            = *( unreserved | escaped
+     *            | "&" | "=" | "+" | "$" | "," )
+     * password        = *( unreserved | escaped
+     *            | "&" | "=" | "+" | "$" | "," )
+     * hostport        = host [ ":" port ]
+     * host            = hostname | IPv4address | IPv6reference
+     * IPv6reference = "[" IPv6address "]"
+     * IPv6address = hexpart [ ":" IPv4address ]
+     * hexpart = hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
+     * hexseq = hex4 *( ":" hex4)
+     * hex4 = 1*4HEXDIG
+     * port = 1*DIGIT
+     * hostname        = *( domainlabel "." ) toplabel [ "." ]
+     * domainlabel     = alphanum | alphanum *( alphanum | "-" ) alphanum
+     * toplabel        = alpha | alpha *( alphanum | "-" ) alphanum
+     * IPv4address     = 1*digit "." 1*digit "." 1*digit "." 1*digit
+     * port            = *digit
+     */
+
+    /* Ignore any leading white spaces too, */
+    SKIP_LWS(url_start);
+
+    /* we are pointing at whatever is after "sip:" */
+    url_main = url_start;
+
+    tokens[0] = url_main;
+    token_cnt = 0;
+    separator_cnt = 0;
+
+    /* Scan the string for : and @ and store their pointers. Terminate
+     * the search when you reach end of string or beginning of parameters
+     * indicated by ;. However the bis02 version of the spec allows ; in
+     * the user part of the URL. Thus the code must differentiate between
+     * a ; in the user part and a ; indicating the start of URL params.
+     */
+    while (1) {
+        if (*url_main == 0 || *url_main == SEMI_COLON ||
+            *url_main == QUESTION_MARK) {
+            /* Either end of string or beginning of SIP URL parameters
+             * or a ; encountered in the user part
+             */
+            parsing_user_part = FALSE;
+            temp_url = url_main;
+            while (*temp_url != 0) {
+                if (*temp_url == AT_SIGN) {
+                    parsing_user_part = TRUE;
+                    break;
+                } else if (*temp_url == QUESTION_MARK) {
+                    /* overloaded headers in url */
+                    break;
+                }
+                temp_url++;
+            }
+            temp_url = NULL;
+
+            if (!parsing_user_part) {
+                ch = *url_main;
+                *url_main++ = 0;  /* Terminate the current token */
+                token_cnt++;
+                break;
+            }
+        }
+
+        /* For IPv6 address colon present so skip that */
+        if ((*url_main == COLON && ipv6_addr == FALSE)|| *url_main == AT_SIGN || *url_main == SPACE ||
+            *url_main == TAB) {
+
+            if (*url_main == SPACE || *url_main == TAB) {
+                *url_main++ = 0;  /* Terminate current token */
+                SKIP_LWS(url_main);
+            }
+            /*
+             * Now we should be pointing to end of string or a separator
+             * (even ';' - beginning of parameters
+             */
+            if (*url_main == COLON || *url_main == AT_SIGN) {
+                if (separator_cnt == 3) {
+                    /* Too many separators */
+                    CCSIP_DEBUG_ERROR(ERROR_3_1, fname, *url_main);
+                    return PARSE_ERR_SYNTAX;
+                }
+                separator[separator_cnt++] = *url_main;
+                *url_main++ = 0;  /* Zero the separator */
+                SKIP_LWS(url_main);
+
+                if (*url_main == LEFT_SQUARE_BRACKET) {
+                    *url_main++ = 0; /* Must be IPv6 address */
+                    ipv6_addr = TRUE;
+                }
+                tokens[++token_cnt] = url_main;
+                if (*url_main == 0) {
+                    break;
+                }
+            }
+
+        } else if (*url_main == RIGHT_SQUARE_BRACKET && ipv6_addr == TRUE) {
+
+            *url_main++ = 0; /* Found complete IPv6 address*/
+        } else {
+            url_main++;
+        }
+    }
+
+    sipUrl->port = SIP_WELL_KNOWN_PORT;
+    sipUrl->port_present = FALSE;
+
+    /* token_cnt contains the number of entries in the tokens array */
+    switch (token_cnt) {
+
+    case 1:
+        /* sip:host */
+        sipUrl->host = tokens[0];
+        break;
+
+    case 2:
+        if (separator[0] == AT_SIGN) {
+            /* sip:user@host */
+            sipUrl->user = tokens[0];
+            sipUrl->host = tokens[1];
+            sipUrl->is_ipv6 = ipv6_addr;
+        } else {
+            /* sip:host:port */
+            sipUrl->host = tokens[0];
+            port = (uint16_t) strtol(tokens[1], &endptr, 10);
+            if (*endptr == 0) {
+                sipUrl->port = port;
+                sipUrl->port_present = TRUE;
+            } else {
+                sipUrl->port = 0;
+            }
+        }
+        break;
+
+    case 3:
+        if (separator[0] == separator[1]) {
+            /* Cannot have 2 successive entries of : or @ */
+            CCSIP_DEBUG_ERROR(ERROR_3_1, fname, separator[1]);
+            return PARSE_ERR_SYNTAX;
+        }
+        if (separator[0] == AT_SIGN) {
+            /* sip:user@host:port */
+            sipUrl->user = tokens[0];
+            sipUrl->host = tokens[1];
+            port = (uint16_t) strtol(tokens[2], &endptr, 10);
+            if (*endptr == 0) {
+                sipUrl->port = port;
+                sipUrl->port_present = TRUE;
+            } else {
+                sipUrl->port = 0;
+            }
+        } else {
+            /* sip:user:password@host */
+            sipUrl->user = tokens[0];
+            sipUrl->password = tokens[1];
+            sipUrl->host = tokens[2];
+        }
+        break;
+
+    case 4:
+        /* sip:user:password@host:port */
+
+        if (separator[0] == COLON && separator[1] == AT_SIGN &&
+            separator[2] == COLON) {
+            sipUrl->user = tokens[0];
+            sipUrl->password = tokens[1];
+            sipUrl->host = tokens[2];
+            port = (uint16_t) strtol(tokens[3], &endptr, 10);
+            if (*endptr == 0) {
+                sipUrl->port = port;
+                sipUrl->port_present = TRUE;
+            } else {
+                sipUrl->port = 0;
+            }
+        } else {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad separator sequence", fname);
+            return PARSE_ERR_SYNTAX;
+        }
+        break;
+
+    }
+
+    /* Remove [ and ] from IPv6 address*/
+    trim_ipv6_host(sipUrl->host);
+
+    /* Verify the Port */
+    if (sipUrl->port == 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received Bad Port", fname);
+        return PARSE_ERR_SYNTAX;
+    }
+
+    /* Verify the host portion */
+    if (sipSPI_validate_ip_addr_name(sipUrl->host) == FALSE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received Bad Host", fname);
+        return PARSE_ERR_SYNTAX;
+    }
+
+    /* Set default transport to UNSPECIFIED */
+    sipUrl->transport = TRANSPORT_UNSPECIFIED;
+    if (ch == SEMI_COLON) { /* Process URL parameters */
+        return parseUrlParams(url_main, sipUrl, genUrl);
+    } else if (ch == QUESTION_MARK) { /* Process URL headers */
+        return url_add_headers_to_list(url_main, sipUrl);
+    }
+    return 0;  /* SUCCESS */
+}
+
+/*
+ * This routine is based on a very simple and valid assumption. TEL URL can be
+ * of the following forms (before the parameters, which start with ';')
+ * tel:user
+ *
+ * Parse the TEL URL and zero the separators (;)
+ * Point fields of sipUrl to appropriate places in the duplicated string
+ * This saves multiple mallocs for different fields of the telUrl
+ * For a TEL URL of the form tel:user;params...
+ */
+static int
+parseTelUrl (char *url_start, genUrl_t *genUrl)
+{
+    static const char fname[] = "parseTelUrl";
+    char *url_main;
+    telUrl_t *telUrl = genUrl->u.telUrl;
+    boolean is_local_subscriber = FALSE;
+
+    /*
+     * TEL-URL               =  telephone-scheme ":" telephone-subscriber
+     * telephone-scheme      =  "tel"
+     * telephone-subscriber  =  global-phone-number/local-phone-number
+     * global-phone-number   =  "+" base-phone-number [isdn-subaddress]
+     *                          [post-dial] *(area-specifier/service-provider/
+     *                          future-extension)
+     * base-phone-number     =  1*phonedigit
+     * local-phone-number    =  1*(phonedigit / dtmf-digit /pause-character)
+     *                          [isdn-subaddress] [post-dial] area-specifier
+     *                          *(are-specifier/service-provider/
+     *                          future-extension)
+     * isdn-subaddress       =  ";isub=" 1*phonedigit
+     * post-dial             =  ";postd=" 1*(phonedigit / dtmf-digit /
+     *                          pause-character)
+     * area-specifier        =  ";" phone-context-tag "=" phone-context-ident
+     * phone-context-tag     =  "phone-context"
+     * phone-context-ident   =  network-prefix / private-prefix
+     * network-prefix        =  global-network-prefix / local-network-prefix
+     * global-network-prefix =  "+" 1*phonedigit
+     * local-network-prefix  =  1*(phonedigit / dtmf-digit / pause-character)
+     * private-prefix        =  (%x21-22 / %x24-27 / %x2C / %x2F / %x3A /
+     *                           %x3C-40 /%x45-4F / %x51-56 / %x58-60 /
+     *                           %x65-6F / %x71-76 / %x78-7E /) *(%x21-3A /
+     *                           %x3C-7E)
+     * service-provider      =  ";" provider-tag "=" provider-hostname
+     * provider-tag          =  "tsp"
+     * provider-hostname     =  domain ; <domain> is defined in [RFC1035]
+     * future-extension      =  ";" token ["=" token]
+     * phonedigit            =  DIGIT / visual-separator
+     * visual-separator      =  "-" / "." / "(" / ")"
+     * pause-character       =  "p" / "w"
+     * dtmf-digit            =  "*" / "#" / "A" / "B" / "C" / "D"
+     */
+
+    if (!url_start) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Missing user field", fname);
+        return PARSE_ERR_SYNTAX;
+    }
+
+    url_main = url_start;
+    SKIP_LWS(url_main);
+    /*
+     * User field is the only mandatory field.  The rest are optional
+     */
+
+    if (*url_main == PLUS) {
+        /* global-phone number */
+        url_main++;
+        telUrl->user = url_main;
+
+        while ((url_main) && ((*url_main == DASH) ||
+               (isdigit((int)*url_main)) || (*url_main == DOT))) {
+            url_main++;
+        }
+    } else {
+        /* local-phone-number */
+        telUrl->user = url_main;
+        is_local_subscriber = TRUE;
+
+        while ((url_main) && ((is_dtmf_or_pause(url_main)) ||
+               (*url_main == DASH) || (isdigit((int)*url_main)) ||
+               (*url_main == DOT))) {
+            url_main++;
+        }
+    }
+    if (url_main) {
+        SKIP_LWS(url_main);
+    }
+    if ((url_main) && (!(*url_main))) {
+        if (!is_local_subscriber) {
+            return 0;  /* no fields to parse */
+        } else {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"local-phone-number must have area-specifier",
+                       fname);
+            return PARSE_ERR_SYNTAX;
+        }
+    }
+    if (url_main) {
+        if ((*url_main) && (*url_main == SEMI_COLON)) {
+            *url_main++ = 0;  /* skip separator */
+        } else {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Need ';' before parameters", fname);
+            return PARSE_ERR_SYNTAX;
+        }
+    }
+
+    while (url_main) {
+        SKIP_LWS(url_main);
+        if (cpr_strncasecmp(url_main, "isub=", 5) == 0) {
+            url_main += 5;
+            SKIP_LWS(url_main);
+            telUrl->isdn_subaddr = url_main;
+        } else if (cpr_strncasecmp(url_main, "postd=", 6) == 0) {
+            url_main += 6;
+            SKIP_LWS(url_main);
+            telUrl->post_dial = url_main;
+        } else if (cpr_strncasecmp(url_main, "phone-context", 13) == 0) {
+            url_main += 13;
+            SKIP_LWS(url_main);
+            if (*url_main != EQUAL_SIGN) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad syntax in phone_context field", fname);
+                return PARSE_ERR_SYNTAX;
+            }
+            *url_main++ = 0;  /* skip "=" */
+            SKIP_LWS(url_main);
+            genUrl->phone_context = url_main;
+        } else if (cpr_strncasecmp(url_main, "tsp", 3) == 0) {
+            url_main += 3;
+            SKIP_LWS(url_main);
+            if (*url_main != EQUAL_SIGN) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad syntax in service-provider field", fname);
+                return PARSE_ERR_SYNTAX;
+            }
+            *url_main++ = 0;  /* skip "=" */
+            SKIP_LWS(url_main);
+            telUrl->unparsed_tsp = url_main;
+        } else {
+            /* future extension */
+            if (telUrl->future_ext) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Only one future extension allowed", fname);
+                return PARSE_ERR_SYNTAX;
+            }
+            SKIP_LWS(url_main);
+            url_main = strchr(url_main, EQUAL_SIGN);
+            if (!url_main) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad field value", fname);
+                return PARSE_ERR_SYNTAX;
+            } else {
+                *url_main++ = 0;
+                SKIP_LWS(url_main);
+                telUrl->future_ext = url_main;
+            }
+        }
+        url_main = strchr(url_main, SEMI_COLON);
+        if (url_main) {
+            *url_main++ = 0;  /* skip and zero separator */
+        }
+    }
+    if ((is_local_subscriber) && (!genUrl->phone_context)) {
+        /* local-phone-number must have area-specifier */
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"local-phone-number requires area-specifier", fname);
+        return PARSE_ERR_SYNTAX;
+    }
+    return 0;  /* Success */
+}
+
+/*
+ * Parses a URL
+ * (eg. "sip:14085266593@sip.cisco.com;transport=UDP;user=phone")
+ * (eg. "tel:+1-555-555-5555")
+ * into a usable genUrl_t struct. Memory is created by this function.
+ * To free first free internal members(sippmh_genurl_free(genUrl)).
+ * Returns NULL if there is a parse error or malloc fails. Components/tokens
+ * not found in the URL are set to NULL, unless they have a default value
+ * defined by the protocol ( for example port 5060 is default)
+ * Parse error will set parse_errno variable to indicate the error.
+ * Expects a NULL terminated string in url.
+ * See comment block before parseSipUrl() and parseTelUrl()
+ * for some other details.
+ *
+ * url - Pointer to URL to be parsed
+ * dup_flag - Should this URL be duplicated and modified ??
+ */
+genUrl_t *
+sippmh_parse_url (char *url, boolean dup_flag)
+{
+    genUrl_t *genUrl;
+    char     *url_main;
+    uint16_t  i;
+    uint16_t  skipValue;
+
+    if (url == NULL) {
+        return NULL;
+    }
+
+    genUrl = (genUrl_t *) cpr_calloc(1, sizeof(genUrl_t));
+    if (genUrl == NULL) {
+        return NULL;
+    }
+
+    /*
+     * Ensure clean other params pointers
+     */
+    i = 0;
+    while (i < SIP_MAX_LOCATIONS) {
+        genUrl->other_params[i] = NULL;
+        i++;
+    }
+
+    if (dup_flag) {
+        url_main = cpr_strdup(url);
+        if (!url_main) {
+            cpr_free(genUrl);
+            return NULL;
+        }
+        genUrl->str_start = url_main;
+    } else {
+        url_main = url;
+    }
+
+    SKIP_LWS(url_main);
+    if (!cpr_strncasecmp(url_main, "sips", 4)) {
+        genUrl->schema = URL_TYPE_SIP;
+        genUrl->sips = TRUE;
+        skipValue = 4;
+    } else if (!cpr_strncasecmp(url_main, "sip", 3)) {
+        genUrl->schema = URL_TYPE_SIP;
+        skipValue = 3;
+    } else if (!cpr_strncasecmp(url_main, "tel", 3)) {
+        genUrl->schema = URL_TYPE_TEL;
+        skipValue = 3;
+    } else if (!cpr_strncasecmp(url_main, "cid", 3)) {
+        genUrl->schema = URL_TYPE_CID;
+        skipValue = 3;
+    } else {
+        genUrl->schema = URL_TYPE_UNKNOWN;
+        skipValue = 0;
+    }
+    url_main += skipValue;  /* skip "sip", "sips", cid, or "tel" */
+    if (*url_main != COLON) {
+        if (dup_flag) {
+            cpr_free(genUrl->str_start);
+        }
+        cpr_free(genUrl);
+        return NULL;
+    }
+    *url_main++ = 0;        /* terminate ":" for schema */
+    switch (genUrl->schema) {
+    case URL_TYPE_CID:
+    case URL_TYPE_SIP:
+        genUrl->u.sipUrl = (sipUrl_t *) cpr_calloc(1, sizeof(sipUrl_t));
+        if (!genUrl->u.sipUrl) {
+            if (dup_flag) {
+                cpr_free(genUrl->str_start);
+            }
+            cpr_free(genUrl);
+            return NULL;
+        }
+        parse_errno = parseSipUrl(url_main, genUrl);
+        if (parse_errno == 0) {
+            //remove_visual_separators(genUrl);
+            if (genUrl->u.sipUrl->port == 0) {
+                /* If no port specified in SIP-URL, then use 5060 */
+                genUrl->u.sipUrl->port = SIP_WELL_KNOWN_PORT;
+                genUrl->u.sipUrl->port_present = FALSE;
+            }
+            return genUrl;
+        }
+        /* For syntax error, we would already have printed the error msg */
+        if (parse_errno != PARSE_ERR_SYNTAX) {
+            if (parse_errno == PARSE_ERR_NO_MEMORY) {
+                parse_errno = 0;  /* Not really a parse error (out of mem) */
+            } else {
+                CCSIP_DEBUG_ERROR(parse_errors[parse_errno], "sippmh_parse_url");
+            }
+        }
+        sippmh_genurl_free(genUrl);
+        return NULL;
+    case URL_TYPE_TEL:
+        genUrl->u.telUrl = (telUrl_t *) cpr_calloc(1, sizeof(telUrl_t));
+        if (!genUrl->u.telUrl) {
+            if (dup_flag) {
+                cpr_free(genUrl->str_start);
+            }
+            cpr_free(genUrl);
+            return NULL;
+        }
+        parse_errno = parseTelUrl(url_main, genUrl);
+        if (parse_errno == 0) {
+            //remove_visual_separators(genUrl);
+            return genUrl;
+        }
+        /* For syntax error, we would already have printed the error msg */
+        if (parse_errno != PARSE_ERR_SYNTAX) {
+            if (parse_errno == PARSE_ERR_NO_MEMORY) {
+                parse_errno = 0;  /* Not really a parse error (out of mem) */
+            } else {
+                CCSIP_DEBUG_ERROR(parse_errors[parse_errno], "sippmh_parse_url");
+            }
+        }
+        sippmh_genurl_free(genUrl);
+        return NULL;
+    default:
+        sippmh_genurl_free(genUrl);
+        return NULL;
+    }
+}
+
+/* If this is a SIP URL, free the sipUrl_t.  If this is
+ * a telUrl_t free the telUrl_t.  Also, free memory allocated by creation
+ * of the genUrl_t then free the genUrl_t.
+ */
+void
+sippmh_genurl_free (genUrl_t *genUrl)
+{
+    uint16_t i;
+
+    if (!genUrl) {
+        return;
+    }
+    if (genUrl->str_start) {
+        cpr_free(genUrl->str_start);
+    }
+    if ((genUrl->schema == URL_TYPE_SIP) ||
+        (genUrl->schema == URL_TYPE_CID)) {
+        if (genUrl->u.sipUrl->headerp) {
+            cpr_free(genUrl->u.sipUrl->headerp);
+        }
+        cpr_free(genUrl->u.sipUrl);
+    } else if (genUrl->schema == URL_TYPE_TEL) {
+        cpr_free(genUrl->u.telUrl);
+    }
+
+    /* Free any "other" parameters that were saved */
+    i = 0;
+    while (i < SIP_MAX_LOCATIONS) {
+        if (genUrl->other_params[i] != NULL) {
+            cpr_free(genUrl->other_params[i]);
+        }
+        i++;
+    }
+
+    cpr_free(genUrl);
+}
+
+/* Parse display name:
+ * "Mr. Watson" <sip:watson@bell-telephone.com>
+ * "Mr. Watson" is the display-name
+ * Return pointer to the '<'
+ */
+
+static char *
+parse_display_name (char *ptr)
+{
+    static const char fname[] = "parse_display_name";
+
+    while (*ptr) {
+        if (*ptr == DOUBLE_QUOTE && *(ptr - 1) != ESCAPE_CHAR) {
+            /* We reached end of quoted-string, mark end of display name */
+            *ptr++ = 0;
+            SKIP_LWS(ptr); /* Skip spaces till '<' */
+            /* ptr should now be pointing to '<' */
+            if (*ptr != LEFT_ANGULAR_BRACKET) {
+                parse_errno = PARSE_ERR_UNMATCHED_BRACKET;
+                CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname);
+
+                return NULL;
+            }
+            return ptr;
+        }
+        ptr++;
+    }
+
+    parse_errno = PARSE_ERR_UNTERMINATED_STRING;
+    CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname);
+
+    return NULL;  /* Unmatched " */
+}
+
+
+/*
+ * Parse the name-addr form or the addr-spec form. These parse entities can
+ * be present in the From, To and Contact headers. So it is called either
+ * by sippmh_parse_contact() or sippmh_parse_from_or_to().
+ *
+ * input_loc_ptr - Pointer to the string to be parsed
+ * dup_flag - Should the input string be duplicated before parsing
+ * more_ptr - Return pointer to the next
+ * Tokens are extracted in place, i.e. we do not do a strdup for every token.
+ * We have on string and the output struct has pointers pointing to various
+ * tokens in the string.
+ * Note that this routine relies on the caller setting parse_errno to 0
+ * before calling.
+ *
+ * input_loc_ptr - Pointer to input string to be parsed
+ * start_ptr - Pointer to beginning of the string, which will be used for
+ *            cleanup.
+ * dup_flag - Should the input string be duplicated before parsing
+ * name_addr_only_flag - If TRUE, means only name-addr form is allowed
+ * more_ptr - Return pointer to start of remaining string ( string that
+ *            remains after parsing name-addr or addr-spec
+ *
+ * Output: A pointer to a valid SIP location  OR
+ *         NULL (in case of error)
+ *
+ */
+sipLocation_t *
+sippmh_parse_nameaddr_or_addrspec (char *input_loc_ptr,
+                                   char *start_ptr,
+                                   boolean dup_flag,
+                                   boolean name_addr_only_flag,
+                                   char **more_ptr)
+{
+    const char *fname = "sippmh_parse_nameaddr_or_addrspec";
+    char           *addr_param;
+    char           *loc_ptr, *addr_spec, *left_bracket;
+    sipLocation_t  *sipLoc = NULL;
+    char           *right_bracket = NULL;
+    char            save_ch = 0;
+    char           *displayNameStart;
+
+    *more_ptr = NULL;
+
+    /*
+     * name-addr      = [ display-name ] "<" addr-spec ">"
+     * addr-spec      = SIP-URL | URI
+     * display-name   = *token | quoted-string
+     */
+    if (dup_flag) {
+        /* Duplicate the string and work with it */
+        start_ptr = loc_ptr = cpr_strdup(input_loc_ptr);
+        if (loc_ptr == NULL) {
+            return NULL;
+        }
+    } else {
+        loc_ptr = input_loc_ptr;
+    }
+
+    if (*loc_ptr == DOUBLE_QUOTE) {
+        displayNameStart = loc_ptr + 1;
+        left_bracket = parse_display_name(loc_ptr + 1);
+        if (left_bracket == NULL) {
+            /* Could not find matching " or reached end of string or could not
+             * find <
+             */
+            if (dup_flag) {
+                cpr_free(loc_ptr);
+            }
+            /*  parse_display_name has already set the parse_errno */
+
+            return NULL;
+        }
+    } else {
+
+        displayNameStart = loc_ptr;
+        /* Either we have token(s) preceding '<' or start of addr-spec */
+        left_bracket = strpbrk(loc_ptr, ",<");
+        if (left_bracket) {
+            if (*left_bracket == COMMA) {
+                *left_bracket = 0;
+                *more_ptr = left_bracket;
+                left_bracket = NULL;
+                save_ch = COMMA;
+            }
+        } else {
+            *more_ptr = NULL;
+        }
+    }
+
+    sipLoc = (sipLocation_t *) cpr_calloc(1, sizeof(sipLocation_t));
+    if (sipLoc == NULL) {
+        if (dup_flag) {
+            cpr_free(loc_ptr);
+        }
+        return NULL;
+    }
+    sipLoc->loc_start = start_ptr;  /* Save pointer to start of allocated mem */
+
+    if (left_bracket) {
+        /* This is a name-addr form */
+
+        *left_bracket = 0; /* Terminate the display-name portion */
+        sipLoc->name = displayNameStart;
+        addr_spec = left_bracket + 1;
+        right_bracket = strchr(addr_spec, RIGHT_ANGULAR_BRACKET);
+        if (right_bracket == NULL) {
+            if (dup_flag) {
+                cpr_free(loc_ptr);
+            }
+            cpr_free(sipLoc);
+            parse_errno = PARSE_ERR_UNMATCHED_BRACKET;
+            CCSIP_DEBUG_ERROR(parse_errors[parse_errno],
+                       "sippmh_parse_nameaddr_or_addrspec");
+            return NULL;
+        }
+
+        /* Terminate the addr_spec at the > */
+        *right_bracket++ = 0;
+
+        /* Look for the next non-whitespace character */
+        SKIP_LWS(right_bracket);
+        *more_ptr = right_bracket;
+    } else {
+
+        /* This is addr-spec format */
+
+        if (name_addr_only_flag) {
+            if (dup_flag) {
+                cpr_free(loc_ptr);
+            }
+            cpr_free(sipLoc);
+            CCSIP_ERR_DEBUG {
+                buginf("\n%s: Bad name-addr format", fname);
+            }
+            return NULL;
+        }
+
+        addr_spec = loc_ptr;
+        /* In addr-spec form, it is illegal to have URL special
+         * chars such as SEMI_COLON, QUESTION MARK
+         */
+        addr_param = addr_spec;
+        while (*addr_param) {
+            if (*addr_param == QUESTION_MARK || *addr_param == SEMI_COLON) {
+
+                if ((save_ch != 0) && *more_ptr) {
+                    /* restore the saved char mostly COMMA */
+                    **more_ptr = save_ch;
+                }
+
+                save_ch = *addr_param;
+                *more_ptr = addr_param;
+
+                /* Terminate the addr-spec form at Special char */
+                *addr_param = 0;
+                break;
+            }
+            addr_param++;
+        }
+    }
+
+    sipLoc->genUrl = sippmh_parse_url(addr_spec, FALSE);
+    if (sipLoc->genUrl == NULL) {
+        if (dup_flag) {
+            cpr_free(loc_ptr);
+        }
+        cpr_free(sipLoc);
+        /* sippmh_parse_url would have set the parse_errno  */
+        return NULL;
+    }
+
+    /*
+     * There is more stuff to follow since *more_ptr is not NULL
+     * replace the special chars (such as ? or ;) in original string
+     */
+    if ((save_ch != 0) && (*more_ptr != NULL)) {
+        **more_ptr = save_ch;
+    }
+    return sipLoc;
+}
+
+
+/*
+ * Validate the tag. The tag is composed of tokens. The tag_ptr
+ * points to the start of the tag and a list of semicolon
+ * separated addr-extensions or just the tag itself.
+ *
+ * Returns 0 (no error) if the tag is valid, else
+ * returns PARSE_ERR_SYNTAX.
+ *
+ */
+static int
+validate_tag (sipLocation_t *sipLoc, char *tag_ptr)
+{
+    static const char fname[] = "validate_tag";
+    int   ret_val = 0;
+    char *term_ptr;
+
+    /* Skip "tag=", points to value */
+    tag_ptr += 4;
+    SKIP_LWS(tag_ptr);
+    if (*tag_ptr == 0) {
+        ret_val = PARSE_ERR_UNEXPECTED_EOS;
+        CCSIP_ERR_DEBUG {
+            buginf(parse_errors[ret_val], fname);
+        }
+
+        return ret_val;
+    }
+
+    sipLoc->tag = tag_ptr;
+
+    /* Walk the string until we encounter a non-token
+     * character. The only valid character at the end of
+     * string is either semicolon or end-of-line.
+     */
+
+    SKIP_SIP_TOKEN(tag_ptr);
+    term_ptr = tag_ptr;
+    /* skip trailing spaces */
+    SKIP_LWS(tag_ptr);
+    if ((*tag_ptr != SEMI_COLON) && (*tag_ptr != 0)) {
+        ret_val = PARSE_ERR_SYNTAX;
+        CCSIP_ERR_DEBUG {
+            buginf(parse_errors[ret_val], fname, tag_ptr);
+        }
+    } else {
+        /* terminate the tag */
+        *term_ptr = 0;
+    }
+
+    return ret_val;
+}
+
+
+sipLocation_t *
+sippmh_parse_from_or_to (char *input_loc_ptr, boolean dup_flag)
+{
+    static const char fname[] = "sippmh_parse_from_or_to";
+    char *more_ptr;
+    sipLocation_t *sipLoc;
+    boolean tag_found = FALSE;
+    char *lasts = NULL;
+
+    /*
+     *  From         =  ( "From" | "f" ) ":" ( name-addr | addr-spec )
+     *                  *( ";" addr-params )
+     *  addr-params  =  tag-param | addr-extension
+     *  tag-param    =  "tag=" token
+     *  addr-exten   =  token = ["=" (token | quoted-string)]
+     *
+     *  NOTE: Only the tag parameter in the addr-params is relevant to
+     *        SIP so we skip all addr-extensions when parsing addr-params.
+     */
+
+    parse_errno = 0;
+    more_ptr = NULL;
+    sipLoc = sippmh_parse_nameaddr_or_addrspec(input_loc_ptr, input_loc_ptr,
+                                               dup_flag, FALSE, &more_ptr);
+
+    if (sipLoc) {  /* valid sipLocation */
+
+        if (more_ptr == NULL) {
+            return sipLoc;
+        }
+
+        /* initialize the tag */
+        sipLoc->tag = NULL;
+        if (*more_ptr == SEMI_COLON) {
+            *more_ptr++ = 0;
+            more_ptr = PL_strtok_r(more_ptr, ";", &lasts);
+            /* if we had a ; without any addr-params */
+            if (more_ptr == NULL) {
+                parse_errno = PARSE_ERR_UNEXPECTED_EOS;
+                CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname);
+            } else {
+                /* parse the tag but skip any addr-extensions */
+                while (more_ptr && tag_found == FALSE) {
+                    SKIP_LWS(more_ptr);
+                    if (strncmp(more_ptr, "tag=", 4) == 0) {
+                        tag_found = TRUE;
+                        parse_errno = validate_tag(sipLoc, more_ptr);
+                    } else {
+                        more_ptr = PL_strtok_r(NULL, ";", &lasts);
+                    }
+                }
+            }
+
+        } else if (*more_ptr) {
+            parse_errno = PARSE_ERR_SYNTAX;
+            CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname, more_ptr);
+        }
+    }
+
+    if (parse_errno) {
+        sippmh_free_location(sipLoc);
+        sipLoc = NULL;
+    }
+
+    return sipLoc;
+}
+
+void
+sippmh_free_location (sipLocation_t * sipLoc)
+{
+    if (sipLoc) {
+        cpr_free(sipLoc->loc_start);
+        /* make sure to free contents in genUrl_t. */
+        sippmh_genurl_free(sipLoc->genUrl);
+        cpr_free(sipLoc);
+    }
+}
+
+static char *
+sippmh_parse_contact_params (char *params, sipContactParams_t *contact_params)
+{
+    char   *param_value;
+    char   *contact_other_param = NULL;
+    boolean good_params;
+    boolean good_qval;
+    char    tmp_char;
+
+
+    /*
+     *    contact-params = "q"       "=" qvalue
+     *            | "action"  "=" "proxy" | "redirect"
+     *            | "expires" "=" delta-seconds | <"> SIP-date <">
+     *            | extension-attribute
+     *
+     * params is pointing to first character after ';'
+     */
+    SKIP_LWS(params);
+    if (*params == 0) {
+        return params;
+    }
+    while (1) {
+        good_params = FALSE;
+
+        /* Parse qval parameter */
+        if (*params == 'q' || *params == 'Q') {
+            params++;
+            SKIP_LWS(params);
+            if (*params == EQUAL_SIGN) {
+                params++;
+                SKIP_LWS(params);
+                if (*params) {
+                    param_value = params;
+                    good_qval = TRUE;
+                    tmp_char = *params;
+                    /* the qval must be in one of these forms */
+                    if ((!strncmp(params, "0", 1)) ||
+                        (!strncmp(params, "1", 1))) {
+                        params++;  /* move up to "." if present */
+                        if (*params == DOT) {
+                            params++;
+                            if (isdigit((int) *params)) {
+                                while (isdigit((int) *params)) {
+                                    if ((tmp_char == '1') &&
+                                        (*params != '0')) {
+                                        good_qval = FALSE;
+                                        break;
+                                    }
+                                    params++;
+                                }
+                            } else {
+                                good_qval = FALSE;
+                            }
+                        } else if (((*params)) && ((*params != SPACE) &&
+                                   (*params != SEMI_COLON) &&
+                                   (*params != TAB))) {
+                            good_qval = FALSE;
+                        }
+                    } else {
+                        good_qval = FALSE;
+                    }
+                    if (good_qval) {
+                        good_params = TRUE;
+                        contact_params->qval = param_value;
+                        if (*params == SPACE || *params == TAB) {
+                            *params++ = 0;  /* Terminate the qval */
+                        }
+                    }
+                }
+            }
+
+            /* Parse action paramater */
+        } else if (cpr_strncasecmp(params, "action", 6) == 0) {
+            params += 6;
+            SKIP_LWS(params);
+            if (*params == EQUAL_SIGN) {
+                params++;
+                SKIP_LWS(params);
+                if (cpr_strncasecmp(params, "proxy", 5) == 0) {
+                    contact_params->action = PROXY;
+                    params += 5;
+                    good_params = TRUE;
+                } else if (cpr_strncasecmp(params, "redirect", 8) == 0) {
+                    contact_params->action = REDIRECT;
+                    params += 8;
+                    good_params = TRUE;
+                }
+            }
+
+            /* Parse expires paramater */
+        } else if (cpr_strncasecmp(params, "expires", 7) == 0) {
+            params += 7;
+            SKIP_LWS(params);
+            if (*params == EQUAL_SIGN) {
+                params++;
+                SKIP_LWS(params);
+                if (*params) {
+                    char save_ch;
+                    boolean is_int = FALSE;
+
+                    /* Expires can be given in delta seconds or date format.
+                     * If date is used, the date must be enclosed in quotes.
+                     * IOS does not use this parameter, but the SIP phones
+                     * do.
+                     */
+                    contact_params->expires_gmt = '\0';
+                    good_params = TRUE;
+                    param_value = params;
+                    while (isdigit((int) *params)) {
+                        params++;
+                        is_int = TRUE;
+                    }
+                    if (is_int == TRUE) {
+                        save_ch = *params;
+                        *params = 0; /* terminate the string for strtoul */
+                        contact_params->expires = strtoul(param_value, NULL, 10);
+                        *params = save_ch;
+                    } else {
+                        /* expires may be in SIP-date format */
+                        char *gmt_str;
+
+                        if (*params == DOUBLE_QUOTE) { /* skip the quote */
+                            params++;
+                        }
+                        if ((gmt_str = strstr(params, "GMT")) != NULL) {
+                            contact_params->expires_gmt = params;
+                            params = gmt_str + 3;
+                            contact_params->expires = 0;
+
+                            if (*params == SPACE || *params == TAB || *params == DOUBLE_QUOTE) {
+                                *params++ = '\0';
+                            }
+                        }
+                    }
+                }
+            }
+            /* Parse x-cisco-newreg */
+        } else if (cpr_strncasecmp(params, REQ_CONT_PARAM_CISCO_NEWREG,
+                                   sizeof(REQ_CONT_PARAM_CISCO_NEWREG) - 1) == 0) {
+            /*
+             * x-cisco-newreg in the contact is set by CCM in the 200 OK
+             * response to the 1st. line registration message as information
+             * only to the endpoint that its registration is a new
+             * registration to the CCM.
+             */
+
+            /*
+             * Move parameter pointer pass this parm (-1 is to exclude
+             * null terminating character of the parameter name).
+             */
+            params += sizeof(REQ_CONT_PARAM_CISCO_NEWREG) - 1;
+            good_params = TRUE;
+            contact_params->flags |= SIP_CONTACT_PARM_X_CISCO_NEWREG;
+
+            /* Parse contact-extension paramater */
+        } else {
+            if (params) {
+                params = parse_other_param(params, &contact_other_param);
+                if (params) {
+                    good_params = TRUE;
+                    /*
+                     * Contact extensions are not supported. Throw away
+                     * any that were just parsed.
+                     */
+                    if (contact_other_param != NULL) {
+                        cpr_free(contact_other_param);
+                    }
+                }
+            }
+        }
+        if (good_params == FALSE) {
+            parse_errno = PARSE_ERR_SYNTAX;
+            return params;
+        }
+        SKIP_LWS(params);
+        if (*params == SEMI_COLON) {
+            /* More parameters follow */
+            *params++ = 0;
+            SKIP_LWS(params);
+        } else {
+            /* Either we have encountered end of string or start of next
+             * location in this contact header.
+             */
+            return params;
+        }
+    }
+}
+
+/* Parses limit parameter for CC-Diversion or CC-Redirect
+ * header. The parsed value is in sipDiversion_t structure.
+ * Returns pointer to remaining characters in the buffer, if fails NULL
+ */
+static char *
+sippmh_parse_limit_params (char *limit_t, sipDiversion_t *sipdiversion)
+{
+    boolean params_good = FALSE;
+    char save_ch;
+    char *param_value = NULL;
+    int digit_count = 0;
+
+    if ((limit_t == NULL) || (sipdiversion == NULL)) {
+        return NULL;
+    }
+
+    SKIP_LWS(limit_t);
+    if (*limit_t == EQUAL_SIGN) {
+        limit_t++;
+        SKIP_LWS(limit_t);
+        if (*limit_t) {
+            params_good = TRUE;
+            param_value = limit_t;
+            while (isdigit((int) *limit_t)) {
+                digit_count++;
+                limit_t++;
+            }
+            if (digit_count > 2) {
+                /* More than two digits */
+                params_good = FALSE;
+                return NULL;
+            }
+            digit_count = 0;
+            save_ch = *limit_t;
+            *limit_t = 0;
+            sipdiversion->limit = strtoul(param_value, NULL, 10);
+            *limit_t = save_ch;
+        }
+    } else {
+        /* Equal to sign missing */
+        params_good = FALSE;
+    }
+
+    if (params_good) {
+        return limit_t;
+    } else {
+        return NULL;
+    }
+}
+
+
+/* Parses counter parameter for CC-Diversion or CC-Redirect
+ * header. The parsed value is in sipDiversion_t structure.
+ * Returns pointer to remaining characters in the buffer, if fails NULL
+ */
+static char *
+sippmh_parse_counter_params (char *counter_t, sipDiversion_t *sipdiversion)
+{
+    boolean params_good = FALSE;
+    char save_ch;
+    char *param_value = NULL;
+    int digit_count = 0;
+
+    if ((counter_t == NULL) || (sipdiversion == NULL)) {
+        return NULL;
+    }
+
+    SKIP_LWS(counter_t);
+    if (*counter_t == EQUAL_SIGN) {
+        counter_t++;
+        SKIP_LWS(counter_t);
+        if (*counter_t) {
+            params_good = TRUE;
+            param_value = counter_t;
+            while (isdigit((int) *counter_t)) {
+                digit_count++;
+                counter_t++;
+            }
+            if (digit_count > 2) {
+                /* More than two digits */
+                params_good = FALSE;
+                return NULL;
+            }
+            digit_count = 0;
+            save_ch = *counter_t;
+            *counter_t = 0;
+            sipdiversion->counter = strtoul(param_value, NULL, 10);
+            *counter_t = save_ch;
+
+        }
+    } else {
+        /* Equal to sign missing */
+        params_good = FALSE;
+    }
+
+    if (params_good) {
+        return counter_t;
+    } else {
+        return NULL;
+    }
+}
+
+
+static boolean
+sippmh_parse_diversion_params (char *diversion_t, sipDiversion_t *sipdiversion)
+{
+
+    while (1) {
+        /* Parsing diversion-reason parameter */
+        if (strncasecmp(diversion_t, DIVERSION_REASON, 6) == 0) {
+// We don't use reason parameter, so comment out at this time.
+//         diversion_t += 6;
+//         if ((diversion_t = sippmh_parse_reason_params(diversion_t, sipdiversion)) == NULL)
+//            return FALSE;
+            /*
+             * Since we do not use reason parameter, search for semi-colan to mark
+             * start of next parameter. If no semi-colon, then we are done.
+             */
+            diversion_t = strchr(diversion_t, SEMI_COLON);
+            if (diversion_t == NULL) {
+                // End of parameter list
+                return TRUE;
+            }
+            /* Parse diversion-limit parameter   */
+        } else if (strncasecmp(diversion_t, DIVERSION_LIMIT, 5) == 0) {
+            diversion_t += 5;
+            if ((diversion_t = sippmh_parse_limit_params(diversion_t, sipdiversion)) == FALSE)
+                return FALSE;
+
+            /* Parse diversion-counter parameter   */
+        } else if (strncasecmp(diversion_t, DIVERSION_COUNTER, 7) == 0) {
+            diversion_t += 7;
+            if ((diversion_t = sippmh_parse_counter_params(diversion_t, sipdiversion)) == FALSE)
+                return FALSE;
+
+            /* Parse diversion-privacy parameter   */
+        } else if (strncasecmp(diversion_t, DIVERSION_PRIVACY, 7) == 0) {
+            diversion_t += 7;
+            if ((diversion_t = parse_generic_param(diversion_t, &(sipdiversion->privacy))) == NULL)
+                return FALSE;
+
+            /* Parse diversion-screen parameter   */
+        } else if (strncasecmp(diversion_t, DIVERSION_SCREEN, 6) == 0) {
+            diversion_t += 6;
+            if ((diversion_t = parse_generic_param(diversion_t, &(sipdiversion->screen))) == NULL)
+                return FALSE;
+        }
+
+        SKIP_LWS(diversion_t);
+        if (*diversion_t == SEMI_COLON) {
+            /* More parameters follow */
+            *diversion_t++ = 0;
+            SKIP_LWS(diversion_t);
+        } else {
+            break;
+        }
+    }
+
+    return TRUE;
+
+}
+
+sipDiversion_t *
+sippmh_parse_diversion (const char *diversion, char *diversionhead)
+{
+    char           *param_ptr;
+    sipDiversion_t *sipdiversion;
+    sipLocation_t  *sipLoc;
+    char           *diversion_t, *start_ptr;
+
+    /*  CC-Diversion = "CC-Diversion"
+     *                              *(diversion-params [comment])
+     *  diversion-params  = diversion-addr | diversion-reason |
+     *                      diversion-counter| diversion-limit
+     *  diversion-addr    = (name-addr | addr-spec)*(";"addr-params)
+     *  diversion-reason  = "reason"= "unknown" | "user-busy" | "no-answer" |
+     *                     "unconditional" | "deflection"| "follow-me" |
+     *                     "out-of-service" | " time-of-day" | "unavailable" |
+     *                     "do-not-disturb"
+     *  diversion-counter = "counter" = 2*DIGIT
+     *  diversion-limit   = "limit" = 2*DIGIT
+     */
+
+    /*
+     * Diversion = "Diversion" ":" 1# (name-addr *( ";" diversion_params ))
+     * diversion-params = diversion-reason | diversion-counter |
+     *                  diversion-limit | diversion-privacy |
+     *         diversion-screen | diversion-extension
+     * diversion-reason = "reason" "="
+     *               ( "unknown" | "user-busy" | "no-answer" |
+     *                 "unavailable" | "unconditional" |
+     *                 "time-of-day" | "do-not-disturb" |
+     *                 "deflection" | "follow-me" |
+     *                 "out-of-service" | "away" |
+     *                 token | quoted-string )
+     * diversion-counter = "counter" "=" 1*2DIGIT
+     * diversion-limit = "limit" "=" 1*2DIGIT
+     * diversion-privacy = "privacy" "=" ( "full" | "name" |
+     *                   "uri" | "off" | token | quoted-string )
+     * diversion-screen = "screen" "=" ( "yes" | "no" | token |
+     *                                    quoted-string )
+     * diversion-extension = token ["=" (token | quoted-string)]
+     */
+
+    sipdiversion = (sipDiversion_t *) cpr_calloc(1, sizeof(sipDiversion_t));
+    if (sipdiversion == NULL) {
+        return NULL;
+    }
+
+    diversion_t = cpr_strdup(diversion);
+
+    if (diversion_t == NULL) {
+        sippmh_free_diversion(sipdiversion);
+        return NULL;
+    }
+
+    start_ptr = diversion_t;
+
+    do {
+
+        param_ptr = NULL;
+        sipLoc = sippmh_parse_nameaddr_or_addrspec(diversion_t, start_ptr, FALSE, FALSE,
+                                                   &param_ptr);
+        if (sipLoc == NULL) {
+            cpr_free(start_ptr);
+            sippmh_free_diversion(sipdiversion);
+            sipdiversion = NULL;
+            break;
+        }
+
+        sipdiversion->locations = sipLoc;
+
+        if (param_ptr == NULL || *param_ptr == 0) {
+            /* No params */
+            break;
+        }
+        diversion_t = param_ptr;
+        if (*diversion_t == SEMI_COLON) {
+            /* Parsing Diversion Headers */
+            *diversion_t++ = 0;
+
+            if ((strncasecmp(diversionhead, SIP_HEADER_DIVERSION,
+                             sizeof(SIP_HEADER_DIVERSION)) == 0) ||
+                (strncasecmp(diversionhead, SIP_HEADER_CC_DIVERSION,
+                             sizeof(SIP_HEADER_CC_DIVERSION)) == 0)) {
+
+                if (sippmh_parse_diversion_params(diversion_t, sipdiversion) == FALSE) {
+
+                    CCSIP_ERR_DEBUG {
+                        buginf("\nsippmh_parse_diversion: syntax error in Diversion header");
+                    }
+                    parse_errno = PARSE_ERR_SYNTAX;
+                    sippmh_free_diversion(sipdiversion);
+                    sipdiversion = NULL;
+                    break;
+                }
+            }
+            /* Must reach here after parsing all parameters  */
+            break; /* break out of original do loop */
+        } else {
+            CCSIP_ERR_DEBUG {
+                buginf("\nsippmh_parse_diversion: syntax error missing "
+                       "semicolon in Diversion header");
+            }
+            parse_errno = PARSE_ERR_SYNTAX;
+            sippmh_free_diversion(sipdiversion);
+            sipdiversion = NULL;
+            break;
+        }
+
+    } while (1);
+    return sipdiversion;
+}
+
+void
+sippmh_free_diversion (sipDiversion_t * ccr)
+{
+    if (ccr) {
+        if (ccr->locations) {
+            sippmh_free_location(ccr->locations);
+        }
+        cpr_free(ccr);
+    }
+}
+
+
+sipContact_t *
+sippmh_parse_contact (const char *input_contact)
+{
+    int j, k;
+    char *contact = NULL, *start_ptr = NULL;
+    char *more_ptr;
+    sipContact_t *sipContact;
+    sipContactParams_t params;
+
+    /*
+     * Contact = ( "Contact" | "m" ) ":"
+     *       ("*" | (1# (( name-addr | addr-spec )
+     *       [ *( ";" contact-params ) ] [ comment ] )))
+     * name-addr      = [ display-name ] "<" addr-spec ">"
+     * addr-spec      = SIP-URL | URI
+     * display-name   = *token | quoted-string
+     *
+     * contact-params = "q"       "=" qvalue
+     *            | "action"  "=" "proxy" | "redirect"
+     *            | "expires" "=" delta-seconds | <"> SIP-date <">
+     *            | extension-attribute
+     *
+     */
+    parse_errno = 0;
+    contact = cpr_strdup(input_contact);
+    if (contact == NULL) {
+        return NULL;
+    }
+
+    sipContact = (sipContact_t *) cpr_calloc(1, sizeof(sipContact_t));
+    if (sipContact == NULL) {
+        cpr_free(contact);
+        return NULL;
+    }
+
+    memset(&params, 0, sizeof(sipContactParams_t));
+
+    start_ptr = contact;
+
+    do {
+        sipLocation_t *sipLoc;
+
+        more_ptr = NULL;
+        sipLoc = sippmh_parse_nameaddr_or_addrspec(contact, start_ptr, FALSE,
+                                                   FALSE, &more_ptr);
+        if (sipLoc == NULL) {
+            if ((more_ptr != NULL) && (*more_ptr == COMMA)) {
+                /* Another location follows */
+                contact = more_ptr;
+                *contact++ = 0;
+                SKIP_LWS(contact);
+                if (sipContact->num_locations == SIP_MAX_LOCATIONS) {
+                    CCSIP_ERR_DEBUG {
+                        buginf("\nsippmh_parse_contact: Too many location headers"
+                               " in Contact header");
+                    }
+                    /* go with what we have */
+                    break;
+                }
+                continue;
+            } else {
+                if (sipContact->num_locations == 0) {
+                    // Free start_ptr only if we fail the first location
+                    // For the others, it will be freed when sipContact is
+                    // freed
+                    cpr_free(start_ptr);
+                }
+                sippmh_free_contact(sipContact);
+                sipContact = NULL;
+                break;
+            }
+        }
+        if (sipContact->num_locations) {
+            sipLoc->loc_start = NULL;
+        }
+        if (more_ptr == NULL || *more_ptr == 0) {
+            /* No params, means no qval and assume lowest priority */
+            sipContact->locations[sipContact->num_locations++] = sipLoc;
+            break;
+        }
+        contact = more_ptr;
+        /*
+         * At this point, either contact is pointing to
+         * 1) ';' - Contact params
+         * 2) ',' - Beginning of next location
+         * 3) End of string
+         * 4) Any other character is syntax error
+         */
+
+        j = sipContact->num_locations; /* By default insert at the end */
+        params.qval = "";
+
+        if (*contact == SEMI_COLON) {
+            /* Now parse the Contact params */
+            *contact++ = 0;
+            params.flags = 0;
+            contact = sippmh_parse_contact_params(contact, &params);
+            /*
+             * Now, do an insertion sort on this list (qval is the key).
+             * Higher values of q come first.
+             */
+            for (j = 0; j < sipContact->num_locations; ++j) {
+                if (strcmp(params.qval, sipContact->params[j].qval) > 0) {
+                    for (k = sipContact->num_locations; k > j; --k) {
+                        sipContact->locations[k] = sipContact->locations[k - 1];
+                        sipContact->params[k] = sipContact->params[k - 1];
+                    }
+                    break;
+                }
+            }
+            /* At this point j points to the entry to use */
+        }
+
+        sipContact->params[j] = params;
+        sipContact->locations[j] = sipLoc;
+        sipContact->num_locations++;
+
+        if (contact && *contact == COMMA) {
+            /* Another location follows */
+            *contact++ = 0;
+            SKIP_LWS(contact);
+            if (sipContact->num_locations == SIP_MAX_LOCATIONS) {
+                CCSIP_ERR_DEBUG {
+                    buginf("\nsippmh_parse_contact: Too many location headers"
+                           " in Contact header");
+                }
+                /* go with what we have */
+                break;
+            }
+        } else {
+            if (contact && *contact) {
+                parse_errno = PARSE_ERR_SYNTAX;
+                CCSIP_DEBUG_ERROR(parse_errors[parse_errno], "sippmh_parse_contact",
+                           contact);
+                sippmh_free_contact(sipContact);
+                sipContact = NULL;
+            }
+            break;
+        }
+    } while (1);
+
+    if (sipContact) {
+        sipContact->new_flag = TRUE;
+    }
+    return sipContact;
+}
+
+
+void
+sippmh_free_contact (sipContact_t *sc)
+{
+    int i;
+
+    if (sc) {
+        for (i = 0; i < sc->num_locations; ++i) {
+            if (sc->locations[i]->loc_start) {
+                /* Free the entire header value that we duplicated */
+                cpr_free(sc->locations[i]->loc_start);
+            }
+            /* Free the genUrl_t struct in the sipLocation_t */
+            sippmh_genurl_free(sc->locations[i]->genUrl);
+            /* Free the sipLocation_t struct */
+            cpr_free(sc->locations[i]);
+        }
+
+        /* Free the sipContact_t struct */
+        cpr_free(sc);
+    }
+}
+
+static boolean
+parse_via_params (char *str, sipVia_t *sipVia)
+{
+    char **ptr;
+
+    /* str is currently pointing to beginning of parameters */
+
+    while (1) {
+        /* To be friendly, skip leading spaces */
+        SKIP_LWS(str);
+        if (strncasecmp(str, VIA_HIDDEN, 6) == 0) {
+            str += 6;
+            sipVia->flags |= VIA_IS_HIDDEN;
+            ptr = NULL;
+        } else if (strncasecmp(str, VIA_TTL, 3) == 0) {
+            str += 3;
+            ptr = &(sipVia->ttl);
+        } else if (strncasecmp(str, VIA_MADDR, 5) == 0) {
+            str += 5;
+            ptr = &(sipVia->maddr);
+        } else if (strncasecmp(str, VIA_RECEIVED, 8) == 0) {
+            str += 8;
+            ptr = &(sipVia->recd_host);
+        } else if (strncasecmp(str, VIA_BRANCH, 6) == 0) {
+            str += 6;
+            ptr = &(sipVia->branch_param);
+        } else {
+            ptr = NULL;
+        }
+        if (ptr) {
+            if (*ptr) {
+                /* ptr already points to something
+                 * -- duplicate param found */
+                return FALSE;
+            }
+            SKIP_LWS(str); /* Skip spaces till equal sign */
+            if (*str == EQUAL_SIGN) {
+                str++;
+                SKIP_LWS(str);
+                *ptr = str;
+            }
+        }
+        str = strpbrk(str, ";,");
+        if (str == NULL) {
+            if (ptr && *ptr) {
+                trim_right(*ptr);
+            }
+            return TRUE;
+        }
+        if (*str == COMMA) {
+            *str++ = 0;
+            SKIP_LWS(str);
+            sipVia->more_via = str;
+            if (ptr && *ptr) {
+                trim_right(*ptr);
+            }
+            return TRUE;
+        }
+        *str++ = 0;             /* Zero ';' and advance pointer */
+        if (ptr && *ptr) {
+            trim_right(*ptr);
+        }
+    }
+}
+
+sipVia_t *
+sippmh_parse_via (const char *input_via)
+{
+    static const char fname[] = "sippmh_parse_via";
+    char     *via;
+    char     *separator;
+    sipVia_t *sipVia;
+    char     *endptr;
+    char     *port_num_str;
+    uint16_t  port_num;
+
+    /*
+     * Via              = ( "Via" | "v") ":" 1#( sent-protocol sent-by
+     *               *( ";" via-params ) [ comment ] )
+     * via-params       = via-hidden | via-ttl | via-maddr
+     *             | via-received | via-branch
+     * via-hidden       = "hidden"
+     * via-ttl          = "ttl" "=" ttl
+     * via-maddr        = "maddr" "=" maddr
+     * via-received     = "received" "=" host
+     * via-branch       = "branch" "=" token
+     * sent-protocol    = protocol-name "/" protocol-version "/" transport
+     * protocol-name    = "SIP" | token
+     * protocol-version = token
+     * transport        = "UDP" | "TCP" | "TLS" | token
+     * sent-by          = ( host [ ":" port ] ) | ( concealed-host )
+     * concealed-host   = token
+     * ttl              = 1*3DIGIT     ; 0 to 255
+     */
+
+    parse_errno = PARSE_ERR_SYNTAX;
+    if (input_via == NULL) {
+        return NULL;
+    }
+    sipVia = NULL;
+    via = NULL;
+    SKIP_LWS(input_via);
+    do {
+        if (strncasecmp(input_via, "SIP", 3)) {
+            /* Unknown protocol-name */
+            break;
+        }
+        input_via += 3;
+
+        SKIP_LWS(input_via);
+        if (*input_via != '/') {
+            break;
+        }
+        input_via++; /* Skip '/' */
+
+        SKIP_LWS(input_via);
+        /* input_via should now be pointing to version */
+
+        /* Duplicate the string and work with it */
+        via = cpr_strdup(input_via);
+        if (via == NULL) {
+            parse_errno = 0;
+            return NULL;
+        }
+        /* Allocate sipVia_t struct */
+        sipVia = (sipVia_t *) cpr_calloc(1, sizeof(sipVia_t));
+        if (sipVia == NULL) {
+            parse_errno = 0;
+            cpr_free(via);
+            return NULL;
+        }
+
+        /* version also points to the start of memory allocated */
+        sipVia->version = via;
+        if (strncmp(via, "2.0", 3)) {
+            break;
+        }
+        via += 3; /* Go past version */
+
+        SKIP_LWS(via);
+        if (*via != '/') {
+            break;
+        }
+        *via++ = 0; /* Terminate version string  and go past '/' */
+
+        SKIP_LWS(via); /* Point to transport */
+        sipVia->transport = via;
+        if (strncasecmp(via, "UDP", 3) && strncasecmp(via, "TCP", 3) &&
+            strncasecmp(via, "TLS", 3)) {
+            /* Invalid transport */
+            break;
+        }
+        via += 3;   /* Go past the transport string */
+        *via++ = 0; /* Terminate transport string and advance pointer */
+
+
+        /* Skip blanks */
+        SKIP_LWS(via);
+        if (*via == 0) {
+            parse_errno = PARSE_ERR_UNEXPECTED_EOS;
+            CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname);
+            break;
+        }
+
+        parse_errno = 0;
+
+    } while (0);
+
+    if (parse_errno) {
+        if (parse_errno == PARSE_ERR_SYNTAX) {
+            CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname, via ? via : input_via);
+        }
+        sippmh_free_via(sipVia);
+        return NULL;
+    }
+
+    port_num_str = SIP_WELL_KNOWN_PORT_STR;
+    sipVia->host = via;
+    /*
+     * Either we can have a ':' followed by port
+     * or start of parameters signalled by ';'
+     * or end of this Via indicated by ','
+     */
+    if (*via == '[') {
+        /* IPv6 address */
+        sipVia->host = via;
+        sipVia->is_ipv6 = TRUE;
+        separator = strpbrk(via, "]");
+
+        if (separator && *separator == ']') {
+            separator++;
+        }
+    } else  {
+
+        separator = via;
+    }
+
+    if (separator) {
+        separator = strpbrk(separator, ":;,");
+    }
+    if (separator) {
+        if (*separator == COLON) {
+            /* Port number follows */
+            *separator++ = 0;
+            port_num_str = separator;
+            separator = strpbrk(separator, ";,");
+        }
+        if (separator) {
+            if (*separator == SEMI_COLON) {
+                *separator++ = 0;
+                /* Parameters follow */
+                if (parse_via_params(separator, sipVia) == FALSE) {
+                    CCSIP_ERR_DEBUG
+                        buginf("%s: Duplicate params in Via\n", fname);
+                    sippmh_free_via(sipVia);
+                    return NULL;
+                }
+            } else {
+                *separator++ = 0;
+                SKIP_LWS(separator);
+                sipVia->more_via = separator;
+            }
+        }
+    }
+
+    /* Validate the host portion */
+    if ((sipSPI_validate_ip_addr_name(sipVia->host) == FALSE)) {
+        CCSIP_ERR_DEBUG buginf("\n%s: Invalid host in Via", fname);
+        sippmh_free_via(sipVia);
+        return NULL;
+    }
+
+    /* Fix host portion */
+    trim_right(sipVia->host);
+    /* Fix IPv6 host */
+    trim_ipv6_host(sipVia->host);
+
+    /* validate port portion */
+    port_num = (uint16_t) strtol(port_num_str, &endptr, 10);
+
+    /*
+     * There may be trailing white space in the port portion. So, ignore them.
+     */
+    SKIP_LWS(endptr);
+    if (*endptr || port_num == 0) {
+        sippmh_free_via(sipVia);
+        CCSIP_ERR_DEBUG buginf("\n%s: Invalid port number in Via", fname);
+        return NULL;
+    }
+    sipVia->remote_port = port_num;
+    sipVia->recd_host = sipVia->host;
+    return sipVia;
+}
+
+void
+sippmh_free_via (sipVia_t * sipVia)
+{
+    if (sipVia) {
+        cpr_free(sipVia->version);
+        cpr_free(sipVia);
+    }
+}
+
+
+sipCseq_t *
+sippmh_parse_cseq (const char *cseq)
+{
+    char *lasts = NULL;
+    /*
+     * CSeq  =  "CSeq" ":" 1*DIGIT Method
+     */
+    sipCseq_t *sipCseq = (sipCseq_t *) cpr_calloc(1, sizeof(sipCseq_t));
+
+    if (!sipCseq) {
+        return (NULL);
+    }
+
+/* if ',' is present then more than 1 cseq are present */
+    if (strchr(cseq, ',')) {
+        cpr_free(sipCseq);
+        return (NULL);
+    }
+
+    if (cseq) {
+        char *mycseq = cpr_strdup(cseq);
+
+        sipCseq->method = sipMethodInvalid;
+
+        if (mycseq) {
+            char *this_token = PL_strtok_r(mycseq, " ", &lasts);
+
+            if (this_token) {
+                sipCseq->number = strtoul(this_token, NULL, 10);
+
+                /* make sure the CSeq value is not > 2^^31 */
+                if (sipCseq->number >= TWO_POWER_31) {
+                    cpr_free(sipCseq);
+                    cpr_free(mycseq);
+                    return (NULL);
+                }
+
+                this_token = PL_strtok_r(NULL, " ", &lasts);
+                if (this_token) {
+                    sipCseq->method = sippmh_get_method_code(this_token);
+                }
+
+            }
+            if (!this_token) {
+                cpr_free(sipCseq);
+                cpr_free(mycseq);
+                return (NULL);
+            }
+            cpr_free(mycseq);
+        } else {
+            cpr_free(sipCseq);
+            return NULL;
+        }
+    }
+
+    return (sipCseq);
+}
+
+sipRet_t
+sippmh_add_cseq (sipMessage_t *msg, const char *method, uint32_t seq_no)
+{
+    sipRet_t retval = STATUS_FAILURE;
+
+    if (msg && method) {
+        char cseq[32];
+
+        sprintf(&cseq[0], "%lu %s", (unsigned long) seq_no, method);
+        retval = sippmh_add_text_header(msg, SIP_HEADER_CSEQ,
+                                        (const char *)&cseq[0]);
+    }
+
+    return (retval);
+}
+
+/*
+ * The Valid SIP URL MUST contain host field.
+ * This is not true for TEL URLs.  Only user is mandatory.
+ * This check is sufficient for "From" & "To" headers
+ * from a GW standpoint. The ReqLine MUST have a "user"
+ * in the URL as the GW supports multiple users.
+ */
+boolean
+sippmh_valid_url (genUrl_t *genUrl)
+{
+    boolean retval = FALSE;
+
+    if (!genUrl) {
+        return retval;
+    }
+    if (genUrl->schema == URL_TYPE_SIP) {
+        if (genUrl->u.sipUrl->host && genUrl->u.sipUrl->host[0])
+            retval = TRUE;
+    } else if (genUrl->schema == URL_TYPE_TEL) {
+        if (genUrl->u.telUrl->user)
+            retval = TRUE;
+    }
+
+    return (retval);
+}
+
+/*
+ * The Input string is the comma separated cached Record-Route
+ * header string. This function duplicates the input string
+ * and generates a set of pointers which are pointing to a
+ * name-addr format string. These pointers are pointing within
+ * the duplicated cached header string.
+ *
+ *                  Loc 0       Loc 1       Loc 2
+ * Loc_start -> name-addr1, name-addr2, name-addr3
+ *
+ * Loc_start points to the duplicated input string and needs
+ * to be freed.
+ * Loc_start is preserved so that the string
+ * can be freed, when required. Thus this pointer needs to be
+ * stored only once.
+ */
+sipRecordRoute_t *
+sippmh_parse_record_route (const char *input_record_route)
+{
+    char *record_route = NULL, *start_ptr = NULL;
+    char *more_ptr;
+    sipRecordRoute_t *sipRecordRoute;
+    char *rr_other_param = NULL;
+
+    /*
+     * Record-Route = "Record-Route" ":" (name-addr *(";" rr-param))
+     * name-addr      = [ display-name ] "<" addr-spec ">"
+     * addr-spec      = SIP-URL | URI
+     * display-name   = *token | quoted-string
+     * rr-param       = generic-param
+     * generic-param  = token [  =  ( token | host | quoted-string ) ]
+     */
+
+    record_route = cpr_strdup(input_record_route);
+    if (record_route == NULL) {
+        return NULL;
+    }
+
+    sipRecordRoute = (sipRecordRoute_t *)
+        cpr_calloc(1, sizeof(sipRecordRoute_t));
+
+    if (sipRecordRoute == NULL) {
+        cpr_free(record_route);
+        return NULL;
+    }
+
+    start_ptr = record_route;
+
+    do {
+        sipLocation_t *sipLoc;
+
+        more_ptr = NULL;
+        parse_errno = 0;
+        sipLoc = sippmh_parse_nameaddr_or_addrspec(record_route, start_ptr,
+                                                   FALSE, TRUE, &more_ptr);
+        if (sipLoc == NULL) {
+            if (sipRecordRoute->locations == 0) {
+                // Free record_route only if we fail the first location
+                // For the others, it will be freed when sipRecordRoute is
+                // freed
+                cpr_free(record_route);
+            }
+            sippmh_free_record_route(sipRecordRoute);
+            sipRecordRoute = NULL;
+            break;
+        }
+
+        /* Loc_start points to the duplicated input  string &
+         * is stored in the zeroth location of location[].
+         * This value needs to be freed only once
+         */
+        if (sipRecordRoute->num_locations) {
+            sipLoc->loc_start = NULL;
+        }
+
+        sipRecordRoute->locations[sipRecordRoute->num_locations++] = sipLoc;
+
+        if (more_ptr == NULL || *more_ptr == 0) {
+            /* No params, means no qval and assume lowest priority */
+            break;
+        }
+
+        record_route = more_ptr;
+
+        /*
+         * At this point, either record_route is pointing to
+         * 1) ',' - Beginning of next location
+         * 3) End of string
+         * 4) Any other character is syntax error
+         */
+
+        /*
+         * This following while loop eats up all the rr_params.
+         * Note that these 'other_params' are not the same as the ones handled
+         * by parseURLParams. That routines parses other params INSIDE the < > brackets.
+         * The following code would handle unknown params outside the brackets.
+         */
+        while (record_route && *record_route == SEMI_COLON) {
+            record_route++;
+            record_route = parse_other_param(record_route, &rr_other_param);
+            /*
+             * Record route extensions are not supported. Throw away
+             * any that were just parsed.
+             */
+            if (rr_other_param != NULL) {
+                cpr_free(rr_other_param);
+            }
+        }
+
+
+        if (record_route && *record_route == COMMA) {
+            /* Another location follows */
+            *record_route++ = 0;
+            SKIP_LWS(record_route);
+            if (sipRecordRoute->num_locations == SIP_MAX_LOCATIONS) {
+                sippmh_free_record_route(sipRecordRoute);
+                sipRecordRoute = NULL;
+                CCSIP_ERR_DEBUG {
+                    buginf("\nsippmh_parse_record_route: Too many location "
+                           "headers in Record-Route header");
+                }
+                break;
+            }
+        } else {
+            /* Flag error if not "end of header" &
+             * any other character other than COMMA
+             */
+            if (record_route && *record_route) {
+                parse_errno = PARSE_ERR_SYNTAX;
+                CCSIP_DEBUG_ERROR(parse_errors[parse_errno],
+                           "sippmh_parse_record_route", record_route);
+                sippmh_free_record_route(sipRecordRoute);
+                sipRecordRoute = NULL;
+            }
+            break;
+        }
+    } while (1);
+
+    if (sipRecordRoute) {
+        sipRecordRoute->new_flag = TRUE;
+    }
+    return sipRecordRoute;
+}
+
+/*
+ *  This function is a placeholder for future expansion.
+ *  If deep copy of the record route is needed from one ccb to another
+ *  this function can be used.
+ */
+sipRecordRoute_t *
+sippmh_copy_record_route (sipRecordRoute_t *rr)
+{
+    //TBD
+    return NULL;
+}
+
+void
+sippmh_free_record_route (sipRecordRoute_t *rr)
+{
+    int i;
+
+    if (rr) {
+        for (i = 0; i < rr->num_locations; ++i) {
+            if (rr->locations[i]->loc_start) {
+                /* Free the entire header value that we duplicated */
+                cpr_free(rr->locations[i]->loc_start);
+            }
+            /* Free the genUrl_t struct in the sipLocation_t */
+            sippmh_genurl_free(rr->locations[i]->genUrl);
+            /* Free the sipLocation_t in Record-Route struct */
+            cpr_free(rr->locations[i]);
+        }
+        /* Free the sipContact_t struct */
+        cpr_free(rr);
+    }
+}
+
+cc_content_disposition_t *
+sippmh_parse_content_disposition (const char *input_content_disp)
+{
+    char *content_disp = NULL;
+    char *content_disp_start = NULL;
+    cc_content_disposition_t *sipContentDisp = NULL;
+    char *separator = NULL;
+    char *more_ptr = NULL;
+
+    if (input_content_disp == NULL) {
+        return NULL;
+    }
+
+    content_disp_start = content_disp = cpr_strdup(input_content_disp);
+    if (content_disp == NULL) {
+        return NULL;
+    }
+
+    /*
+     * Content-Disposition = "Content-Disposition" ":"
+     *                        disposition-type*(";" disposition-param)
+     * disposition-type    = "render" "session" "Icon" "alert" "precondition"
+     *                        disp-extension-token
+     * disposition-param   = "handling" "="
+     *                        ("optional"|"required" other-handling)
+     *                        generic-param
+     * other-handling      = token
+     * disp-extension-token = token
+     */
+
+    sipContentDisp = (cc_content_disposition_t *)
+        cpr_calloc(1, sizeof(cc_content_disposition_t));
+    if (sipContentDisp == NULL) {
+        cpr_free(content_disp);
+        return sipContentDisp;
+    }
+
+    /* Set the field to protocol define defaults
+     * - Session & required handling
+     */
+    sipContentDisp->disposition = cc_disposition_session;
+    sipContentDisp->required_handling = TRUE;
+
+    /* first token should be disposition-type - so look for
+     * a token delimiter either a space or semicolon
+     */
+    SKIP_LWS(content_disp);
+    separator = strpbrk(content_disp, " ;");
+    if (separator) {
+        if (*separator == ';') {
+            *separator = 0;
+            more_ptr = separator + 1;
+        } else {
+            *separator = 0;
+            more_ptr = NULL;
+        }
+    } else {
+        more_ptr = NULL;
+    }
+
+    /* Parse disposition-type */
+    if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_SESSION, 7) == 0) {
+        sipContentDisp->disposition = cc_disposition_session;
+    } else if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_PRECONDITION, 12) == 0) {
+        sipContentDisp->disposition = cc_disposition_precondition;
+    } else if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_ICON, 4) == 0) {
+        sipContentDisp->disposition = cc_dispostion_icon;
+    } else if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_ALERT, 5) == 0) {
+        sipContentDisp->disposition = cc_disposition_alert;
+    } else if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_RENDER, 6) == 0) {
+        sipContentDisp->disposition = cc_disposition_render;
+    } else {
+        sipContentDisp->disposition = cc_disposition_unknown;
+    }
+
+    if (more_ptr) {
+        /* parse disposition-param */
+        SKIP_LWS(more_ptr);
+        if (strncasecmp(more_ptr, "handling", 8) == 0) {
+            more_ptr += 8;
+            SKIP_LWS(more_ptr);
+            if (*more_ptr == '=') {
+                more_ptr++;
+                SKIP_LWS(more_ptr);
+                if (strncasecmp(more_ptr, "optional", 8) == 0) {
+                    sipContentDisp->required_handling = FALSE;
+                } else if (strncasecmp(more_ptr, "required", 8) == 0) {
+                    sipContentDisp->required_handling = TRUE;
+                }
+            }
+        } else {
+            /* Keyword "handling" is not found
+             * default values are already populated
+             */
+        }
+    }
+
+    cpr_free(content_disp_start);
+    return (sipContentDisp);
+}
+
+
+/***************************************************************
+ *
+ * Parses headers in Refer-To body in a char array
+ *
+ **************************************************************/
+static uint16_t
+sippmh_parse_referto_headers (char *refto_line, char **header_arr)
+{
+    uint16_t headNum = 0;
+    char *tmpHead = NULL;
+    char *lasts = NULL;
+
+    if (refto_line == NULL) {
+        return 0;
+    }
+
+    tmpHead = PL_strtok_r(refto_line, "?&", &lasts);
+    while (tmpHead != NULL) {
+        header_arr[headNum++] = tmpHead;
+        tmpHead = PL_strtok_r(NULL, "?&", &lasts);
+    }
+    return headNum;
+}
+
+
+/*******************************************************************
+ *
+ * Parser routine for headers other than sip url. Parses Replaces
+ * header, copies Accept-Contact and Proxy-Authorization headers
+ * and skips other headers.
+ *
+ * Input parameters : Array containing headers in Refer-To body,
+ *                    count of number of headers in Refer-To body
+ *                    & pointer to Refer-To structure for parsed
+ *                    values to be returned to calling function
+ *
+ * Returns : Zero if success, non-zero otherwise
+ *
+ *******************************************************************/
+static int
+sippmh_parse_other_headers (char **headerArr, uint16_t tokCount,
+                            sipReferTo_t *refer_to)
+{
+    uint16_t count = 0;
+    char *dup_header, *mhead;
+
+    dup_header = mhead = NULL;
+
+    for (count = 1; count < tokCount; count++) {
+        dup_header = cpr_strdup(headerArr[count]);
+        if (dup_header == NULL) {
+            return (PARSE_ERR_NO_MEMORY);
+        }
+        mhead = strchr(dup_header, '=');
+        if (mhead == NULL) {
+            cpr_free(dup_header);
+            return (PARSE_ERR_SYNTAX);
+        } else {
+            *mhead = '\0';
+        }
+        if (cpr_strcasecmp(dup_header, SIP_HEADER_REPLACES) == 0) {
+
+            char *unescRepl_str = NULL;
+
+            /*
+             * copy the Replaces header so that it can be
+             * echoed back in the triggered INVITE. Also
+             * unescape any escpaed characters.
+             */
+            headerArr[count] = strstr(headerArr[count], "=");
+            if (headerArr[count] == NULL) {
+                cpr_free(dup_header);
+                return (PARSE_ERR_SYNTAX);
+            }
+            *(headerArr[count])++ = 0;
+            SKIP_LWS(headerArr[count]);
+
+            /* if header body is NULL, return syntax error */
+            if (*headerArr[count] == '\0') {
+                cpr_free(dup_header);
+                return (PARSE_ERR_SYNTAX);
+            }
+
+            unescRepl_str = (char *) cpr_malloc(strlen(headerArr[count]) + 1);
+            if (unescRepl_str == NULL) {
+                cpr_free(dup_header);
+                return (PARSE_ERR_NO_MEMORY);
+            }
+
+            sippmh_convertEscCharToChar((const char *) headerArr[count],
+                                        (uint8_t) strlen(headerArr[count]), unescRepl_str);
+
+            refer_to->sip_replaces_hdr = unescRepl_str;
+
+        } else if (cpr_strcasecmp(dup_header, SIP_HEADER_ACCEPT_CONTACT) == 0 ||
+                   cpr_strcasecmp(dup_header, SIP_C_HEADER_ACCEPT_CONTACT) == 0) {
+            char *unescAccCont = NULL;
+
+            /*
+             * copy unescaped version of this header so
+             * that it can be echoed back in triggered INVITE
+             */
+            headerArr[count] = strstr(headerArr[count], "=");
+            if (headerArr[count] == NULL) {
+                cpr_free(dup_header);
+                return (PARSE_ERR_SYNTAX);
+            }
+            *(headerArr[count])++ = 0;
+            SKIP_LWS(headerArr[count]);
+
+            /* if header body is NULL, return syntax error */
+            if (*headerArr[count] == '\0') {
+                cpr_free(dup_header);
+                return (PARSE_ERR_SYNTAX);
+            }
+
+            unescAccCont = (char *) cpr_malloc(strlen(headerArr[count]) + 1);
+
+            if (unescAccCont == NULL) {
+                cpr_free(dup_header);
+                return (PARSE_ERR_NO_MEMORY);
+            }
+
+            sippmh_convertEscCharToChar((const char *) headerArr[count],
+                                        (uint8_t) strlen(headerArr[count]),
+                                        unescAccCont);
+
+            refer_to->sip_acc_cont = unescAccCont;
+
+        } else if (cpr_strcasecmp(dup_header, SIP_HEADER_PROXY_AUTH) == 0) {
+
+            char *unescPrAuth = NULL;
+
+            /*
+             * copy this header to be echoed back in triggered INVITE
+             * unescape any escaped chars before storing
+             */
+
+            headerArr[count] = strstr(headerArr[count], "=");
+            if (headerArr[count] == NULL) {
+                cpr_free(dup_header);
+                return (PARSE_ERR_SYNTAX);
+            }
+            *(headerArr[count])++ = 0;
+            SKIP_LWS(headerArr[count]);
+
+            /* if header body is NULL, return syntax error */
+            if (*headerArr[count] == '\0') {
+                cpr_free(dup_header);
+                return (PARSE_ERR_SYNTAX);
+            }
+
+            unescPrAuth = (char *) cpr_malloc(strlen(headerArr[count]) + 1);
+            if (unescPrAuth == NULL) {
+                cpr_free(dup_header);
+                return (PARSE_ERR_NO_MEMORY);
+            }
+
+            sippmh_convertEscCharToChar((const char *) headerArr[count],
+                                        (uint8_t) strlen(headerArr[count]),
+                                        unescPrAuth);
+
+            refer_to->sip_proxy_auth = unescPrAuth;
+        }
+        cpr_free(dup_header);
+    }
+
+    return 0;  /* SUCCESS */
+}
+
+/**************************************************************
+ * Parser routine for Refer-To header. Refer-To may contain
+ * other headers like:
+ * a) Replaces
+ * b) Accept-Contact
+ * c) Proxy-Authorization
+ *  will skip headers, we do not understand; parse Replaces;
+ *  and copy Accept-Contact and Proxy-Authorization headers
+ *  to be echoed back in the triggered INVITE.
+ *
+ * Input parameters : Refer-To string to be parsed.
+ * Returns : Pointer to Refer-To structure with parsed values
+ *           if successful, NULL otherwise.
+ *
+ **************************************************************/
+sipReferTo_t *
+sippmh_parse_refer_to (char *input_refer_to)
+{
+    char *refer_to_headers[MAX_REFER_TO_HEADER_CONTENTS];
+    char *left_bracket, *right_bracket, *msg_body, *refer_to_val;
+    sipReferTo_t *sipReferTo = NULL;
+    genUrl_t *refUrl = NULL;
+    uint16_t tokens = 0;
+    int ref_header_count = 0;
+
+    left_bracket = right_bracket = msg_body = refer_to_val = NULL;
+
+    if (input_refer_to == NULL) {
+        return NULL;
+    }
+
+    /*
+     * Refer-To = ("Refer-To" | "r") ":" URL
+     */
+
+    /* make a copy of refer-to to work with */
+    refer_to_val = cpr_strdup(input_refer_to);
+    if (refer_to_val == NULL) {
+        return NULL;
+    }
+
+    sipReferTo = (sipReferTo_t *) cpr_calloc(1, sizeof(sipReferTo_t));
+    if (sipReferTo == NULL) {
+        cpr_free(refer_to_val);
+        return NULL;
+    }
+    sipReferTo->ref_to_start = refer_to_val;
+
+    /* Initialize */
+    for (ref_header_count = 0;
+         ref_header_count < MAX_REFER_TO_HEADER_CONTENTS;
+         ref_header_count++) {
+        refer_to_headers[ref_header_count] = NULL;
+    }
+
+    /* first get rid of opening and closing braces, if there */
+    left_bracket = strpbrk(refer_to_val, ",<");
+    if (left_bracket) {
+
+        /* This is a name-addr form */
+        left_bracket++;
+        right_bracket = strchr(left_bracket, '>');
+        if (right_bracket) {
+            *right_bracket++ = 0;
+        }
+        msg_body = left_bracket;
+    } else {
+
+        /* This is addr-spec format */
+        msg_body = refer_to_val;
+        SKIP_LWS(msg_body);
+    }
+
+    /* parse headers separated by ? or & in an array of char pointers */
+    tokens = sippmh_parse_referto_headers(msg_body, refer_to_headers);
+
+    if (tokens == 0) {
+        CCSIP_ERR_DEBUG {
+            buginf("\nEmpty Refer-To header\n");
+        }
+        sippmh_free_refer_to(sipReferTo);
+        return NULL;
+    }
+
+    /* First parse url portion, this should be the first element
+     * of the headers array
+     */
+    refUrl = sippmh_parse_url(refer_to_headers[0], FALSE);
+    if (refUrl == NULL) {
+        sippmh_free_refer_to(sipReferTo);
+        return NULL;
+    }
+    sipReferTo->targetUrl = refUrl;
+
+    /* next parse rest of the headers, skip ones we don't understand */
+    parse_errno = sippmh_parse_other_headers(refer_to_headers, tokens,
+                                             sipReferTo);
+
+    if (parse_errno != 0) {
+        CCSIP_ERR_DEBUG {
+            buginf("\nError while parsing other Refer-To header\n");
+        }
+        sippmh_free_refer_to(sipReferTo);
+        return NULL;
+    }
+
+    return (sipReferTo);
+}
+
+
+
+
+
+/*****************************************************************
+ *
+ * Parser routine for Replaces header. Replaces header has
+ * call-id, to and from tags and signature scheme as parameters.
+ * The parser parses and stores call-id and tags in the Replaces
+ * structure and copies the signature scheme to be echoed back
+ * in the triggered INVITE for attended transfer.
+ *
+ * Input parameters : Replaces string to be parsed,
+ *                    boolean flag indicating whether the input
+ *                    string shall be duplicated or not.
+ *
+ * Returns : pointer to Replaces structure with parsed values
+ *           if successful, NULL otherwise.
+ *
+ *****************************************************************/
+sipReplaces_t *
+sippmh_parse_replaces (char *input_repl, boolean dup_flag)
+{
+
+    char *repl_str, *this_tok, *tagVal, *sign, *callid;
+    sipReplaces_t *repcs = NULL;
+    char *lasts = NULL;
+
+    repl_str = this_tok = tagVal = sign = NULL;
+
+    if (input_repl == NULL) {
+        return NULL;
+    }
+
+
+    /*
+     *  Replaces  = "Replaces" ":" 1#replaces-values
+     *  replaces-values  = callid *( ";" replaces-param )
+     *  callid  = token [ "@" token ]
+     *  replaces-param  = to-tag | from-tag | rep-signature
+     *                    | extension-param
+     *  to-tag  =  "to-tag=" UUID
+     *  from-tag  = "from-tag=" UUID
+     *  rep-signature = signature-scheme *( ";" sig-scheme-params)
+     *  signature-scheme = "scheme" "=" token
+     *  sig-scheme-params = token "=" ( token | quoted string )
+     */
+
+    repcs = (sipReplaces_t *) cpr_calloc(1, sizeof(sipReplaces_t));
+    if (repcs == NULL) {
+        return NULL;
+    }
+
+    if (dup_flag) {
+        repl_str = cpr_strdup(input_repl);
+        if (!repl_str) {
+            cpr_free(repcs);
+            return NULL;
+        }
+        repcs->str_start = repl_str;
+    } else {
+        repl_str = input_repl;
+    }
+
+
+    /* if Replaces string has signature scheme parameter,
+     * make a copy of the input string for parsing entire signature
+     * parameter
+     */
+
+    if ((sign = strstr(repl_str, SIGNATURE_SCHEME)) != NULL) {
+
+        /*
+         * if to or from tag follows signature params, remove
+         * them from this string
+         */
+        char *sign_str, *tag_str;
+
+        sign_str = tag_str = NULL;
+
+        sign_str = cpr_strdup(sign);
+        if (sign_str == NULL) {
+            sippmh_free_replaces(repcs);
+            return NULL;
+        }
+        if ((tag_str = strstr(sign_str, ";to-tag")) ||
+            (tag_str = strstr(sign_str, ";from-tag"))) {
+            *tag_str = '\0';
+        } else {
+            /* terminate input string at signature param */
+            *sign = '\0';
+        }
+        (repcs->signature_scheme) = sign_str;
+    }
+
+
+    this_tok = PL_strtok_r(input_repl, ";", &lasts);
+    while (this_tok != NULL) {
+        if (strncasecmp(this_tok, TO_TAG, 6) == 0) {
+            if (repcs->toTag != NULL) {
+                sippmh_free_replaces(repcs);
+                return NULL; /* ERROR - more than 1 TO tag */
+            } else {
+                tagVal = strchr(this_tok, '=');
+                if (tagVal) {
+                    tagVal++;
+                    SKIP_LWS(tagVal);
+                    repcs->toTag = tagVal;
+                } else {
+                    sippmh_free_replaces(repcs);
+                    return NULL;    /* ERROR */
+                }
+            }
+        } else if (strncasecmp(this_tok, FROM_TAG, 8) == 0) {
+            if (repcs->fromTag != NULL) {
+                sippmh_free_replaces(repcs);
+                return NULL; /* ERROR - more than 1 FROM tag */
+            } else {
+                tagVal = strchr(this_tok, '=');
+                if (tagVal) {
+                    tagVal++;
+                    SKIP_LWS(tagVal);
+                    repcs->fromTag = tagVal;
+                } else {
+                    sippmh_free_replaces(repcs);
+                    return NULL; /* ERROR */
+                }
+            }
+        } else if (strncasecmp(this_tok, SIP_HEADER_REPLACES,
+                               sizeof(SIP_HEADER_REPLACES) - 1) == 0) {
+            /* only other allowable field should be callid */
+            callid = strchr(this_tok, '=');
+            if (callid) {
+                char *sp_token;
+
+                callid++;
+                SKIP_LWS(callid);
+                repcs->callid = callid;
+                /* remove trailing spaces */
+                sp_token = strchr(repcs->callid, ' ');
+                if (sp_token == NULL) {
+                    sp_token = strchr(repcs->callid, '\t');
+                }
+                if (sp_token) {
+                    *sp_token = 0;
+                }
+            } else {
+                sippmh_free_replaces(repcs);
+                return NULL; /* ERROR */
+            }
+        } else {
+            sippmh_free_replaces(repcs);
+            return NULL;
+        }
+        this_tok = PL_strtok_r(NULL, ";", &lasts);
+    }
+    /* check for errors */
+    if ((repcs->callid == NULL) ||
+        (repcs->toTag == NULL) ||
+        (repcs->fromTag == NULL)) {
+        sippmh_free_replaces(repcs);
+        return NULL;
+    }
+
+    return repcs; /* SUCCESS */
+}
+
+
+/*
+ * Converts a hex character (0-9, a-f, or A-F) into its integer
+ * equivalent value.
+ *    ex.)  '9' --> 9
+ *          'a' --> 10
+ *          'F' --> 15
+ */
+static int
+sippmh_htoi (const char inputChar)
+{
+    char inputValue[2];
+    long strtol_result;
+    char *strtol_end;
+
+    inputValue[0] = inputChar;
+    inputValue[1] = '\0';
+
+    errno = 0;
+    strtol_result = strtol(inputValue, &strtol_end, 16);
+
+    if (errno || inputValue == strtol_end) {
+      return 0;
+    } else {
+      return (int) strtol_result;
+    }
+}
+
+/*
+ * This function(similar to memchr()) works with both strings and array of characters.
+ * Returns the pointer to the matched character, if any; NULL otherwise. To include memchr.c
+ * in the phone library takes 1K of extra size and hence wrote this.
+ */
+static void *
+char_lookup (const void *src, unsigned char c, int n)
+{
+    const unsigned char *mem = (unsigned char *) src;
+
+    while (n--) {
+        if (*mem == c) {
+            return ((void *) mem);
+        }
+        mem++;
+    }
+
+    return NULL;
+}
+
+
+/* List of characters that can exist in user part of URL/URI without escaping */
+#define  userUnResCharListSize  17
+static char userUnResCharList[userUnResCharListSize] =
+{
+    SEMI_COLON, EQUAL_SIGN, TILDA, STAR, UNDERSCORE, PLUS,
+    SINGLE_QUOTE, LEFT_PARENTHESIS, RIGHT_PARENTHESIS, DOLLAR_SIGN,
+    FORWARD_SLASH, DOT, DASH, EXCLAMATION, AMPERSAND, COMMA, QUESTION_MARK
+};
+
+
+/*
+ * This utility function converts selected characters in a string to escaped characters,
+ * per RFC2396. Escape format = %hexhex (hexhex - hex value of ASCII character)
+ *   ex.  'abc@123' gets converted to "abc%40123" if '@' exists in escapable character list
+ *
+ * inptStr     = ptr to string of chars
+ * inputStrLen = num of characters that you want to convert
+ * excludeCharTbl  = lookup table/string of characters that need not be escaped
+ * excludeCharTblSize = number of characters in the table/string that need not be escaped
+ * null_terminate = TRUE if '\0' is required to be added at end
+ *                  FALSE if not required.
+ * outputStr   = ptr to pre-allocated memory for storing the
+ *               converted string
+ * outputStrSize = the size limit of the outputStr buffer
+ *
+ * returns the new size of the output string
+ * Converted string is stored in outputStr
+ *                          (contents = converted str)
+ */
+static size_t
+sippmh_escapeChars_util (const char *inputStr, size_t inputStrLen,
+                         char *excludeCharTbl, size_t excludeCharTblSize,
+                         char *outputStr, size_t outputStrSize,
+                         boolean null_terminate)
+{
+    size_t char_cnt = 0;
+    char  *nextOutputChar;
+    int    additional_byte = null_terminate ? 1 : 0;
+    size_t copy_count = 0;
+
+    nextOutputChar = outputStr;
+
+    while (char_cnt++ < inputStrLen) {
+        /* Check for characters that doesn't need to be escaped */
+        if (((*inputStr) >= 'A' && (*inputStr) <= 'Z') ||
+            ((*inputStr) >= 'a' && (*inputStr) <= 'z') ||
+            ((*inputStr) >= '0' && (*inputStr) <= '9') ||
+            (char_lookup(excludeCharTbl, (unsigned char) *inputStr,
+            excludeCharTblSize) != NULL)) {
+            if (outputStrSize < (copy_count + 1 + additional_byte)) {
+                break;
+            }
+            *nextOutputChar++ = *inputStr++;
+            copy_count++;
+        } else {  // Characters that need escaping
+            if (outputStrSize < (copy_count + 3 + additional_byte)) {
+                break;
+            }
+            /* Copy %hh to output string */
+            sprintf(nextOutputChar, "%c%x", '%', *inputStr++);
+            nextOutputChar = nextOutputChar + 3;
+            copy_count += 3;
+        }
+
+    }
+
+    if (null_terminate) {
+        copy_count++;
+        *nextOutputChar = '\0';
+    }
+    return (copy_count);
+}
+
+/*
+ * This function converts selected characters in a string to escaped characters,
+ * per RFC2396. Escape format = %hexhex (hexhex - hex value of ASCII character)
+ *   ex.  'abc@123' gets converted to "abc%40123" if '@' exists in escapable character list
+ *
+ * inptStr     = ptr to string of chars
+ * inputStrLen = num of characters that you want to convert
+ * outputStr   = ptr to pre-allocated memory for storing the
+ *               converted string
+ * outputStrSize = the size limit of the outputStr buffer
+ * null_terminate = TRUE if '\0' is required to be added at end
+ *                  FALSE if not required.
+ * returns the new size of the output string
+ *
+ * Converted string is stored in outputStr
+ *                          (contents = converted str)
+ */
+size_t
+sippmh_convertURLCharToEscChar (const char *inputStr, size_t inputStrLen,
+                                char *outputStr, size_t outputStrSize,
+                                boolean null_terminate)
+{
+    return (sippmh_escapeChars_util(inputStr, inputStrLen,
+                                    userUnResCharList, userUnResCharListSize,
+                                    outputStr, outputStrSize, null_terminate));
+}
+/*
+ * This function converts any '\' or '"' character in a quoted string to escaped characters,
+ * per RFC3261. Escape format = %hexhex (hexhex - hex value of ASCII character)
+ *   ex.  'abc\\' gets converted to "abc%5c%5c" if '\' exists in escapable character list
+ *
+ * inptStr     = ptr to string of chars
+ * inputStrLen = num of characters that you want to convert
+ * outputStr   = ptr to pre-allocated memory for storing the
+ *               converted string
+ * outputStrSize = the size limit of the outputStr buffer
+ * null_terminate = TRUE if '\0' is required to be added at end
+ *                  FALSE if not required.
+ * returns the new size of the output string
+ *
+ * Converted string is stored in outputStr
+ *                          (contents = converted str)
+ */
+size_t
+sippmh_converQuotedStrToEscStr(const char *inputStr, size_t inputStrLen,
+                         char *outputStr, size_t outputStrSize,
+                         boolean null_terminate)
+{
+    size_t char_cnt = 0;
+    char  *nextOutputChar;
+    int    additional_byte = null_terminate ? 1 : 0;
+    size_t copy_count = 0;
+
+    nextOutputChar = outputStr;
+
+    while (char_cnt++ < inputStrLen) {
+        /* Check for characters that need to be escaped */
+        if( ((*inputStr) == ESCAPE_CHAR)  || ((*inputStr) == DOUBLE_QUOTE))
+         {
+            // Characters that need escaping
+             if (outputStrSize < (copy_count + 3 + additional_byte)) {
+                break;
+            }
+            /* Copy %hh to output string */
+            sprintf(nextOutputChar, "%c%02x", '%', *inputStr++);
+            nextOutputChar = nextOutputChar + 3;
+            copy_count += 3;
+        }
+        else {
+            if (outputStrSize < (copy_count + 1 + additional_byte)) {
+                break;
+            }
+            *nextOutputChar++ = *inputStr++;
+            copy_count++;
+        }
+
+    }
+
+    if (null_terminate) {
+        copy_count++;
+        *nextOutputChar = '\0';
+    }
+
+    return (copy_count);
+}
+
+/*
+ * This function converts all Escaped characters in a string
+ * to their non-escaped format according to RFC2396.
+ * Escape format = %hh   (%<hex equivalent of ASCII char>)
+ *   ex.  'abc%40123' gets converted to "abc@123"
+ *
+ * inptStr     = ptr to string of chars
+ * inputStrLen = num of characters that you want to convert
+ * outputStr   = ptr to pre-allocated memory for storing the
+ *               converted string
+ *
+ * Converted string is stored in outputStr
+ *                          (contents = converted str)
+ *                          (contents = '\0' if no input string)
+ */
+void
+sippmh_convertEscCharToChar (const char *inputStr, size_t inputStrLen,
+                             char *outputStr)
+{
+    size_t  char_cnt = 0;
+    char   *nextOutputChar;
+    int     value;
+    boolean esc_char_found = FALSE;
+
+    *outputStr = '\0';
+    nextOutputChar = outputStr;
+
+    while (char_cnt < inputStrLen) {
+        if (*inputStr == PERCENT) {
+            /* Skip ESC syntax */
+            inputStr++;
+            char_cnt++;
+            esc_char_found = TRUE;
+        }
+
+        /* Copy character to output str */
+        if (esc_char_found) {
+
+            /* Convert 1st and 2nd hex chars to int */
+            value = sippmh_htoi(*inputStr) * 16;
+            inputStr++;
+            char_cnt++;
+            value += sippmh_htoi(*inputStr);
+            sprintf(nextOutputChar, "%c", toascii(value));
+
+            inputStr++;
+            char_cnt++;
+            esc_char_found = FALSE;
+
+        } else {
+            *nextOutputChar = *inputStr;
+            inputStr++;
+            char_cnt++;
+        }
+
+        nextOutputChar++;
+    }
+
+    *nextOutputChar = '\0';
+}
+
+
+void
+sippmh_free_replaces (sipReplaces_t *repl)
+{
+
+    if (repl == NULL) {
+        return;
+    }
+    if (repl->str_start) {
+        cpr_free(repl->str_start);
+    }
+    if (repl->signature_scheme) {
+        cpr_free(repl->signature_scheme);
+    }
+    cpr_free(repl);
+}
+
+
+void
+sippmh_free_refer_to (sipReferTo_t *ref_to)
+{
+
+    if (ref_to == NULL) {
+        return;
+    }
+    if (ref_to->ref_to_start) {
+        cpr_free(ref_to->ref_to_start);
+    }
+    if (ref_to->sip_replaces_hdr) {
+        cpr_free(ref_to->sip_replaces_hdr);
+    }
+    if (ref_to->sip_acc_cont) {
+        cpr_free(ref_to->sip_acc_cont);
+    }
+    if (ref_to->sip_proxy_auth) {
+        cpr_free(ref_to->sip_proxy_auth);
+    }
+    if (ref_to->targetUrl) {
+        sippmh_genurl_free(ref_to->targetUrl);
+    }
+    cpr_free(ref_to);
+
+}
+
+
+static void
+sippmh_free_remote_party_id (sipRemotePartyId_t *remote_party_id)
+{
+    if (remote_party_id) {
+        if (remote_party_id->loc) {
+            sippmh_free_location(remote_party_id->loc);
+        }
+        cpr_free(remote_party_id);
+    }
+}
+
+
+void
+sippmh_free_remote_party_id_info (sipRemotePartyIdInfo_t *rpid_info)
+{
+    uint32_t i;
+
+    if (rpid_info) {
+        if (rpid_info->num_rpid > 0) {
+            for (i = 0; i < rpid_info->num_rpid; i++) {
+                sippmh_free_remote_party_id(rpid_info->rpid[i]);
+                rpid_info->rpid[i] = NULL;
+            }
+        }
+        cpr_free(rpid_info);
+    }
+}
+
+void
+sippmh_free_diversion_info (sipDiversionInfo_t *div_info)
+{
+    if (div_info) {
+        if (div_info->orig_called_name) {
+            strlib_free(div_info->orig_called_name);
+        }
+        if (div_info->orig_called_number) {
+            strlib_free(div_info->orig_called_number);
+        }
+        if (div_info->last_redirect_name) {
+            strlib_free(div_info->last_redirect_name);
+        }
+        if (div_info->last_redirect_number) {
+            strlib_free(div_info->last_redirect_number);
+        }
+        cpr_free(div_info);
+    }
+
+}
+
+static boolean
+sippmh_parse_remote_party_id_params (char *params,
+                                     sipRemotePartyId_t *remote_party_id)
+{
+    boolean params_good;
+    int param_len;
+    char *param_name;
+
+    if ((params == NULL) || (remote_party_id == NULL)) {
+        return FALSE;
+    }
+
+    while (1) {
+        params_good = FALSE;
+
+        while (*params == ';') {
+            params++;
+        }
+
+        param_name = params;
+        SKIP_SIP_TOKEN(params);
+        param_len = params - param_name;
+        if (param_len == 0) {
+            return FALSE;
+        }
+
+        /* Parse screen parameter */
+        /* If there are multiple screen parameters, "no" takes precedence */
+        if ((param_len == RPID_SCREEN_LEN) &&
+            (strncasecmp(param_name, RPID_SCREEN, RPID_SCREEN_LEN) == 0) &&
+            (!remote_party_id->screen ||
+             cpr_strcasecmp(remote_party_id->screen, "no"))) {
+            params = parse_generic_param(params, &remote_party_id->screen);
+            if (params == NULL) {
+                return FALSE;
+            } else {
+                params_good = TRUE;
+            }
+
+        /* Parse party-type parameter */
+        } else if ((param_len == RPID_PARTY_TYPE_LEN) &&
+                   (strncasecmp(param_name, RPID_PARTY_TYPE,
+                                RPID_PARTY_TYPE_LEN) == 0)) {
+            params = parse_generic_param(params, &remote_party_id->party_type);
+            if (params == NULL) {
+                return FALSE;
+            } else {
+                params_good = TRUE;
+            }
+
+        /* Parse id-type parameter */
+        } else if ((param_len == RPID_ID_TYPE_LEN) &&
+                   (strncasecmp(param_name, RPID_ID_TYPE,
+                                RPID_ID_TYPE_LEN) == 0)) {
+            params = parse_generic_param(params, &remote_party_id->id_type);
+            if (params == NULL) {
+                return FALSE;
+            } else {
+                params_good = TRUE;
+            }
+
+        /* Parse privacy parameter */
+        } else if ((param_len == RPID_PRIVACY_LEN) &&
+                   (strncasecmp(param_name, RPID_PRIVACY,
+                                RPID_PRIVACY_LEN) == 0)) {
+            params = parse_generic_param(params, &remote_party_id->privacy);
+            if (params == NULL) {
+                return FALSE;
+            } else {
+                params_good = TRUE;
+            }
+
+        /* Parse np parameter */
+        } else if ((param_len == RPID_NP_LEN) &&
+                   (strncasecmp(param_name, RPID_NP, RPID_NP_LEN) == 0)) {
+            params = parse_generic_param(params, &remote_party_id->np);
+            if (params == NULL) {
+                return FALSE;
+            } else {
+                params_good = TRUE;
+            }
+        }
+
+        SKIP_LWS(params);
+        if (*params == SEMI_COLON) {
+            /* More parameters follow */
+            *params++ = '\0';
+            SKIP_LWS(params);
+        } else {
+            break;
+        }
+    }
+
+    return params_good;
+}
+
+sipRemotePartyId_t *
+sippmh_parse_remote_party_id (const char *input_remote_party_id)
+{
+    /* static const char fname[] = "sippmh_parse_remote_party_id"; */
+    char *more_ptr;
+    sipRemotePartyId_t *remote_party_id;
+    char *remote_party_id_str;
+
+    /*
+     * Remote-Party-ID = "Remote-Party-ID" ":" [display-name]
+     *                   "<" addr-spec ">" *(";" rpi-token)
+     * rpi-token       = rpi-screen | rpi-pty-type |
+     *                   rpi-id-type | rpi-privacy | rpi-np |
+     *                   other-rpi-token
+     * rpi-screen      = "screen" "=" ("no" | "yes")
+     * rpi-pty-type    = "party" "=" ("calling" | "called" | token)
+     * rpi-id-type     = "id-type" "=" ("subscriber" | "user" | "alias" |
+     *                                  "return" | "term" | token)
+     * rpi-privacy     = "privacy" = 1#(
+     *                     ("full" | "name" | "uri" | "off" | token)
+     *                     ["-" ("network" | token) ] )
+     * rpi-np = "np" "=" ("ordinary" | "residential" | "business" |
+     *                    "priority" | "hotel" | "failure" | "hospital" |
+     *                    "prison" | "police" | "test" | "payphone" |
+     *                    "coin" | "payphone-public" | "payphone-private" |
+     *                    "coinless" | "restrict" | "coin-restrict" |
+     *                    "coinless-restrict" | "reserved" | "operator" |
+     *                    "trans-freephone" | "isdn-res" | "isdn-bus" |
+     *                    "unknown" | "emergency" | token )
+     * other-rpi-token = ["-"] token ["=" (token | quoted-string)]
+     */
+
+    remote_party_id = (sipRemotePartyId_t *)
+        cpr_calloc(1, sizeof(sipRemotePartyId_t));
+    if (remote_party_id == NULL) {
+        return NULL;
+    }
+
+    /* Duplicate the string and work with it -
+     * This duplicate string is stored in the location structure
+     * pointed to by remote_party_id->loc and freed when the that
+     * structure is freed. Note that this pointer is *not* dup'ed
+     * in the parse_nameaddr_or_addrspec structure
+     */
+    remote_party_id_str = cpr_strdup(input_remote_party_id);
+
+    if (remote_party_id_str == NULL) {
+        sippmh_free_remote_party_id(remote_party_id);
+        remote_party_id = NULL;
+        more_ptr = NULL;
+    } else {
+        remote_party_id->loc =
+            sippmh_parse_nameaddr_or_addrspec(remote_party_id_str,
+                                              remote_party_id_str, FALSE,
+                                              FALSE, &more_ptr);
+
+        if (remote_party_id->loc == NULL) {
+            cpr_free(remote_party_id_str);
+            sippmh_free_remote_party_id(remote_party_id);
+            remote_party_id = NULL;
+            more_ptr = NULL;
+        }
+    }
+
+    if (more_ptr == NULL || *more_ptr == 0) {
+        /* No params. Use defaults of:
+         *     party=calling, screen=no, id-type=subscriber,
+         *     privacy=off, np=0 (ordinary)
+         */
+    } else {
+        (void) sippmh_parse_remote_party_id_params(more_ptr, remote_party_id);
+    }
+
+    return remote_party_id;
+}
+
+
+char *
+sippmh_generate_authorization (sip_author_t *sip_author)
+{
+    char *buffer;
+
+    /*
+     * This routine takes a sip_author_t struct and generates an Authorization
+     * header or a Proxy-Authorization header.  Since this routine generates
+     * a header for Authorization or Proxy-Authorization, the header will
+     * start with "Digest" or "Basic".  The user should use sstrncat to
+     * add "Proxy Authorization: " or "Authorization: " depending which is
+     * needed.
+     */
+    if (!sip_author) {
+        return NULL;
+    }
+    buffer = (char *) cpr_malloc(MAX_SIP_HEADER_LENGTH);
+    if (!buffer) {
+        return NULL;
+    }
+
+    if (sip_author->scheme == SIP_DIGEST) {
+        snprintf(buffer, MAX_SIP_HEADER_LENGTH,
+                 "%s %s=\"%s\",%s=\"%s\",%s=\"%s\",%s=\"%s\",%s=\"%s\"",
+                 AUTHENTICATION_DIGEST,
+                 AUTHENTICATION_USERNAME, sip_author->d_username,
+                 AUTHENTICATION_REALM, sip_author->realm,
+                 AUTHENTICATION_URI, sip_author->unparsed_uri,
+                 AUTHENTICATION_RESPONSE, sip_author->response,
+                 AUTHENTICATION_NONCE, sip_author->nonce);
+
+        /*
+         * The header must have username, realm, uri, response, and nonce.
+         * All other parameters are optional, so check if they exist and
+         * add them on to the buffer if they do.
+         */
+
+        if (sip_author->opaque) {
+            char *buffer2;
+
+            buffer2 = (char *) cpr_malloc(MAX_URI_LENGTH);
+            if (!buffer2) {
+                cpr_free(buffer);
+                return NULL;
+            }
+            snprintf(buffer2, MAX_URI_LENGTH, ",%s=\"%s\"",
+                     AUTHENTICATION_OPAQUE, sip_author->opaque);
+            sstrncat(buffer, buffer2, MAX_SIP_HEADER_LENGTH - strlen(buffer));
+            cpr_free(buffer2);
+        }
+        if (sip_author->cnonce) {
+            char *buffer3;
+
+            buffer3 = (char *) cpr_malloc(MAX_URI_LENGTH);
+            if (!buffer3) {
+                cpr_free(buffer);
+                return NULL;
+            }
+            snprintf(buffer3, MAX_URI_LENGTH,
+                     ",cnonce=\"%s\"", sip_author->cnonce);
+            sstrncat(buffer, buffer3, MAX_SIP_HEADER_LENGTH - strlen(buffer));
+            cpr_free(buffer3);
+        }
+        if (sip_author->qop) {
+            char *buffer4;
+
+            buffer4 = (char *) cpr_malloc(MAX_URI_LENGTH);
+            if (!buffer4) {
+                cpr_free(buffer);
+                return NULL;
+            }
+            snprintf(buffer4, MAX_URI_LENGTH, ",qop=%s", sip_author->qop);
+            sstrncat(buffer, buffer4, MAX_SIP_HEADER_LENGTH - strlen(buffer));
+            cpr_free(buffer4);
+        }
+        if (sip_author->nc_count) {
+            char *buffer5;
+
+            buffer5 = (char *) cpr_malloc(MAX_URI_LENGTH);
+            if (!buffer5) {
+                cpr_free(buffer);
+                return NULL;
+            }
+            snprintf(buffer5, MAX_URI_LENGTH, ",nc=%s", sip_author->nc_count);
+            sstrncat(buffer, buffer5, MAX_SIP_HEADER_LENGTH - strlen(buffer));
+            cpr_free(buffer5);
+        }
+        if (sip_author->algorithm) {
+            char *buffer6;
+
+            buffer6 = (char *) cpr_malloc(MAX_URI_LENGTH);
+            if (!buffer6) {
+                cpr_free(buffer);
+                return NULL;
+            }
+            snprintf(buffer6, MAX_URI_LENGTH,
+                     ",%s=%s", AUTHENTICATION_ALGORITHM, sip_author->algorithm);
+            sstrncat(buffer, buffer6, MAX_SIP_HEADER_LENGTH - strlen(buffer));
+            cpr_free(buffer6);
+        }
+    } else {
+        sprintf(buffer, "%s %s", AUTHENTICATION_BASIC, sip_author->user_pass);
+    }
+    return buffer;
+}
+
+void
+sippmh_free_authen (sip_authen_t *sip_authen)
+{
+    if (sip_authen) {
+        cpr_free(sip_authen->str_start);
+        cpr_free(sip_authen);
+    }
+}
+
+static boolean
+sippmh_validate_authenticate (sip_authen_t *sip_authen)
+{
+    if (sip_authen) {
+        if ((sip_authen->realm != NULL) && (sip_authen->nonce != NULL)) {
+            return TRUE;
+        } else {
+            return FALSE;
+        }
+    }
+    return FALSE;
+}
+
+/* routine used to parse WWW-Authenticate and Proxy-Authenticate headers */
+
+sip_authen_t *
+sippmh_parse_authenticate (const char *input_char)
+{
+    sip_authen_t *sip_authen;
+    char *input;
+    boolean good_params;
+    boolean qop_found = FALSE;
+    char *trash;
+
+/*
+ * Basic:
+ * "WWW-Authenticate" ":" 1#challenge
+ * challenge   = "Basic" realm
+ *******************************************************
+ * Digest:
+ * "Digest" digest-challenge
+ * digest-challenge  = 1#( realm | [ domain ] | nonce |
+ *                     [ opaque ] |[ stale ] | [ algorithm ] |
+ *                     [ qop-options ] | [auth-param] )
+ *
+ * domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
+ * URI               = absoluteURI | abs_path
+ * opaque            = "opaque" "=" quoted-string
+ * stale             = "stale" "=" ( "true" | "false" )
+ * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" |
+ *                     token )
+ * qop-options       = "qop" "=" <"> 1#qop-value <">
+ * qop-value         = "auth" | "auth-int" | token
+ */
+
+    if (!input_char) {
+        return NULL;
+    }
+
+    input = cpr_strdup(input_char);
+    if (!input) {
+        return NULL;
+    }
+
+    sip_authen = (sip_authen_t *) cpr_calloc(1, sizeof(sip_authen_t));
+    if (!sip_authen) {
+        cpr_free(input);
+        return NULL;
+    }
+
+    sip_authen->str_start = input;
+    SKIP_WHITE_SPACE(input);
+
+    /* now pointing at Basic, pgp or Digest */
+
+    if (strncasecmp(input, AUTHENTICATION_BASIC, 5) == 0) {
+        sip_authen->scheme = SIP_BASIC;
+        input += 5;
+        *input = 0;
+        input++;
+        SKIP_WHITE_SPACE(input);
+        if (strncasecmp(input, AUTHENTICATION_REALM, 5) == 0) {
+            input += 5;
+            SKIP_WHITE_SPACE(input);
+            if (*input == '=') {
+                input++;
+            } else {
+                sippmh_free_authen(sip_authen);
+                CCSIP_ERR_DEBUG {
+                    buginf("\n%s", get_debug_string(DEBUG_PMH_INCORRECT_SYNTAX));
+                }
+                return NULL;
+            }
+            SKIP_WHITE_SPACE(input);
+            sip_authen->realm = input;
+            return sip_authen;
+        }
+        sip_authen->user_pass = input;  /* encoded base 64 */
+        return sip_authen;
+
+    } else if (strncasecmp(input, AUTHENTICATION_DIGEST, 6) == 0) {
+        sip_authen->scheme = SIP_DIGEST;
+        input += 6;
+        *input = 0;
+        input++;
+        SKIP_WHITE_SPACE(input);
+    } else {
+        sippmh_free_authen(sip_authen);
+        CCSIP_ERR_DEBUG {
+            buginf("\n%s", get_debug_string(DEBUG_PMH_INVALID_SCHEME));
+        }
+        return NULL;
+    }
+
+    /* pointing at Digest-challenge */
+
+    /* if algorithm is not in string set it to md5 */
+
+    sip_authen->algorithm = "md5";
+
+    /* loop through string until all fields have been handled */
+
+    while (input) {
+        char **ptr = NULL;
+
+        if (strncasecmp(input, AUTHENTICATION_DOMAIN, 6) == 0) {
+            input += 6;
+            ptr = &(sip_authen->unparsed_domain);
+        } else if (strncasecmp(input, AUTHENTICATION_ALGORITHM, 9) == 0) {
+            input += 9;
+            ptr = &(sip_authen->algorithm);
+        } else if (strncasecmp(input, AUTHENTICATION_OPAQUE, 6) == 0) {
+            input += 6;
+            ptr = &(sip_authen->opaque);
+        } else if (strncasecmp(input, "stale", 5) == 0) {
+            input += 5;
+            ptr = &(sip_authen->stale);
+        } else if (strncasecmp(input, AUTHENTICATION_REALM, 5) == 0) {
+            input += 5;
+            ptr = &(sip_authen->realm);
+        } else if (strncasecmp(input, AUTHENTICATION_NONCE, 5) == 0) {
+            input += 5;
+            ptr = &(sip_authen->nonce);
+        } else if (strncasecmp(input, "qop", 3) == 0) {
+            input += 3;
+            ptr = &(sip_authen->qop);
+            qop_found = TRUE;
+        } else {
+            /*
+             * Ignore unrecognized parameters.
+             */
+            ptr = &trash;
+            input = strchr(input, EQUAL_SIGN);
+            if (input == NULL) {
+                sippmh_free_authen(sip_authen);
+                CCSIP_ERR_DEBUG {
+                    buginf("\n%s", get_debug_string(DEBUG_PMH_INVALID_FIELD_VALUE));
+                }
+                return NULL;
+            }
+        }
+        SKIP_WHITE_SPACE(input);
+        if (*input != EQUAL_SIGN) {
+            sippmh_free_authen(sip_authen);
+            CCSIP_ERR_DEBUG {
+                buginf("\n%s", get_debug_string(DEBUG_PMH_INCORRECT_SYNTAX));
+            }
+            return NULL;
+        }
+        input++; /* skip the equal sign */
+        SKIP_WHITE_SPACE(input);
+        /* input is now pointing at the value of the parameter */
+        *ptr = input;
+        if (*input == DOUBLE_QUOTE) {
+            input++;
+            *ptr = input; /*sam do not want the quotes in the string */
+            input = strchr(input, DOUBLE_QUOTE);
+            if (input == NULL) {
+                sippmh_free_authen(sip_authen);
+                CCSIP_ERR_DEBUG {
+                    buginf("\n%s", get_debug_string(DEBUG_PMH_INCORRECT_SYNTAX));
+                }
+                return NULL;
+            } else {
+                *input++ = '\0';    /*sam string needs to be null terminated */
+            }
+            /*
+             * Check the qop. If qop is included it has to be either
+             * auth, auth-int or both. If both are included then we will
+             * just drop the second one.
+             */
+            if (qop_found == TRUE) {
+                char *qop_input = NULL;
+
+                if (sip_authen->qop) {
+                    qop_input = strchr(sip_authen->qop, COMMA);
+                }
+                if (qop_input != NULL) {
+                    *qop_input = '\0';
+                }
+
+                /*
+                 * Make sure that the qop is either auth or auth-int
+                 */
+                if ((strncasecmp(sip_authen->qop, "auth", 4) != 0) &&
+                    (strncasecmp(sip_authen->qop, "auth-int", 8) != 0)) {
+                    sip_authen->qop = NULL;
+                }
+            }
+        }
+
+        input = strchr(input, COMMA);
+        if (!input) {
+            good_params = sippmh_validate_authenticate(sip_authen);
+            if (good_params) {
+                return sip_authen;
+            } else {
+                sippmh_free_authen(sip_authen);
+                CCSIP_ERR_DEBUG {
+                    buginf("\n%s", get_debug_string(DEBUG_PMH_NOT_ENOUGH_PARAMETERS));
+                }
+                return NULL;
+            }
+        }
+        *input++ = 0;
+        SKIP_WHITE_SPACE(input);
+    }
+
+    sippmh_free_authen(sip_authen);
+    return NULL;
+}
+
+/*
+ *  Function: sippmh_parse_displaystr
+ *
+ *  Parameters: Display string to be parsed
+ *
+ *  Description:Removes leading <sip: and every character
+ *              after : in string
+ *
+ *  Returns:  Pointer to parsed string
+ *
+ */
+
+string_t
+sippmh_parse_displaystr (string_t displaystr)
+{
+    char temp_displaystr[CC_MAX_DIALSTRING_LEN];
+    char *temp_ptr;
+    char *temp;
+
+    sstrncpy(temp_displaystr, displaystr, CC_MAX_DIALSTRING_LEN);
+    temp_ptr = (char *) temp_displaystr;
+
+    temp = strcasestr(temp_ptr, "sip:");
+    if (temp) {
+        temp_ptr = temp + 4;
+    }
+    temp = strchr(temp_ptr, ':');
+    if (temp) {
+        *temp = 0;
+    }
+    temp = strchr(temp_ptr, '?');
+    if (temp) {
+        *temp = 0;
+    }
+    temp = strchr(temp_ptr, ';');
+    if (temp) {
+        *temp = 0;
+    }
+    temp = strchr(temp_ptr, '>');
+    if (temp) {
+        *temp = 0;
+    }
+    displaystr = strlib_update(displaystr, temp_ptr);
+    return (displaystr);
+}
+
+/*
+ *  Function: sippmh_process_via_header
+ *
+ *  Parameters: Received Sip Message
+ *              IP Address the Message was received from
+ *
+ *  Description: Appends received=<ip_address> to the first Via
+ *               field if the ip address we received this message
+ *               from is not the same as the ip address in the via
+ *               field or if it contains a domain name
+ *
+ *  Returns: None
+ *
+ */
+void
+sippmh_process_via_header (sipMessage_t *sip_message,
+                           cpr_ip_addr_t *source_ip_address)
+{
+    char dotted_ip[MAX_IPADDR_STR_LEN];
+    char *hdr_start;
+    char *new_buf;
+    int new_buf_len;
+    char *offset;
+    long old_header_offset;
+    sipVia_t *via;
+    cpr_ip_addr_t tmp_ip_addr;
+
+    CPR_IP_ADDR_INIT(tmp_ip_addr);
+
+    if (sip_message && sippmh_is_request(sip_message)) {
+        via = sippmh_parse_via(sip_message->hdr_cache[VIA].val_start);
+        if (via == NULL) {
+            /* error message already displayed in parse function */
+            /* mark the message as being incomplete so a 400 will get sent */
+            sip_message->is_complete = 0;
+            return;
+        }
+
+        util_ntohl(&tmp_ip_addr, source_ip_address);
+        ipaddr2dotted(dotted_ip, &tmp_ip_addr);
+        if (strcmp(via->host, dotted_ip) && (via->recd_host == NULL)) {
+            hdr_start = sip_message->hdr_cache[VIA].hdr_start;
+
+            /*
+             * +3 accounts for the ; and the = before and after the received
+             * and the terminating NULL
+             */
+            new_buf_len = strlen(hdr_start) + sizeof(VIA_RECEIVED) + strlen(dotted_ip) + 3;
+            new_buf = (char *) cpr_malloc(new_buf_len);
+            /*
+             * If we cannot allocate memory, we will just leave
+             * the VIA alone and hope things work out for the best
+             */
+            if (new_buf != NULL) {
+                offset = strchr(hdr_start, ',');
+                old_header_offset = sip_message->hdr_cache[VIA].val_start -
+                    hdr_start;
+                sip_message->hdr_cache[VIA].hdr_start = new_buf;
+                sip_message->hdr_cache[VIA].val_start = new_buf +
+                    old_header_offset;
+
+                if (offset) {
+                  snprintf(new_buf, new_buf_len, "%.*s;%s=%s%s", (int) (offset - hdr_start), hdr_start, VIA_RECEIVED, dotted_ip, offset);
+                } else {
+                  snprintf(new_buf, new_buf_len, "%s;%s=%s", hdr_start, VIA_RECEIVED, dotted_ip);
+                }
+
+                cpr_free(hdr_start);
+            }
+        }
+        sippmh_free_via(via);
+    }
+}
+
+
+/*
+ *  Function: sippmh_parse_user
+ *
+ *  Parameters: user portion of URL to parse
+ *
+ *  Description: This function will remove any parameters
+ *          which appear in the user portion of the url.
+ *    (i.e. 15726;oct3=128@10.10.10.10 will return 15726)
+ *    The SIP Phone does not care about the extra parameters and
+ *    they need to be removed so that the user portion will
+ *    match the phone provisioning and display correctly.
+ *
+ *  Returns: A Copy of the input string with the parameters removed
+ *           if things go well.  NULL if we encounter an error.
+ */
+char *
+sippmh_parse_user (char *url_main)
+{
+    char *user = NULL;
+    size_t size = 0;
+    char *lasts = NULL;
+
+    if (url_main == NULL) {
+        return (NULL);
+    }
+
+    /*
+     * If the first char is ';', give up.  That
+     * breaks our assumption.
+     */
+    if (*url_main == SEMI_COLON) {
+        return (NULL);
+    }
+
+    /*
+     * Create a new string and copy the url_main
+     * string into it.  (We can't change the original
+     * string because it may be needed for creating
+     * SIP response messages.)
+     */
+    size = strlen(url_main) + 1;
+    user = (char *) cpr_malloc(size);
+    /*
+     * If the malloc fails, return the original URL.
+     */
+    if (user == NULL) {
+        return (NULL);
+    }
+
+    sstrncpy(user, url_main, size);
+
+    /*
+     * Search for ";" and null terminate the string there.
+     * An assumption has been made that any parameters will come
+     * after the number. i.e.: "15726;param=paramval"
+     */
+    (void) PL_strtok_r(user, ";", &lasts);
+    return (user);
+}
+
+/*
+ *  Function: sippmh_parse_message_summary
+ *
+ *  Parameters: The incoming SIP message and a container to be
+ *              filled with the parsed message body.
+ *
+ *  Description: This function will parse the incoming MWI NTFY SIP message
+ *               and fill the passed in structure with the
+ *               contents of the parsed message.Basic Error checking is done on
+ *               the passed in message.
+ *
+ *  Returns: SIP_ERROR on error.SIP_OK otherwise.
+ */
+int32_t
+sippmh_parse_message_summary(sipMessage_t *pSipMessage, sipMessageSummary_t *mesgSummary)
+{
+    int     i           = 0;
+    int     j           = 0;
+    char   *p           = NULL;
+    char   *val         = NULL;
+    char    temp[MAX_SIP_URL_LENGTH];
+    boolean  token_found = FALSE;
+    boolean  hp_found = FALSE;
+    long strtol_result;
+    char *strtol_end;
+
+    p = strstr(pSipMessage->mesg_body[0].msgBody, "Messages-Waiting");
+
+    if (!p) {
+        p = strstr(pSipMessage->mesg_body[0].msgBody, "Message-Waiting");
+    }
+
+    if (p) {
+        memset(temp, '\0', MAX_SIP_URL_LENGTH);
+
+        trim_right(p);
+
+        val = strstr(p, ":");
+        if (val) {
+            val++;
+
+            i = j = 0;
+            while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
+                j++;
+            }
+            while (val[j] != '\0' && val[j] != '\n' && val[j] != '\r') {
+                // return error if obviously wrong
+                if (!isalpha(val[j])) {
+                    return SIP_ERROR;
+                }
+                temp[i] = val[j];
+                j++;
+                if (++i >= MAX_SIP_URL_LENGTH) {
+                    return SIP_ERROR;
+                }
+            }
+            temp[i] = '\0';
+        } else {
+            return SIP_ERROR;
+        }
+    } else {
+        return SIP_ERROR;
+    }
+
+    if (cpr_strcasecmp(temp, "no") == 0) {
+        mesgSummary->mesg_waiting_on = FALSE;
+    } else if (cpr_strcasecmp(temp, "yes") == 0) {
+        mesgSummary->mesg_waiting_on = TRUE;
+    } else {
+        return SIP_ERROR;
+    }
+
+/*
+    p = strstr(pSipMessage->mesg_body[0].msgBody, "Message-Account");
+
+    if (p) {
+        trim_right(p);
+
+        val = strstr(p, ":");
+        if (val) {
+            val++;
+
+            i = j = 0;
+            while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
+                j++;
+            }
+
+            while (val[j] != '\0' && val[j] != '\n' && val[j] != '\r') {
+                mesgSummary->message_account[i] = val[j];
+                j++;
+                if (++i >= MAX_SIP_URL_LENGTH) {
+                    return SIP_ERROR;
+                }
+            }
+            mesgSummary->message_account[i] = '\0';
+        } else {
+            return SIP_ERROR;
+        }
+    }
+*/
+
+    p = strstr(pSipMessage->mesg_body[0].msgBody, "Voice-Message");
+
+    if (p) {
+        mesgSummary->type = mwiVoiceType;
+        memset(temp, '\0', MAX_SIP_URL_LENGTH);
+        trim_right(p);
+        i = j = 0;
+        val = strstr(p, ":");
+        if (val) {
+            val++;
+
+            while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
+                j++;
+            }
+
+            while (val[j] != '\0' && val[j] != '\n' && val[j] != '\r' && !isspace(val[j]) && val[j] != '(') {
+                if (!isdigit(val[j])) {
+                    if (val[j] == '/') {
+                        temp[i] = '\0';
+
+                        errno = 0;
+                        strtol_result = strtol(temp, &strtol_end, 10);
+
+                        if (errno || temp == strtol_end || strtol_result > INT_MAX) {
+                            return SIP_ERROR;
+                        }
+
+                        mesgSummary->newCount = (int) strtol_result;
+                        token_found = TRUE;
+                        i = 0;
+                    } else {
+                        return SIP_ERROR;
+                    }
+                } else {
+                    temp[i] = val[j];
+                    if (++i >= MAX_SIP_URL_LENGTH) {
+                        return SIP_ERROR;
+                    }
+                }
+                j++;
+            }
+            temp[i] = '\0';
+
+            if (token_found) {
+                errno = 0;
+                strtol_result = (temp, &strtol_end, 10);
+
+                if (errno || temp == strtol_end || strtol_result > INT_MAX) {
+                    return SIP_ERROR;
+                }
+
+                mesgSummary->oldCount = (int) strtol_result;
+            }
+
+            temp[i = 0] = '\0';
+            while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
+                j++;
+            }
+
+            if (val[j] == '(') {
+                j++;
+                while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
+                    j++;
+                }
+
+                while (val[j] != '\0' && val[j] != '\n' && val[j] != '\r' && !isspace(val[j]) && val[j] != ')') {
+                    if (!isdigit(val[j])) {
+                        if (val[j] == '/') {
+                            temp[i] = '\0';
+
+                            errno = 0;
+                            strtol_result = strtol(temp, &strtol_end, 10);
+
+                            if (errno || temp == strtol_end || strtol_result > INT_MAX) {
+                                return SIP_ERROR;
+                            }
+
+                            mesgSummary->hpNewCount = (int) strtol_result;
+                            hp_found = TRUE;
+                            i = 0;
+                        } else {
+                            return SIP_ERROR;
+                        }
+                    } else {
+                        temp[i] = val[j];
+                        if (++i >= MAX_SIP_URL_LENGTH) {
+                            return SIP_ERROR;
+                        }
+                    }
+                    j++;
+                }
+                temp[i] = '\0';
+                if (hp_found) {
+                    errno = 0;
+                    strtol_result = strtol(temp, &strtol_end, 10);
+
+                    if (errno || temp == strtol_end || strtol_result > INT_MAX) {
+                        return SIP_ERROR;
+                    }
+
+                    mesgSummary->hpOldCount = (int) strtol_result;
+                }
+            }
+            if (!hp_found) {
+                mesgSummary->hpNewCount = mesgSummary->hpOldCount = -1;
+            }
+            /*make sure the urgent message counts don't exceed the total message counts*/
+            if ((mesgSummary->hpNewCount > mesgSummary->newCount)   ||
+                (mesgSummary->hpOldCount > mesgSummary->oldCount)) {
+                return SIP_ERROR;
+            }
+            if (!token_found) {
+                return SIP_ERROR;
+            }
+       } else {
+            return SIP_ERROR;
+       }
+    }
+
+    return SIP_OK;
+}
+/*
+ * This function compares two strings that may contain escaped characters
+ * in either of them and determines if they are equivalent, ignoring the
+ * case if ignore_case is TRUE.
+ * s1 = ptr to the first string of chars
+ * s2 = ptr to the second string of chars
+ * return: integer value of 0 if they are equivalent, <> 0 otherwise.
+ */
+int
+sippmh_cmpURLStrings (const char *s1, const char *s2, boolean ignore_case)
+{
+    const unsigned char *us1 = (const unsigned char *) s1;
+    const unsigned char *us2 = (const unsigned char *) s2;
+    int value1, value2;
+    int esc_char_incr_in_s1 = 0; // # of chars to skip in s1 if the given char is escaped
+    int esc_char_incr_in_s2 = 0; // # of chars to skip in s2 if the given char is escaped
+
+    if ((!s1 && s2) || (s1 && !s2)) /* no match if only one ptr is NULL */
+        return (int) (s1 - s2); /* if one of these is NULL it will be the
+                                 * lesser of the two values and therefore
+                                 * we'll get the proper sign in the int */
+
+    if (s1 == s2) {             /* match if both ptrs the same (e.g. NULL) */
+        return 0;
+    }
+
+    value1 = 0;
+    value2 = 0;
+    while (*us1 != '\0') {
+        if (*us1 == PERCENT) {
+            esc_char_incr_in_s1 = 2;
+
+            /* Convert 1st and 2nd hex chars to int */
+            value1 = sippmh_htoi(*(us1 + 1)) * 16;
+            value1 += sippmh_htoi(*(us1 + 2));
+        } else {
+            value1 = *us1;
+        }
+
+        if (*us2 == PERCENT) {
+            esc_char_incr_in_s2 = 2;
+
+            /* Convert 1st and 2nd hex chars to int */
+            value2 = sippmh_htoi(*(us2 + 1)) * 16;
+            value2 += sippmh_htoi(*(us2 + 2));
+        } else {
+            value2 = *us2;
+        }
+
+        if ((ignore_case && (toupper(value1) == toupper(value2))) ||
+            (!ignore_case && (value1 == value2))) {
+            us1 += (1 + esc_char_incr_in_s1);
+            us2 += (1 + esc_char_incr_in_s2);
+            esc_char_incr_in_s1 = 0;
+            esc_char_incr_in_s2 = 0;
+        } else {
+            break;
+        }
+    }
+
+    if (ignore_case) {
+        return (toupper(value1) - toupper(value2));
+    } else {
+        return (value1 - value2);
+    }
+}
+
+int
+sippmh_add_call_info (sipMessage_t *sip_message_p, cc_call_info_t *call_info_p)
+{
+    if (sip_message_p && call_info_p) {
+        char *call_info_hdr_p = ccsip_encode_call_info_hdr(call_info_p, NULL);
+
+        if (call_info_hdr_p != NULL) {
+            (void) sippmh_add_text_header(sip_message_p, SIP_HEADER_CALL_INFO,
+                                          (const char *) call_info_hdr_p);
+            cpr_free(call_info_hdr_p);
+        }
+    }
+    return (0);
+}
+
+/*
+ *  Function: sippmh_parse_supported_require
+ *
+ *  Parameters: contents of the Supported or Require header
+ *
+ *              punsupported_tokens: if the caller wants to know the
+ *                  unsupported option tokens, it will pass a non-null pointer.
+ *                  If punsupporte_tokens is NULL, then the caller does not
+ *                  want to know the unsupported  options.
+ *
+ *              It is caller's responsibility to free the contents of this
+ *              pointer.
+ *
+ *  Description: This function will parse for the various options tags
+ *               in the supported and require headers
+ *
+ *  Returns: A bit map containing the values in the header
+ *
+ *  Format of the line is as follows:
+ *  Supported: replaces, join, sec-agree, whatever ...
+ *  Require: replaces, join, sec-agree, whatever ...
+ */
+uint32_t
+sippmh_parse_supported_require (const char *header, char **punsupported_tokens)
+{
+    const char *fname = "sippmh_parse_supported_require";
+    uint32_t tags = 0;
+    char *temp_header;
+    char *token;
+    const char *delim = ", \r\n\t";
+    int unsupported_tokens_size = 0;
+    char *bad_token = NULL;
+    int  size;
+    char *lasts = NULL;
+
+    if (header == NULL) {
+        return (tags);
+    }
+
+    if (punsupported_tokens != NULL) {
+        *punsupported_tokens = NULL; //assume everything  will go right
+    }
+
+    //need to keep own buffer since PL_strtok_r is destructive
+    size = strlen(header) + 1;
+    temp_header = (char *) cpr_malloc(size);
+    if (temp_header == NULL) {
+        CCSIP_DEBUG_ERROR("%s: malloc failed for strlen(header)=%d\n", fname,
+                          strlen(header));
+        return tags;
+    }
+    sstrncpy(temp_header, header, size);
+
+    token = PL_strtok_r(temp_header, delim, &lasts);
+    while (token != NULL) {
+        bad_token = NULL;
+
+        if (strcmp(token, REQ_SUPP_PARAM_REPLACES) == 0) {
+            tags |= replaces_tag;
+        } else if (strcmp(token, REQ_SUPP_PARAM_100REL) == 0) {
+            tags |= rel_tag;
+            bad_token = token;
+        } else if (strcmp(token, REQ_SUPP_PARAM_EARLY_SESSION) == 0) {
+            tags |= early_session_tag;
+            bad_token = token;
+        } else if (strcmp(token, REQ_SUPP_PARAM_JOIN) == 0) {
+            tags |= join_tag;
+        } else if (strcmp(token, REQ_SUPP_PARAM_PATH) == 0) {
+            tags |= path_tag;
+            bad_token = token;
+        } else if (strcmp(token, REQ_SUPP_PARAM_PRECONDITION) == 0) {
+            tags |= precondition_tag;
+            bad_token = token;
+        } else if (strcmp(token, REQ_SUPP_PARAM_PREF) == 0) {
+            tags |= pref_tag;
+            bad_token = token;
+        } else if (strcmp(token, REQ_SUPP_PARAM_PRIVACY) == 0) {
+            tags |= privacy_tag;
+            bad_token = token;
+        } else if (strcmp(token, REQ_SUPP_PARAM_SEC_AGREE) == 0) {
+            tags |= sec_agree_tag;
+            bad_token = token;
+        } else if (strcmp(token, REQ_SUPP_PARAM_TIMER) == 0) {
+            tags |= timer_tag;
+            bad_token = token;
+        } else if (strcmp(token, REQ_SUPP_PARAM_NOREFERSUB) == 0) {
+            tags |= norefersub_tag;
+        } else if (strcmp(token, REQ_SUPP_PARAM_CISCO_CALLINFO) == 0) {
+            tags |= cisco_callinfo_tag;
+        } else if (strcmp(token, REQ_SUPP_PARAM_CISCO_SERVICE_CONTROL) == 0) {
+            tags |= cisco_service_control_tag;
+
+        } else if (strcmp(token, REQ_SUPP_PARAM_SDP_ANAT) == 0) {
+            tags |= sdp_anat_tag;
+        }
+        else if (strcmp(token, REQ_SUPP_PARAM_EXTENED_REFER) == 0) {
+            tags |= extended_refer_tag;
+        } else if (strcmp(token, REQ_SUPP_PARAM_CISCO_SERVICEURI) == 0) {
+            tags |= cisco_serviceuri_tag;
+        } else if (strcmp(token, REQ_SUPP_PARAM_CISCO_ESCAPECODES) == 0) {
+            tags |= cisco_escapecodes_tag;
+        } else if (strcmp(token, REQ_SUPP_PARAM_CISCO_SRTP_FALLBACK) == 0) {
+            tags |= cisco_srtp_fallback_tag;
+        }
+        else {
+            //This is not necessarily an error. Other end may support something we
+            //don't. Is only an error if it shows up in Require.
+            tags |= (uint32_t) unrecognized_tag;
+            bad_token = token;
+        }
+
+        if (bad_token != NULL) {
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Invalid tag in Require/Supported %s\n",
+                DEB_F_PREFIX_ARGS(SIP_TAG, fname), bad_token);
+
+            //allocate memory for unsupported options if necessary
+            if (punsupported_tokens) {
+                if (!*punsupported_tokens) {
+                    unsupported_tokens_size = strlen(header) + 1;
+                    *punsupported_tokens = (char *) cpr_malloc(unsupported_tokens_size);
+                    if (*punsupported_tokens) {
+                        memset(*punsupported_tokens, 0, unsupported_tokens_size);
+                    }
+                }
+            }
+
+            //caller wants to know what came in Require and we dont support
+            if (punsupported_tokens && *punsupported_tokens) {
+                //include token into illegal_tokens
+                if (strlen(*punsupported_tokens) > 0) {
+                    sstrncat(*punsupported_tokens, ",", unsupported_tokens_size - strlen(*punsupported_tokens)); //add a ","
+                }
+                sstrncat(*punsupported_tokens, bad_token, unsupported_tokens_size - strlen(*punsupported_tokens));
+            }
+            bad_token = NULL;
+        }
+
+        //get next token
+        token = PL_strtok_r(NULL, delim, &lasts);
+    }
+    cpr_free(temp_header);
+    return (tags);
+}
+
+/*
+ *  Function: sippmh_parse_allow_header
+ *
+ *  Parameters: contents of the Allow header
+ *
+ *  Description: This function will parse for the various methods
+ *               supported in the allow header
+ *
+ *  Returns: A bit map containing the values in the header
+ *
+ *  Format of the line is as follows:
+ *  Allow: ACK, BYE, CANCEL, ....
+ */
+uint16_t
+sippmh_parse_allow_header (const char *header)
+{
+    uint16_t tags = 0;
+
+    if (header == NULL) {
+        return (tags);
+    }
+    if (strstr(header, SIP_METHOD_ACK)) {
+        tags |= ALLOW_ACK;
+    }
+    if (strstr(header, SIP_METHOD_BYE)) {
+        tags |= ALLOW_BYE;
+    }
+    if (strstr(header, SIP_METHOD_CANCEL)) {
+        tags |= ALLOW_CANCEL;
+    }
+    if (strstr(header, SIP_METHOD_INFO)) {
+        tags |= ALLOW_INFO;
+    }
+    if (strstr(header, SIP_METHOD_INVITE)) {
+        tags |= ALLOW_INVITE;
+    }
+    if (strstr(header, SIP_METHOD_MESSAGE)) {
+        tags |= ALLOW_MESSAGE;
+    }
+    if (strstr(header, SIP_METHOD_NOTIFY)) {
+        tags |= ALLOW_NOTIFY;
+    }
+    if (strstr(header, SIP_METHOD_OPTIONS)) {
+        tags |= ALLOW_OPTIONS;
+    }
+    if (strstr(header, SIP_METHOD_PRACK)) {
+        tags |= ALLOW_PRACK;
+    }
+    if (strstr(header, SIP_METHOD_PUBLISH)) {
+        tags |= ALLOW_PUBLISH;
+    }
+    if (strstr(header, SIP_METHOD_REFER)) {
+        tags |= ALLOW_REFER;
+    }
+    if (strstr(header, SIP_METHOD_REGISTER)) {
+        tags |= ALLOW_REGISTER;
+    }
+    if (strstr(header, SIP_METHOD_SUBSCRIBE)) {
+        tags |= ALLOW_SUBSCRIBE;
+    }
+    if (strstr(header, SIP_METHOD_UPDATE)) {
+        tags |= ALLOW_UPDATE;
+    }
+    return (tags);
+}
+
+/*
+ *  Function: sippmh_parse_accept_header
+ *
+ *  Parameters: contents of the Accept header
+ *
+ *  Description: This function will parse for the various capabilities
+ *               signalled in the Accept header
+ *
+ *  Returns: A bit map containing the values in the header
+ *
+ *  Format of the line is as follows:
+ *  Accept: application/sdp, nultipart/mixed, multipart/alternative
+ */
+uint16_t
+sippmh_parse_accept_header (const char *header)
+{
+    uint16_t tags = 0;
+
+    if (header == NULL) {
+        return (tags);
+    }
+    if (strstr(header, SIP_CONTENT_TYPE_SDP)) {
+        tags |= (0x01 << SIP_CONTENT_TYPE_SDP_VALUE);
+    }
+    if (strstr(header, SIP_CONTENT_TYPE_MULTIPART_MIXED)) {
+        tags |= (0x01 << SIP_CONTENT_TYPE_MULTIPART_MIXED_VALUE);
+    }
+    if (strstr(header, SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE)) {
+        tags |= (0x01 << SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE_VALUE);
+    }
+    return tags;
+}
+
+/*
+ *  Function: ccsip_process_network_message
+ *
+ *  Parameters: sipmsg, pointer to pointer of the buffer
+ *              bytes used, display message.
+ *
+ *  Description: Currently only used for tcp packets received to do the
+ *               framing. Ported from Propel.
+ *  Returns: A bit map containing the values in the header
+ */
+ccsipRet_e
+ccsip_process_network_message (sipMessage_t **sipmsg_p,
+                               char **buf,
+                               unsigned long *nbytes_used,
+                               char **display_msg)
+{
+    static const char fname[] = "ccsip_process_network_message";
+    sipMessage_t *sip_msg = NULL;
+    int local_nbytes = *nbytes_used;
+    uint32_t bytes_used;
+    char *local_buf_ptr;
+
+    sip_msg = sippmh_message_create();
+    if (sip_msg == NULL) {
+        CCSIP_ERR_DEBUG {
+            buginf("%s: Error in creating SIP Msg\n", fname);
+        }
+        *sipmsg_p = NULL;
+        return SIP_MSG_CREATE_ERR;
+    }
+
+    /* Init local variables */
+    bytes_used = local_nbytes;
+    local_buf_ptr = *buf;
+
+    if (sippmh_process_network_message(sip_msg, local_buf_ptr, &bytes_used)
+        == STATUS_FAILURE) {
+        CCSIP_ERR_DEBUG {
+            buginf("%s: process_network_message failed.\n", fname);
+        }
+        sippmh_message_free(sip_msg);
+        *sipmsg_p = NULL;
+        return SIP_MSG_PARSE_ERR;
+    }
+
+    if (sippmh_is_message_complete(sip_msg)) {
+        if (display_msg) {
+            *display_msg = (char *) cpr_malloc(bytes_used + 1);
+            if (*display_msg == NULL) {
+                CCSIP_ERR_DEBUG {
+                    buginf("%s: malloc of display msg failed.\n", fname);
+                }
+                sippmh_message_free(sip_msg);
+                *sipmsg_p = NULL;
+                return SIP_MSG_PARSE_ERR;
+            }
+            sstrncpy(*display_msg, local_buf_ptr, bytes_used + 1);
+        }
+        local_nbytes -= bytes_used;
+        local_buf_ptr += bytes_used;
+
+        /* Update information */
+        *sipmsg_p = sip_msg;
+        *nbytes_used = local_nbytes;
+        *buf = local_buf_ptr;
+
+        return SIP_SUCCESS;
+    } else {
+        CCSIP_ERR_DEBUG {
+            buginf("%s: process_network_msg: not complete\n", fname);
+        }
+        sippmh_message_free(sip_msg);
+        *sipmsg_p = NULL;
+
+        return SIP_MSG_INCOMPLETE_ERR;
+    }
+}
+
+/*
+ *  Function: sippmh_parse_service_control_body
+ *
+ *  Parameters: contents of the service control notify body and its length
+ *
+ *  Description: This function will parse for the reset/restart
+ *               instructions and parameters in this body
+ *
+ *  Returns: 0 if body parsed correctly
+ *           Parsed values are added to passed structure
+ *
+ *  Format of the body is as follows:
+ *            action = [reset | restart | check-version | call-preservation | apply-config ]
+ *            RegisterCallId= {<callid from register msg>}
+ *            ConfigVersionStamp = {79829A69-9489-4C8E-8143-90A9C22DFAD7}
+ *            DialplanVersionStamp = {79829A69-9489-4C8E-8143-90A9C22DFAD7}
+ *            SoftkeyVersionStamp = {79829A69-9489-4C8E-8143-90A9C22DFAD7}
+ *            CUCMResult=[no_change | config_applied | reregister_needed]
+ *            FirmwareLoadId = {SIP70.8-4-0-28S}
+ *            LoadServer={10.81.15.24}
+ *            LogServer={<ipv4 address or ipv6 address or fqdn> <port>} // This is used for ppid
+ *            UpgradeTime=[now | later]
+ *            PPID=[enabled | disabled]
+ */
+
+sipServiceControl_t *
+sippmh_parse_service_control_body (char *msgBody, int msgLength)
+{
+    pmhRstream_t *rs = NULL;
+    char *line = NULL, *value = NULL;
+    boolean body_read = FALSE;
+    sipServiceControl_t *scp = NULL;
+
+    if (msgLength==0) {
+        return (NULL);
+    }
+
+    if ((rs = pmhutils_rstream_create(msgBody, msgLength))
+            == NULL) {
+        return (NULL);
+    }
+
+    scp = (sipServiceControl_t *)
+        cpr_calloc(1, sizeof(sipServiceControl_t));
+    if (!scp) {
+        cpr_free(rs);
+        return NULL;
+    }
+
+    while (!body_read) {
+        line = pmhutils_rstream_read_line(rs);
+        if (line) {
+            value = strchr(line, '=');
+            if (value) {
+                value++;
+                while (*value == ' ') {
+                    value++;
+                }
+            }
+            if (strlen(line) == 0) {
+
+            } else if (!strncasecmp(line, "action", sizeof("action") - 1)) {
+                if (value == NULL) {
+                    scp->action = SERVICE_CONTROL_ACTION_INVALID;
+                } else if (!strncasecmp(value, "reset", sizeof("reset") - 1)) {
+                    scp->action = SERVICE_CONTROL_ACTION_RESET;
+                } else if (!strncasecmp(value, "restart",
+                                        sizeof("restart") - 1)) {
+                    scp->action = SERVICE_CONTROL_ACTION_RESTART;
+                } else if (!strncasecmp(value, "check-version",
+                                        sizeof("check-version") - 1)) {
+                    scp->action = SERVICE_CONTROL_ACTION_CHECK_VERSION;
+                } else if (!strncasecmp(value, "call-preservation",
+                                        sizeof("call-preservation") - 1)) {
+                    scp->action = SERVICE_CONTROL_ACTION_CALL_PRESERVATION;
+                } else if (!strncasecmp(value, "apply-config",
+                                        sizeof("apply-config") - 1)) {
+                    scp->action = SERVICE_CONTROL_ACTION_APPLY_CONFIG;
+
+                } else {
+                    scp->action = SERVICE_CONTROL_ACTION_INVALID;
+                }
+            } else if (!strncasecmp(line, "RegisterCallId",
+                                    sizeof("RegisterCallId") - 1)) {
+                if (scp->registerCallID == NULL) {
+                    if (value == NULL) {
+                        scp->registerCallID = NULL;
+                    } else if (value[0]) {
+                        int len = strlen(value);
+
+                        /* make sure curly braces are present */
+                        if ((*value == '{') && (*(value + len - 1) == '}')) {
+                            scp->registerCallID = (char *)
+                                cpr_calloc(1, len + 1);
+                            /* ignore the beginning and ending curly brace */
+                            if (scp->registerCallID)
+                                memcpy(scp->registerCallID, value + 1,
+                                       (len - 2));
+                        }
+                    }
+                }
+
+            } else if (!strncasecmp(line, "ConfigVersionStamp",
+                                    sizeof("ConfigVersionStamp") - 1)) {
+                if (scp->configVersionStamp == NULL) {
+                    if (value == NULL) {
+                        scp->configVersionStamp = NULL;
+                    } else if (value[0]) {
+                        int len = strlen(value);
+
+                        /* make sure curly braces are present */
+                        if ((*value == '{') && (*(value + len - 1) == '}')) {
+                            scp->configVersionStamp = (char *)
+                                cpr_calloc(1, len + 1);
+                            /* ignore the beginning and ending curly brace */
+                            if (scp->configVersionStamp)
+                                memcpy(scp->configVersionStamp, value + 1,
+                                       (len - 2));
+                        }
+                    }
+                }
+
+            } else if (!strncasecmp(line, "DialplanVersionStamp",
+                                    sizeof("DialplanVersionStamp") - 1)) {
+                if (scp->dialplanVersionStamp == NULL) {
+                    if (value == NULL) {
+                        scp->dialplanVersionStamp = NULL;
+                    } else if (value[0]) {
+                        int len = strlen(value);
+
+                        /* make sure curly braces are present */
+                        if ((*value == '{') && (*(value + len - 1) == '}')) {
+                            scp->dialplanVersionStamp = (char *)
+                                cpr_calloc(1, len + 1);
+                            /* ignore the beginning and ending curly brace */
+                            if (scp->dialplanVersionStamp)
+                                memcpy(scp->dialplanVersionStamp, value + 1,
+                                       (len - 2));
+                        }
+                    }
+                }
+            } else if (!strncasecmp(line, "SoftkeyVersionStamp",
+                                    sizeof("SoftkeyVersionStamp") - 1)) {
+                if (scp->softkeyVersionStamp == NULL) {
+                    if (value == NULL) {
+                        scp->softkeyVersionStamp = NULL;
+                    } else if (value[0]) {
+                        int len = strlen(value);
+
+                        /* make sure curly braces are present */
+                        if ((*value == '{') && (*(value + len - 1) == '}')) {
+                            scp->softkeyVersionStamp = (char *)
+                                cpr_calloc(1, len + 1);
+                            /* ignore the beginning and ending curly brace */
+                            if (scp->softkeyVersionStamp)
+                                memcpy(scp->softkeyVersionStamp, value + 1,
+                                       (len - 2));
+                        }
+                    }
+                }
+            } else if (!strncasecmp(line, "FeatureControlVersionStamp",
+                                    sizeof("FeatureControlVersionStamp") - 1)) {
+                if (scp->fcpVersionStamp == NULL) {
+                    if (value == NULL) {
+                        scp->fcpVersionStamp = NULL;
+                    } else if (value[0]) {
+                        int len = strlen(value);
+
+                        /* make sure curly braces are present */
+                        if ((*value == '{') && (*(value + len - 1) == '}')) {
+                            scp->fcpVersionStamp = (char *)
+                                cpr_calloc(1, len + 1);
+                            /* ignore the beginning and ending curly brace */
+                            if (scp->fcpVersionStamp)
+                                memcpy(scp->fcpVersionStamp, value + 1,
+                                       (len - 2));
+                        }
+                    }
+                }
+            }  else if (!strncasecmp(line, "CUCMResult",
+                                                 sizeof("CUCMResult") - 1)) {
+                if (value == NULL) {
+                    scp->cucm_result = NULL;
+                } else {
+                    int len =0;
+                    if ((strncasecmp(value, "no_change",
+                                         len = strlen("no_change")) == 0)  ||
+                        (strncasecmp(value, "config_applied",
+                                    len = strlen("config_applied")) == 0)  ||
+                        (strncasecmp(value, "reregister_needed",
+                                    len = strlen("reregister_needed")) == 0)) {
+                        scp->cucm_result = (char *) cpr_calloc(1,len + 1);
+                        if (scp->cucm_result) {
+                            sstrncpy(scp->cucm_result, value, len);
+                            scp->cucm_result[len] = '\0';
+                        }
+                    }
+                }
+            } else if (!strncasecmp(line, "LoadId",
+                                    sizeof("LoadId") - 1)) {
+                if (scp->firmwareLoadId == NULL) {
+                    if (value == NULL) {
+                        scp->firmwareLoadId = NULL;
+                    } else if (value[0]) {
+                        int len = strlen(value);
+
+                        /* make sure curly braces are present */
+                        if ((*value == '{') && (*(value + len - 1) == '}')) {
+                            scp->firmwareLoadId = (char *)
+                                cpr_calloc(1, len + 1);
+                            /* ignore the beginning and ending curly brace */
+                            if (scp->firmwareLoadId)
+                                memcpy(scp->firmwareLoadId, value + 1,
+                                       (len - 2));
+                        }
+                    }
+                }
+            } else if (!strncasecmp(line, "InactiveLoadId",
+                                    sizeof("InactiveLoadId") - 1)) {
+                if (scp->firmwareInactiveLoadId == NULL) {
+                    if (value == NULL) {
+                        scp->firmwareInactiveLoadId = NULL;
+                    } else if (value[0]) {
+                        int len = strlen(value);
+
+                        /* make sure curly braces are present */
+                        if ((*value == '{') && (*(value + len - 1) == '}')) {
+                            scp->firmwareInactiveLoadId = (char *)
+                                cpr_calloc(1, len + 1);
+                            /* ignore the beginning and ending curly brace */
+                            if (scp->firmwareInactiveLoadId)
+                                memcpy(scp->firmwareInactiveLoadId, value + 1,
+                                       (len - 2));
+                        }
+                    }
+                }
+           }  else if (!strncasecmp(line, "LoadServer",
+                                    sizeof("LoadServer") - 1)) {
+                if (scp->loadServer == NULL) {
+                    if (value == NULL) {
+                        scp->loadServer = NULL;
+                    } else if (value[0]) {
+                        int len = strlen(value);
+
+                        /* make sure curly braces are present */
+                        if ((*value == '{') && (*(value + len - 1) == '}')) {
+                            scp->loadServer = (char *)
+                                cpr_calloc(1, len + 1);
+                            /* ignore the beginning and ending curly brace */
+                            if (scp->loadServer)
+                                memcpy(scp->loadServer, value + 1,
+                                       (len - 2));
+                        }
+                    }
+                }
+           }  else if (!strncasecmp(line, "LogServer",
+                                    sizeof("LogServer") - 1)) {
+                if (scp->logServer == NULL) {
+                    if (value == NULL) {
+                        scp->logServer = NULL;
+                    } else if (value[0]) {
+                        int len = strlen(value);
+
+                        /* make sure curly braces are present */
+                        if ((*value == '{') && (*(value + len - 1) == '}')) {
+                            scp->logServer = (char *)
+                                cpr_calloc(1, len + 1);
+                            /* ignore the beginning and ending curly brace */
+                            if (scp->logServer)
+                                memcpy(scp->logServer, value + 1,
+                                       (len - 2));
+                        }
+                    }
+                }
+           }   else if (!strncasecmp(line, "PPID", sizeof("PPID") - 1)) {
+                if (value == NULL) {
+                    scp->ppid = FALSE;
+                } else if (!strncasecmp(value, "enabled", sizeof("enabled") - 1)) {
+                    scp->ppid = TRUE;
+                } else if (!strncasecmp(value, "disabled",
+                                        sizeof("disabled") - 1)) {
+                    scp->ppid = FALSE;
+                } else {
+                    scp->ppid = FALSE;
+                }
+
+           }
+           cpr_free(line);
+
+        } else {
+            body_read = TRUE;
+        }
+    }
+    // Free the created stream structure
+    pmhutils_rstream_delete(rs, FALSE);
+    cpr_free(rs);
+    return (scp);
+}
+
+void
+sippmh_free_service_control_info (sipServiceControl_t *scp)
+{
+    if (!scp) {
+        return;
+    }
+
+    if (scp->configVersionStamp)
+        cpr_free(scp->configVersionStamp);
+    if (scp->dialplanVersionStamp)
+        cpr_free(scp->dialplanVersionStamp);
+    if (scp->registerCallID)
+        cpr_free(scp->registerCallID);
+    if (scp->softkeyVersionStamp)
+        cpr_free(scp->softkeyVersionStamp);
+    if (scp->fcpVersionStamp)
+        cpr_free(scp->fcpVersionStamp);
+    if (scp->cucm_result)
+        cpr_free(scp->cucm_result);
+    if (scp->loadServer)
+        cpr_free(scp->loadServer);
+    if (scp->firmwareLoadId)
+        cpr_free(scp->firmwareLoadId);
+    if (scp->logServer)
+        cpr_free(scp->logServer);
+
+    cpr_free(scp);
+}
+
+int32_t
+sippmh_parse_max_forwards (const char *max_fwd_hdr)
+{
+    int32_t maxFwd;
+
+    if (max_fwd_hdr) {
+        if (isdigit(*max_fwd_hdr)) {
+            maxFwd = strtol(max_fwd_hdr, NULL, 10);
+            if ((maxFwd >= 0) && (maxFwd <= 255)) {
+                return maxFwd;
+            }
+        }
+    }
+    return -1;
+}
+
+/*
+ *  Function: sippmh_parse_url_from_hdr
+ *
+ *  Parameters: String that needs to be parsed
+ *
+ *  Description: This function will strip out the tags and
+ *                other characters in the header to retreive
+ *               a string that could be used as the request uri.
+ *               Note that the input string needs to be well formed.
+ *
+ *  Returns: A string containing the request uri
+ *
+ */
+string_t
+sippmh_get_url_from_hdr (char *input_str)
+{
+    char *left_bracket, *right_bracket = NULL;
+
+    /*
+      This function is only intended for properly
+      formed headers.
+    */
+    left_bracket = strpbrk(input_str, ",<");
+    if (left_bracket) {
+        left_bracket++;
+        right_bracket = strchr(left_bracket, '>');
+        if (right_bracket) {
+            *right_bracket = '\0';
+        }
+        input_str = left_bracket;
+    }
+    return (string_t)input_str;
+}
+/*
+ *  Function: sippmh_parse_join_header
+ *
+ *  Parameters: pointer to string of the join header contents
+ *
+ *  Returns: Parsed structure if parsed correctly, NULL otherwise
+ *
+ *  Format of the header is as follows:
+ *           Join: abcd;from-tag=efgh;to-tag=ijkl
+ */
+sipJoinInfo_t *
+sippmh_parse_join_header (const char *header)
+{
+    sipJoinInfo_t *join = NULL;
+    char          *semi = NULL;
+    unsigned int  param_len;
+    char          *params, *param_name, *param_value, *save_params;
+
+    if (!header) {
+        return NULL;
+    }
+
+    join = (sipJoinInfo_t *) cpr_calloc(1, sizeof(sipJoinInfo_t));
+    if (!join) {
+        return NULL;
+    }
+
+    // Read the call-id, if present
+    semi = strchr(header, SEMI_COLON);
+    if (semi) {
+        join->call_id = (char *) cpr_calloc(1, semi-header + 1);
+        if (join->call_id == NULL) {
+            sippmh_free_join_info(join);
+            return NULL;
+        }
+        sstrncpy(join->call_id, header, semi-header);
+    } else {
+        // call-id is the only parameter
+        join->call_id = cpr_strdup(header);
+        if (join->call_id == NULL) {
+            sippmh_free_join_info(join);
+            return NULL;
+        }
+        return (join);
+    }
+
+    params = cpr_strdup(semi);
+    if (!params) {
+        sippmh_free_join_info(join);
+        return NULL;
+    }
+    save_params = params;
+    while (1) {
+        while (*params == ';') {
+            params++;
+        }
+        param_name = params;
+        SKIP_SIP_TOKEN(params);
+        param_len = params - param_name;
+        if (param_len == 0) {
+            sippmh_free_join_info(join);
+            cpr_free(save_params);
+            return NULL;
+        }
+
+        /* Parse from-tag parameter */
+        if ((param_len == sizeof(SIP_HEADER_JOIN_FROM_TAG) - 1) &&
+            (strncasecmp(param_name, SIP_HEADER_JOIN_FROM_TAG,
+                         sizeof(SIP_HEADER_JOIN_FROM_TAG) - 1) == 0) &&
+            (join->from_tag == NULL)) {
+            params = parse_generic_param(params, &param_value);
+            if (params == NULL) {
+                sippmh_free_join_info(join);
+                cpr_free(save_params);
+                return NULL;
+            } else {
+                join->from_tag = (char *) cpr_calloc(1, params-param_value + 1);
+                if (join->from_tag) {
+                    sstrncpy(join->from_tag, param_value, params-param_value + 1);
+                }
+                SKIP_LWS(params);
+                if (*params == SEMI_COLON) {
+                    *params++ = '\0';
+                } else {
+                    break;
+                }
+
+            }
+
+        /* Parse to-tag parameter */
+        } else if ((param_len == sizeof(SIP_HEADER_JOIN_TO_TAG) - 1) &&
+                   (strncasecmp(param_name, SIP_HEADER_JOIN_TO_TAG,
+                                sizeof(SIP_HEADER_JOIN_TO_TAG) - 1) == 0) &&
+                   (join->to_tag == NULL)) {
+            params = parse_generic_param(params, &param_value);
+            if (params == NULL) {
+                sippmh_free_join_info(join);
+                cpr_free(save_params);
+                return NULL;
+            } else {
+                join->to_tag = (char *) cpr_calloc(1, params-param_value + 1);
+                if (join->to_tag) {
+                    sstrncpy(join->to_tag, param_value, params-param_value + 1);
+                }
+                if (*params == SEMI_COLON) {
+                    *params++ = '\0';
+                } else {
+                    break;
+                }
+            }
+
+        } else {
+            // Skip over unexpected parameter
+            SKIP_SIP_TOKEN(params);
+        }
+
+        SKIP_LWS(params);
+    }
+    cpr_free(save_params);
+    return (join);
+}
+
+void
+sippmh_free_join_info (sipJoinInfo_t *join)
+{
+
+    if (!join) {
+        return;
+    }
+    if (join->call_id)
+        cpr_free(join->call_id);
+    if (join->from_tag)
+        cpr_free(join->from_tag);
+    if (join->to_tag)
+        cpr_free(join->to_tag);
+    cpr_free(join);
+}
+
+sipRet_t
+sippmh_add_join_header (sipMessage_t *message, sipJoinInfo_t *join)
+{
+    char joinhdr[MAX_SIP_HEADER_LENGTH+1];
+    int  left;
+
+    // Write out the header in a buffer
+    if (!message) {
+        return STATUS_FAILURE;
+    }
+
+    snprintf(joinhdr, MAX_SIP_HEADER_LENGTH, "%s", join->call_id);
+    left = (uint16_t) MAX_SIP_HEADER_LENGTH - strlen(join->call_id);
+    if (join->from_tag && left > 0) {
+        sstrncat(joinhdr, ";from-tag=", left);
+        left -= sizeof(";from-tag=") - 1;
+        sstrncat(joinhdr, join->from_tag, left);
+        left -= strlen(join->from_tag);
+    }
+    if (join->to_tag && left > 0) {
+        sstrncat(joinhdr, ";to-tag=", left);
+        left -= sizeof(";to-tag=") - 1;
+        sstrncat(joinhdr, join->to_tag, left);
+    }
+    return (sippmh_add_text_header(message, SIP_HEADER_JOIN, joinhdr));
+}
+
+/*
+ *  Function: sippmh_parse_subscription_state
+ *
+ *  Parameters: contents of the Subscription-State header
+ *
+ *  Description: This function will parse for the state, expiry
+ *               time, and a reason code in the header
+ *
+ *  Returns: -1 if error, 0 if successfully parsed.
+ *
+ *  Format of the line is as follows:
+ *  Subscription-State: active;expires=7200;reason=whatever;retry-after=50
+ */
+int
+sippmh_parse_subscription_state(sipSubscriptionStateInfo_t *subsStateInfo,
+                                const char *subs_state)
+//TODO: how can subs_state be a const char [] when it can be modified below?
+{
+    char *ptr;
+    char temp[TEMP_PARSE_BUFFER_SIZE];
+    uint8_t i;
+
+    if (!subs_state) {
+        return (-1);
+    }
+
+    if (!strncasecmp(subs_state, SIP_SUBSCRIPTION_STATE_ACTIVE,
+                     sizeof(SIP_SUBSCRIPTION_STATE_ACTIVE) - 1)) {
+        subsStateInfo->state = SUBSCRIPTION_STATE_ACTIVE;
+    } else if (!strncasecmp(subs_state, SIP_SUBSCRIPTION_STATE_PENDING,
+                     sizeof(SIP_SUBSCRIPTION_STATE_PENDING) - 1)) {
+        subsStateInfo->state = SUBSCRIPTION_STATE_PENDING;
+    } else if (!strncasecmp(subs_state, SIP_SUBSCRIPTION_STATE_TERMINATED,
+                     sizeof(SIP_SUBSCRIPTION_STATE_PENDING) - 1)) {
+        subsStateInfo->state = SUBSCRIPTION_STATE_TERMINATED;
+    }
+
+    ptr = strchr(subs_state, ';');
+    if (!ptr) {
+        return (0);
+    }
+    SKIP_LWS(ptr);
+
+    // Look for expires tag
+    ptr = strstr(subs_state, SIP_SUBSCRIPTION_STATE_EXPIRES);
+    if (ptr) {
+        ptr = ptr + sizeof(SIP_SUBSCRIPTION_STATE_EXPIRES); // Go over the '='
+        SKIP_LWS(ptr);
+        if (*ptr) {
+            boolean is_int = FALSE;
+
+            memset(temp, 0, sizeof(temp));
+            i = 0;
+            while (isdigit(*ptr) && (i < sizeof(temp)-1)) {
+                temp[i++] = *ptr;
+                ptr++;
+                is_int = TRUE;
+            }
+            if (is_int == TRUE) {
+                subsStateInfo->expires = strtoul(temp, NULL, 10);
+            }
+        }
+    }
+    // Look for reason tag
+    ptr = strstr(subs_state, SIP_SUBSCRIPTION_STATE_REASON);
+    if (ptr) {
+        ptr = ptr + sizeof(SIP_SUBSCRIPTION_STATE_REASON);
+        SKIP_LWS(ptr);
+        if (*ptr) {
+            if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_DEACTIVATED,
+                             sizeof(SIP_SUBSCRIPTION_STATE_REASON_DEACTIVATED) - 1)) {
+                subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_DEACTIVATED;
+            } else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_PROBATION,
+                             sizeof(SIP_SUBSCRIPTION_STATE_REASON_PROBATION) - 1)) {
+                subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_PROBATION;
+            } else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_REJECTED,
+                             sizeof(SIP_SUBSCRIPTION_STATE_REASON_REJECTED) - 1)) {
+                subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_REJECTED;
+            } else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_TIMEOUT,
+                             sizeof(SIP_SUBSCRIPTION_STATE_REASON_TIMEOUT) - 1)) {
+                subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_TIMEOUT;
+            } else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_GIVEUP,
+                             sizeof(SIP_SUBSCRIPTION_STATE_REASON_GIVEUP) - 1)) {
+                subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_GIVEUP;
+            } else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_NORESOURCE,
+                             sizeof(SIP_SUBSCRIPTION_STATE_REASON_NORESOURCE) - 1)) {
+                subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_NORESOURCE;
+            } else {
+                subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_INVALID;
+            }
+        }
+    }
+
+    // Look for retry-after tag
+    ptr = strstr(subs_state, SIP_SUBSCRIPTION_STATE_RETRY_AFTER);
+    if (ptr) {
+        ptr = ptr + sizeof(SIP_SUBSCRIPTION_STATE_RETRY_AFTER); // Go over the '='
+        SKIP_LWS(ptr);
+        if (*ptr) {
+            boolean is_int = FALSE;
+
+            memset(temp, 0, sizeof(temp));
+            i = 0;
+            while (isdigit(*ptr) && (i < sizeof(temp)-1)) {
+                temp[i++] = *ptr;
+                ptr++;
+                is_int = TRUE;
+            }
+            if (is_int == TRUE) {
+                *ptr = '\0';
+                subsStateInfo->retry_after = strtoul(temp, NULL, 10);
+            }
+        }
+    }
+
+    return (0);
+}
+
+/*
+ *  Function: sippmh_add_subscription_state
+ *
+ *  Parameters: contents of the Subscription-State header
+ *
+ *  Description: This function will add the subscription state header
+ *
+ *  Returns: -1 if error, 0 if successfully added
+ *
+ *  Format of the line is as follows:
+ *  Subscription-State: active; expires=7200; reason=whatever
+ */
+int
+sippmh_add_subscription_state(sipMessage_t *msg,
+                              sipSubscriptionStateInfo_t *subsStateInfo)
+{
+    char subs_state[MAX_SUB_STATE_HEADER_SIZE];
+
+    if (!msg) {
+        return -1;
+    }
+
+    if ((subsStateInfo->state == SUBSCRIPTION_STATE_ACTIVE) && (subsStateInfo->expires == 0)) {
+        snprintf(&subs_state[0], MAX_SUB_STATE_HEADER_SIZE, "%s", SIP_SUBSCRIPTION_STATE_ACTIVE);
+    } else if (subsStateInfo->state == SUBSCRIPTION_STATE_ACTIVE) {
+        snprintf(&subs_state[0], MAX_SUB_STATE_HEADER_SIZE, "%s; expires=%d",
+                SIP_SUBSCRIPTION_STATE_ACTIVE,
+                subsStateInfo->expires);
+    } else if (subsStateInfo->state == SUBSCRIPTION_STATE_PENDING) {
+        snprintf(&subs_state[0], MAX_SUB_STATE_HEADER_SIZE, "%s; expires=%d",
+                SIP_SUBSCRIPTION_STATE_PENDING,
+                subsStateInfo->expires);
+    } else if (subsStateInfo->state == SUBSCRIPTION_STATE_TERMINATED) {
+        snprintf(&subs_state[0], MAX_SUB_STATE_HEADER_SIZE, "%s; reason=timeout",
+                SIP_SUBSCRIPTION_STATE_TERMINATED);
+    }
+
+    (void) sippmh_add_text_header(msg, SIP_HEADER_SUBSCRIPTION_STATE,
+                                  (const char *)&subs_state[0]);
+    return 0;
+}
+
+boolean
+sippmh_parse_kpml_event_id_params (char *params, char **call_id,
+                                   char **from_tag, char **to_tag)
+{
+    boolean params_good;
+    int param_len;
+    char *param_name;
+
+    if (params == NULL) {
+        return FALSE;
+    }
+
+    while (1) {
+        params_good = FALSE;
+
+        while (*params == ';') {
+            params++;
+        }
+
+        param_name = params;
+        SKIP_SIP_TOKEN(params);
+        param_len = params - param_name;
+        if (param_len == 0) {
+            return FALSE;
+        }
+
+        /* Parse call-id parameter */
+        if ((param_len == KPML_ID_CALLID_LEN) &&
+            (strncasecmp(param_name, KPML_ID_CALLID, KPML_ID_CALLID_LEN) == 0)) {
+            params = parse_generic_param(params, call_id);
+            if (params == NULL) {
+                return FALSE;
+            } else {
+                params_good = TRUE;
+            }
+
+        /* Parse from-tag parameter */
+        } else if ((param_len == KPML_ID_FROM_TAG_LEN) &&
+                   (strncasecmp(param_name, KPML_ID_FROM_TAG,
+                                KPML_ID_FROM_TAG_LEN) == 0)) {
+            params = parse_generic_param(params, from_tag);
+            if (params == NULL) {
+                return FALSE;
+            } else {
+                params_good = TRUE;
+            }
+
+        /* Parse to-tag parameter */
+        } else if ((param_len == KPML_ID_TO_TAG_LEN) &&
+                   (strncasecmp(param_name, KPML_ID_TO_TAG,
+                                KPML_ID_TO_TAG_LEN) == 0)) {
+            params = parse_generic_param(params, to_tag);
+            if (params == NULL) {
+                return FALSE;
+            } else {
+                params_good = TRUE;
+            }
+
+        }
+
+        SKIP_LWS(params);
+        if (*params == SEMI_COLON) {
+            /* More parameters follow */
+            *params++ = '\0';
+            SKIP_LWS(params);
+        } else {
+            break;
+        }
+    }
+
+    return params_good;
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_publish.c b/libs/sipcc/core/sipstack/ccsip_publish.c
new file mode 100644 (file)
index 0000000..acde663
--- /dev/null
@@ -0,0 +1,896 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ccsip_publish.h"
+#include "phntask.h"
+#include "sip_common_transport.h"
+#include "ccsip_task.h"
+#include "ccsip_callinfo.h"
+#include "ccsip_macros.h"
+#include "util_string.h"
+#include "cpr_rand.h"
+#include "platform_api.h"
+#include "ccsip_register.h"
+#include "ccsip_reldev.h"
+#include "debug.h"
+
+static sll_handle_t s_PCB_list = NULL; // signly linked list handle of PCBs
+static int outgoingPublishes;
+
+static boolean sipSPISendPublish(ccsip_publish_cb_t *pcb_p, boolean authen);
+static void free_pending_reqs (sll_handle_t list);
+
+/**
+ * This function will generate a new handle and return it.
+ *
+ * @return  a non-zero integer value
+ */
+static pub_handle_t generate_new_pub_handle (void)
+{
+    static pub_handle_t handle = 0;
+
+    handle++;
+    if (handle == NULL_PUBLISH_HANDLE) {
+        handle++;
+    }
+    return handle;
+}
+
+/**
+ * This function will check if it is the PCB we are looking for based on the key.
+ * This is only invoked by sll_find().
+ *
+ * @param[in] key - pointer to the key.
+ * @param[in] data - pointer to a node data.
+ *
+ * @return  SLL_MATCH_FOUND if the node matches.
+ *          Otherwise, SLL_MATCH_NOT_FOUND is returned.
+ *
+ *  @pre     (key != NULL) and (data != NULL)
+ */
+static sll_match_e is_matching_pcb (void *key, void *data)
+{
+    pub_handle_t pub_handle = *((pub_handle_t *)key);
+    ccsip_publish_cb_t *pcb_p = (ccsip_publish_cb_t *)data;
+
+    if (pub_handle == pcb_p->pub_handle) {
+        return SLL_MATCH_FOUND;
+    }
+    return SLL_MATCH_NOT_FOUND;
+}
+
+/**
+ * This function will create a PCB. It will also create the PCB linked list.
+ *
+ * @return  NULL if there are no resources to create a PCB.
+ *          Otherwise,  pointer to a new PCB is returned.
+ *
+ *  @pre     (key != NULL) and (data != NULL)
+ */
+static ccsip_publish_cb_t *get_new_pcb (void)
+{
+    ccsip_publish_cb_t *pcb_p;
+
+    /*
+     * If PCB list is not created yet, create the list.
+     */
+    if (s_PCB_list == NULL) {
+        s_PCB_list = sll_create(is_matching_pcb);
+        if (s_PCB_list == NULL) {
+            return NULL;
+        }
+    }
+
+    pcb_p = (ccsip_publish_cb_t *)cpr_malloc(sizeof(ccsip_publish_cb_t));
+    if (pcb_p == NULL) {
+        return NULL;
+    }
+    memset(pcb_p, 0, sizeof(ccsip_publish_cb_t));
+    pcb_p->pub_handle = generate_new_pub_handle();
+    pcb_p->hb.cb_type = PUBLISH_CB;
+    pcb_p->hb.dn_line = 1; // for now set it to primary line. This will change when we do line based PUBLISH.
+    /*
+     * set up dest & src ip addr and port number.
+     */
+    ccsip_common_util_set_dest_ipaddr_port(&pcb_p->hb);
+    ccsip_common_util_set_src_ipaddr(&pcb_p->hb);
+    pcb_p->hb.local_port = sipTransportGetListenPort(pcb_p->hb.dn_line, NULL);
+    pcb_p->retry_timer.timer = cprCreateTimer("PUBLISH retry timer",
+                                              SIP_PUBLISH_RETRY_TIMER,
+                                              TIMER_EXPIRATION,
+                                              sip_msgq);
+    if (pcb_p->retry_timer.timer == NULL) {
+        cpr_free(pcb_p);
+        return NULL;
+    }
+    pcb_p->pending_reqs = sll_create(NULL);
+    if (pcb_p->pending_reqs == NULL) {
+        (void)cprDestroyTimer(pcb_p->retry_timer.timer);
+        cpr_free(pcb_p);
+        return NULL;
+    }
+    (void) sll_append(s_PCB_list, pcb_p);
+
+    return pcb_p;
+}
+
+/**
+ * This function will find matching PCB by the key in the PCB list.
+ *
+ * @param[in] pub_handle - publish handle.
+ *
+ * @return  NULL if there is no matching PCB
+ *          Otherwise, pointer to the found PCB is returned.
+ *
+ *  @pre    (pub_handle > 0) and (s_PCB_list != NULL)
+ */
+static ccsip_publish_cb_t *find_pcb (pub_handle_t pub_handle)
+{
+    ccsip_publish_cb_t *pcb_p;
+
+    pcb_p = (ccsip_publish_cb_t *)sll_find(s_PCB_list, &pub_handle);
+
+    return pcb_p;
+}
+
+/**
+ * This function will find matching PCB by the SIP Call-ID in the PCB list.
+ *
+ * @param[in] callID_p - SIP Call-ID
+ *
+ * @return  NULL if there is no matching PCB
+ *          Otherwise, pointer to the found PCB is returned.
+ *
+ *  @pre    (callID_p != NULL)
+ */
+static ccsip_publish_cb_t *find_pcb_by_sip_callid (const char *callID_p)
+{
+    ccsip_publish_cb_t *pcb_p;
+
+    pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, NULL);
+    while (pcb_p != NULL) {
+        if (strncmp(callID_p, pcb_p->hb.sipCallID, (sizeof(pcb_p->hb.sipCallID) -1)) == 0) {
+            return pcb_p;
+        }
+        pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, pcb_p);
+    }
+    return NULL;
+}
+
+/**
+ * This function will free up the PCB resources and removes it from the PCB list.
+ *
+ * @param[in] pcb_p -  pointer to a PCB.
+ *
+ * @return  none
+ *
+ *  @pre    (pcb_p != NULL)
+ */
+static void free_pcb (ccsip_publish_cb_t *pcb_p)
+{
+   if (pcb_p->hb.authen.authorization != NULL) {
+       cpr_free(pcb_p->hb.authen.authorization);
+   }
+   if (pcb_p->hb.authen.sip_authen != NULL) {
+       sippmh_free_authen(pcb_p->hb.authen.sip_authen);
+   }
+
+    cpr_free(pcb_p->entity_tag);
+    free_pending_reqs(pcb_p->pending_reqs);
+    (void)cprDestroyTimer(pcb_p->retry_timer.timer);
+    free_event_data(pcb_p->hb.event_data_p);
+    (void)sll_remove(s_PCB_list, (void *)pcb_p);
+    cpr_free(pcb_p);
+}
+
+/**
+ * This function will append the application request in a pending list.
+ *
+ * @param[in] pcb_p -  pointer to a PCB.
+ * @param[in] msg_p -  pointer to an application request.
+ *
+ * @return  TRUE if it is successful.
+ *          Otherwise, FALSE is returned.
+ *
+ *  @pre    (pcb_p != NULL) and (msg_p != NULL)
+ *  @post   ((slink_list_t *)s_pres_req_list->count++)
+ */
+static boolean append_pending_reqs (ccsip_publish_cb_t *pcb_p, pub_req_t *msg_p)
+{
+    pub_req_t *temp_msg_p;
+
+    temp_msg_p = (pub_req_t *)cpr_malloc(sizeof(pub_req_t));
+    if (temp_msg_p == NULL) {
+        return FALSE;
+    }
+    (*temp_msg_p) = (*msg_p);
+    (void) sll_append(pcb_p->pending_reqs, temp_msg_p);
+    return TRUE;
+}
+
+/**
+ * This function will free up entire list of pending requests
+ *
+ * @param[in] list -  pending requests list handle
+ *
+ * @return none
+ *
+ */
+static void free_pending_reqs (sll_handle_t list)
+{
+    pub_req_t *msg_p;
+
+    if (list == NULL)  {
+        return;
+    }
+
+    msg_p = (pub_req_t *)sll_next(list, NULL);
+    while (msg_p != NULL) {
+        free_event_data(msg_p->event_data_p);
+        (void)sll_remove(list, (void *)msg_p);
+        cpr_free(msg_p);
+        msg_p = (pub_req_t *)sll_next(list, NULL);
+    }
+    sll_destroy(list);
+}
+
+/**
+ * This function will send the PUBLSIH request response to the application.
+ *
+ * @param[in] resp_code -  this also includes error responses that start at 1000
+ * @param[in] pub_handle - handle to a PCB
+ * @param[in] app_handle - application generated handle to this PUBLISH context.
+ * @param[in] callback_task - task in which the application resides.
+ * @param[in] resp_msg_id - messageID posted to the task.
+ *
+ * @return none
+ */
+static void send_resp_to_app (int resp_code, pub_handle_t pub_handle, pub_handle_t app_handle,
+                              cc_srcs_t callback_task, int resp_msg_id)
+{
+    static const char fname[] = "send_resp_to_app";
+    pub_rsp_t rsp;
+
+    rsp.resp_code = resp_code;
+    rsp.pub_handle = pub_handle;
+    rsp.app_handle = app_handle;
+    if (publish_int_response(&rsp, callback_task, resp_msg_id) != CC_RC_SUCCESS) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Failed to post PUBLISH response to the application", fname);
+    }
+}
+
+/**
+ * This function will process SIPSPI_EV_CC_PUBLISH posted by applications.
+ * If there is an outstanding transaction, it will hold the request in the pending list.
+ *
+ * @param[in] buf -  inter-process message buffer.
+ *
+ * @return SIP_OK if it successfully processes the request.
+ *         SIP_ERROR if it fails to process the request.
+ *         SIP_DEFER if it defers the processing.
+ *
+ * @note  This will not free buf.
+ *
+ * @pre    (buf != NULL)
+ */
+int publish_handle_ev_app_publish (cprBuffer_t buf)
+{
+    static const char fname[] = "publish_handle_ev_app_publish";
+    pub_req_t *msg_p = (pub_req_t *)buf;
+    ccsip_publish_cb_t *pcb_p;
+
+
+    /*
+     * If this is initial PUBLISH, allocate a PCB.
+     * Otherwise, look up for PCB based on pub_handle.
+     */
+    if (msg_p->pub_handle != NULL_PUBLISH_HANDLE) {
+        pcb_p = find_pcb(msg_p->pub_handle);
+
+        if (pcb_p == NULL) {
+            send_resp_to_app(PUBLISH_FAILED_NOCONTEXT, msg_p->pub_handle, msg_p->app_handle,
+                             msg_p->callback_task, msg_p->resp_msg_id);
+            free_event_data(msg_p->event_data_p);
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX
+                              "Modification PUBLISH cannot be sent as the PCB is missing\n",
+                              fname);
+            return SIP_ERROR;
+        }
+
+        /*
+         * Check if there is an outstanding transaction.
+         * if so, put the request in pending request queue.
+         */
+        if (pcb_p->outstanding_trxn == TRUE) {
+            if (append_pending_reqs(pcb_p, msg_p) == TRUE) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"deffering as there is an outstanding transaction\n",
+                                 DEB_F_PREFIX_ARGS(SIP_PUB, fname));
+                return SIP_DEFER;
+            }
+            /* free up PCB and respond with error */
+            free_pcb (pcb_p);
+            send_resp_to_app(PUBLISH_FAILED_NORESOURCE, msg_p->pub_handle, msg_p->app_handle,
+                             msg_p->callback_task, msg_p->resp_msg_id);
+            free_event_data(msg_p->event_data_p);
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Queueing outgoing PUBLISH request failed\n", fname);
+            return SIP_ERROR;
+        }
+        /*
+         * if event_data_p is NULL, this is terminating PUBLISH.
+         * otherwise, it is a modifying PUBLISH.
+         */
+        free_event_data(pcb_p->hb.event_data_p);
+        pcb_p->hb.event_data_p = msg_p->event_data_p;
+        if ((msg_p->event_data_p == NULL) && (msg_p->expires == 0)) { // removing PUBLISH
+            pcb_p->hb.orig_expiration = 0;
+        }
+    } else {
+        pcb_p = get_new_pcb();
+        if (pcb_p == NULL) {
+            send_resp_to_app(PUBLISH_FAILED_NORESOURCE, msg_p->pub_handle, msg_p->app_handle,
+                             msg_p->callback_task, msg_p->resp_msg_id);
+            free_event_data(msg_p->event_data_p);
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"PCB allocation failed\n", fname);
+            return SIP_ERROR;
+        }
+        pcb_p->app_handle = msg_p->app_handle;
+        sstrncpy(pcb_p->ruri, msg_p->ruri, MAX_URI_LENGTH);
+        sstrncpy(pcb_p->esc, msg_p->esc, MAX_URI_LENGTH);
+        pcb_p->hb.orig_expiration = msg_p->expires;
+        pcb_p->hb.event_type = msg_p->event_type;
+        pcb_p->hb.event_data_p = msg_p->event_data_p;
+        pcb_p->callback_task = msg_p->callback_task;
+        pcb_p->resp_msg_id = msg_p->resp_msg_id;
+    }
+
+    pcb_p->hb.authen.cred_type = 0;
+
+    if (sipSPISendPublish(pcb_p, FALSE) == TRUE) {
+        pcb_p->outstanding_trxn = TRUE;
+        outgoingPublishes++;
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"PUBLISH request sent successfully\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
+        return SIP_OK;
+    }
+
+    /* free up PCB and respond with error */
+    free_pcb (pcb_p);
+    send_resp_to_app(PUBLISH_FAILED_SEND, msg_p->pub_handle, msg_p->app_handle,
+                     msg_p->callback_task, msg_p->resp_msg_id);
+    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Failed to send PUBLISH request\n", fname);
+    return SIP_ERROR;
+
+}
+
+
+/**
+ * This function will create the PUBLISH message and send it. it also starts the retry timer.
+ *
+ * @param[in] pcb_p -  pointer to PCB
+ * @param[in] authen - boolean that indicates whether to add authorization header.
+ *
+ * @return TRUE if it successfully sent PUBLISH
+ *         Otherwise, FALSE is returned
+ *
+ * @pre    (pcb_p != NULL)
+ */
+static boolean sipSPISendPublish (ccsip_publish_cb_t *pcb_p, boolean authen)
+{
+    static const char fname[] = "sipSPISendPublish";
+    static uint32_t   cseq = 0;
+    char              dest_sip_addr_str[MAX_IPADDR_STR_LEN];
+    char             *domainloc;
+    char              src_addr_str[MAX_IPADDR_STR_LEN];
+    char              sip_temp_str[MAX_SIP_URL_LENGTH];
+    char              sip_temp_tag[MAX_SIP_URL_LENGTH];
+    uint8_t           mac_address[MAC_ADDRESS_LENGTH];
+    char              via[SIP_MAX_VIA_LENGTH];
+    int               max_forwards_value = 70;
+    static uint16_t   count = 1;
+    sipMessage_t     *request = NULL;
+    int               timeout = 0;
+
+    request = GET_SIP_MESSAGE();
+    if (!request) {
+        return FALSE;
+    }
+
+    /*
+     * Populate full RURI if it is not yet. Sometimes, applications may only provide user part.
+     */
+    if (pcb_p->full_ruri[0] == 0) {
+        sstrncpy(pcb_p->full_ruri, "sip:", MAX_SIP_URL_LENGTH);
+        sstrncat(pcb_p->full_ruri, pcb_p->ruri, MAX_SIP_URL_LENGTH - sizeof("sip:"));
+        /* check if it has host part */
+        domainloc = strchr(pcb_p->full_ruri, '@');
+        if (domainloc == NULL) {
+            domainloc = pcb_p->full_ruri + strlen(pcb_p->full_ruri);
+            if ((domainloc - pcb_p->full_ruri) < (MAX_SIP_URL_LENGTH - 1)) {
+                /* Do not include @ when there is no user part */
+                if (pcb_p->ruri[0] != '\0') {
+                    *domainloc++ = '@';
+                }
+                ipaddr2dotted(dest_sip_addr_str, &pcb_p->hb.dest_sip_addr);
+                sstrncpy(domainloc, dest_sip_addr_str,
+                         MAX_SIP_URL_LENGTH - (domainloc - (pcb_p->full_ruri)));
+            }
+        }
+    }
+
+    ipaddr2dotted(src_addr_str, &pcb_p->hb.src_addr);
+
+    // Add request line
+    if (HSTATUS_SUCCESS != sippmh_add_request_line(request,
+                                                   sipGetMethodString(sipMethodPublish),
+                                                   pcb_p->full_ruri, SIP_VERSION)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Request line\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // Add local Via
+    snprintf(via, sizeof(via), "SIP/2.0/%s %s:%d;%s=%s%.8x",
+             sipTransportGetTransportType(1, TRUE, NULL),
+             src_addr_str, pcb_p->hb.local_port, VIA_BRANCH,
+             VIA_BRANCH_START, (unsigned int) cpr_rand());
+    if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_VIA, via)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding VIA header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // Add To Header
+    snprintf(sip_temp_str, MAX_SIP_URL_LENGTH, "<%s>", pcb_p->full_ruri);
+    if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_TO, sip_temp_str)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding TO header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // Add From Header.
+    sstrncat(sip_temp_str, ";tag=", MAX_SIP_URL_LENGTH - strlen(sip_temp_str));
+    sip_util_make_tag(sip_temp_tag);
+    sstrncat(sip_temp_str, sip_temp_tag, MAX_SIP_URL_LENGTH - strlen(sip_temp_str));
+    if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_FROM, sip_temp_str)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding FROM header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // Add Call-ID Header.
+    platform_get_wired_mac_address(mac_address);
+    count++;
+    snprintf(pcb_p->hb.sipCallID, MAX_SIP_CALL_ID, "%.4x%.4x-%.4x%.4x-%.8x-%.8x@%s", // was MAX_SIP_URL_LENGTH
+                 mac_address[0] * 256 + mac_address[1],
+                 mac_address[2] * 256 + mac_address[3],
+                 mac_address[4] * 256 + mac_address[5], count,
+                 (unsigned int) cpr_rand(),
+                 (unsigned int) cpr_rand(),
+                 src_addr_str);
+    if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_CALLID, pcb_p->hb.sipCallID)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding CALLID header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // Add Contact header. Contact header is not needed as per RFC. BUT CCM needs it.
+    snprintf(sip_temp_str, MAX_SIP_URL_LENGTH, "<sip:%.4x%.4x%.4x@%s:%d>",
+             mac_address[0] * 256 + mac_address[1],
+             mac_address[2] * 256 + mac_address[3],
+             mac_address[4] * 256 + mac_address[5],
+             src_addr_str, pcb_p->hb.local_port);
+    if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_CONTACT, sip_temp_str)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Contact header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // Add Cseq.
+    cseq++;
+    if (cseq == 0) {
+        cseq = 1;
+    }
+    if (HSTATUS_SUCCESS != sippmh_add_cseq(request, sipGetMethodString(sipMethodPublish), cseq)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding CSEQ header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // Add UserAgent header
+    (void) sippmh_add_text_header(request, SIP_HEADER_USER_AGENT,
+                                  sipHeaderUserAgent);
+
+
+    // Add SIP-If-Match header.
+    if (pcb_p->entity_tag != NULL) {
+        if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_SIPIFMATCH,
+                                                      pcb_p->entity_tag)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Event header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    // Add Expires Header
+    if (HSTATUS_SUCCESS != sippmh_add_int_header(request, SIP_HEADER_EXPIRES,
+                pcb_p->hb.orig_expiration)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Expires header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // Add max-forwards header
+    config_get_value(CFGID_SIP_MAX_FORWARDS, &max_forwards_value,
+                     sizeof(max_forwards_value));
+    if (HSTATUS_SUCCESS !=
+        sippmh_add_int_header(request, SIP_HEADER_MAX_FORWARDS,
+            max_forwards_value)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Max-Forwards header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // add Authorization header
+    if (authen) {
+        if (HSTATUS_SUCCESS != sippmh_add_text_header(request, AUTHOR_HDR(pcb_p->hb.authen.status_code),
+                                                      pcb_p->hb.authen.authorization)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Authorization header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    // Add content, if any
+    if (pcb_p->hb.event_data_p) {
+        if (add_content(pcb_p->hb.event_data_p, request, fname) == FALSE) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Content\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    } else {
+        if (HSTATUS_SUCCESS != sippmh_add_int_header(request, SIP_HEADER_CONTENT_LENGTH, 0)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Content-Len\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    ccsip_common_util_set_retry_settings(&pcb_p->hb, &timeout);
+    if (sipTransportCreateSendMessage(NULL, request, sipMethodPublish,
+                                      &(pcb_p->hb.dest_sip_addr),
+                                      (int16_t) pcb_p->hb.dest_sip_port,
+                                      FALSE, TRUE, timeout, pcb_p,
+                                      RELDEV_NO_STORED_MSG) < 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send PUBLISH message\n", fname);
+        return (FALSE);
+    }
+
+    return (TRUE);
+
+}
+
+/**
+ * This function will handle retry-timer expiration
+ *
+ * @param[in] handle -  handle to PCB
+ *
+ * @return 0 if it is successful in handling the timer
+ *         Otherwise, -1 is returned
+ *
+ * @pre    (handle != 0)
+ */
+int publish_handle_retry_timer_expire (uint32_t handle)
+{
+    static const char fname[] = "publish_handle_retry_timer_expire";
+    pub_handle_t pub_handle = handle;
+    ccsip_publish_cb_t *pcb_p;
+    uint32_t        max_retx = 0;
+    uint32_t        time_t1 = 0;
+    uint32_t        time_t2 = 0;
+    uint32_t        timeout = 0;
+
+    /*
+     * find the PCB
+     */
+    pcb_p = find_pcb(pub_handle);
+    if (pcb_p == NULL) {
+        /* No PCB. So do nothing. */
+        return 0;
+    }
+    if (pcb_p->hb.retx_flag == FALSE) {
+        /* probably we got some response. so do nothing */
+        return 0;
+    }
+    config_get_value(CFGID_SIP_RETX, &max_retx, sizeof(max_retx));
+    if (max_retx > MAX_NON_INVITE_RETRY_ATTEMPTS) {
+        max_retx = MAX_NON_INVITE_RETRY_ATTEMPTS;
+    }
+    if (pcb_p->hb.retx_counter < max_retx) {
+        pcb_p->hb.retx_counter++;
+        config_get_value(CFGID_TIMER_T1, &time_t1, sizeof(time_t1));
+        timeout = time_t1 * (1 << pcb_p->hb.retx_counter);
+        config_get_value(CFGID_TIMER_T2, &time_t2, sizeof(time_t2));
+        if (timeout > time_t2) {
+            timeout = time_t2;
+        }
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Resending message #%d\n",
+                         DEB_F_PREFIX_ARGS(SIP_PUB, fname), pcb_p->hb.retx_counter);
+            if (sipTransportSendMessage(NULL,
+                                        pcb_p->retry_timer.message_buffer,
+                                        pcb_p->retry_timer.message_buffer_len,
+                                        pcb_p->retry_timer.message_type,
+                                        &(pcb_p->retry_timer.ipaddr),
+                                        pcb_p->retry_timer.port,
+                                        FALSE, TRUE, timeout, pcb_p) < 0) {
+                /* free up PCB and respond with error */
+                send_resp_to_app(PUBLISH_FAILED_SEND, pcb_p->pub_handle, pcb_p->app_handle,
+                                 pcb_p->callback_task, pcb_p->resp_msg_id);
+                free_pcb (pcb_p);
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send message", fname);
+                return (-1);
+            }
+
+    } else {
+        /*
+         * send timeout response and free up PCB
+         */
+        send_resp_to_app(SIP_CLI_ERR_REQ_TIMEOUT, pcb_p->pub_handle, pcb_p->app_handle,
+                         pcb_p->callback_task, pcb_p->resp_msg_id);
+        free_pcb (pcb_p);
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"reached MAX retries", fname);
+    }
+    return 0;
+}
+
+/**
+ * This function will check if wee need to send refresh PUBLISH.
+ * If so, it will send refresh PUBLISH
+ *
+ * @param[in] none
+ *
+ * @return none
+ */
+void publish_handle_periodic_timer_expire (void)
+{
+    static const char fname[] = "publish_handle_periodic_timer_expire";
+    int                 delta = 0;
+    ccsip_publish_cb_t *pcb_p;
+    pub_req_t           msg;
+
+    config_get_value(CFGID_TIMER_SUBSCRIBE_DELTA, &delta,
+                     sizeof(delta));
+    pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, NULL);
+    while (pcb_p != NULL) {
+        if (pcb_p->outstanding_trxn == FALSE) {
+            if (pcb_p->hb.expires >= TMR_PERIODIC_PUBLISH_INTERVAL) {
+                pcb_p->hb.expires -= TMR_PERIODIC_PUBLISH_INTERVAL;
+            }
+            if (pcb_p->hb.expires <= (delta + TMR_PERIODIC_PUBLISH_INTERVAL)) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"sending REFRESH PUBLISH", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
+                memset (&msg, 0, sizeof(msg));
+                /* refresh is triggered by NULL event data and non-zero expires value */
+                msg.pub_handle = pcb_p->pub_handle;
+                msg.expires = pcb_p->hb.orig_expiration;
+                (void)publish_handle_ev_app_publish(&msg);
+            }
+        }
+        pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, pcb_p);
+    }
+}
+
+/**
+ * This function will process the response to PUBLISH request sent by us.
+ *
+ * @param[in] pSipMessage -  pointer to received SIP response msg
+ *
+ * @return SIP_OK if it successfully processes the response.
+ *         SIP_ERROR if it fails to process the response.
+ *
+ * @pre    (pSipMessage != NULL)
+ */
+int publish_handle_ev_sip_response (sipMessage_t *pSipMessage)
+{
+    static const char fname[] = "publish_handle_ev_sip_response";
+    const char     *callID_p = NULL;
+    int             response_code = 0;
+    const char     *expires = NULL;
+    const char     *sip_etag = NULL;
+    long            expiry_time;
+    pub_req_t      *msg_p;
+    ccsip_publish_cb_t *pcb_p;
+    int entity_tag_size;
+
+    callID_p = sippmh_get_cached_header_val(pSipMessage, CALLID);
+    if (!callID_p) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Cannot obtain SIP Call ID.\n", fname);
+        return SIP_ERROR;
+    }
+
+    /*
+     * Find PCB by Call-ID. The Call-IDs generated by the phone ae unique.
+     */
+    pcb_p = find_pcb_by_sip_callid(callID_p);
+    if (pcb_p == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No matching PCB found\n", fname);
+        return SIP_ERROR;
+    }
+
+    /*
+     * Cancel the retry_timer and set the retx_flag to FALSE.
+     */
+    sip_platform_msg_timer_subnot_stop(&(pcb_p->retry_timer));
+    pcb_p->hb.retx_flag = FALSE;
+
+    // Parse the return code
+    (void) sipGetResponseCode(pSipMessage, &response_code);
+
+    if (response_code >= 200) {
+        pcb_p->outstanding_trxn = FALSE;
+    }
+
+
+    if ((response_code == SIP_CLI_ERR_UNAUTH) ||
+        (response_code == SIP_CLI_ERR_PROXY_REQD)) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Authentication Required\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
+        if (ccsip_common_util_generate_auth(pSipMessage, &pcb_p->hb, SIP_METHOD_PUBLISH,
+                                            response_code, pcb_p->full_ruri) == TRUE) {
+            if (sipSPISendPublish(pcb_p, TRUE) == TRUE) {
+                pcb_p->outstanding_trxn = TRUE;
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"sent request with Auth header\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
+                return SIP_OK;
+             }
+        }
+        /*
+         * Since we failed to resend the PUBLISH request, free up the PCB and let the app know.
+         */
+        send_resp_to_app(PUBLISH_FAILED_SEND, pcb_p->pub_handle, pcb_p->app_handle,
+                         pcb_p->callback_task, pcb_p->resp_msg_id);
+        free_pcb (pcb_p);
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to respond to auth challenge\n", fname);
+        return SIP_ERROR;
+    }
+
+    /*
+     * if response code is 423, grab Min-Expires and send new PUBLISH with new expires value
+     */
+    if (response_code == SIP_CLI_ERR_INTERVAL_TOO_SMALL) {
+        expires = sippmh_get_header_val(pSipMessage,
+                                        (const char *)SIP_HEADER_MIN_EXPIRES,
+                                        NULL);
+        if (expires) {
+            expiry_time = strtoul(expires, NULL, 10);
+            //ensure new Min-Expires is > what we set before in Expires
+            if ((long) expiry_time > pcb_p->hb.expires) {
+                pcb_p->hb.expires = expiry_time;
+                pcb_p->hb.orig_expiration = expiry_time;
+            }
+            if (sipSPISendPublish(pcb_p, FALSE) == TRUE) {
+                pcb_p->outstanding_trxn = TRUE;
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"sent request with increased expires\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
+                return SIP_OK;
+             }
+        }
+        /*
+         * Since we failed to resend the PUBLISH request, free up the PCB and let the app know.
+         */
+        send_resp_to_app(PUBLISH_FAILED_SEND, pcb_p->pub_handle, pcb_p->app_handle,
+                         pcb_p->callback_task, pcb_p->resp_msg_id);
+        free_pcb (pcb_p);
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to respond to 423\n", fname);
+        return SIP_ERROR;
+    }
+
+    /*
+     * if the response_code is > 299, free up the PCB and let the app know.
+     */
+    if (response_code > 299) {
+        send_resp_to_app(response_code, pcb_p->pub_handle, pcb_p->app_handle,
+                         pcb_p->callback_task, pcb_p->resp_msg_id);
+        free_pcb (pcb_p);
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"received %d response\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname), response_code);
+        return SIP_OK;
+    }
+
+    /*
+     * if the response is < 200, do nothing.
+     */
+    if (response_code < 200) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"received %d response\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname), response_code);
+        return SIP_OK;
+    }
+
+    /*
+     * If it is PUBLISH remove operation, free up PCB
+     */
+    if (pcb_p->hb.orig_expiration == 0) {
+        send_resp_to_app(response_code, pcb_p->pub_handle, pcb_p->app_handle,
+                         pcb_p->callback_task, pcb_p->resp_msg_id);
+        free_pcb (pcb_p);
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"removed PCB as this was a terminating PUBLISH\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
+        return SIP_OK;
+    }
+
+    /*
+     * extract Expires and SIP-ETag headers and save them.
+     */
+    expires = sippmh_get_header_val(pSipMessage, SIP_HEADER_EXPIRES, NULL);
+    if (expires) {
+        expiry_time = strtoul(expires, NULL, 10);
+        pcb_p->hb.expires = expiry_time;
+    }
+    sip_etag = sippmh_get_header_val(pSipMessage, SIP_HEADER_SIPETAG, NULL);
+    if (sip_etag != NULL) {
+        cpr_free(pcb_p->entity_tag);
+        entity_tag_size = strlen(sip_etag) + 1;
+        pcb_p->entity_tag = cpr_malloc(entity_tag_size);
+        if (pcb_p->entity_tag != NULL) {
+            sstrncpy(pcb_p->entity_tag, sip_etag, entity_tag_size);
+        } else {
+            free_pcb (pcb_p);
+            send_resp_to_app(PUBLISH_FAILED_NORESOURCE, pcb_p->pub_handle, pcb_p->app_handle,
+                             pcb_p->callback_task, pcb_p->resp_msg_id);
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"memory allocation failed\n", fname);
+            return SIP_ERROR;
+
+        }
+    }
+
+    /*
+     * If there are no pending requests, provide the response to the application.
+     */
+    msg_p = (pub_req_t *)sll_next(pcb_p->pending_reqs, NULL);
+    if (msg_p != NULL) {
+        (void)sll_remove(pcb_p->pending_reqs, msg_p);
+        (void)publish_handle_ev_app_publish(msg_p);
+        cpr_free(msg_p);
+        return SIP_OK;
+    }
+    send_resp_to_app(response_code, pcb_p->pub_handle, pcb_p->app_handle,
+                     pcb_p->callback_task, pcb_p->resp_msg_id);
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"sent response %d to app\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname), response_code);
+    return SIP_OK;
+}
+
+/**
+ * This function will inform the application that phone is either
+ * 1. restarting or
+ * 2. failing over/ falling back
+ *
+ * @note detection of CCM reboot will be handled by PUBLISH ETag mechanism.
+ *
+ * @param[in] none
+ *
+ * @return none
+ */
+void publish_reset (void)
+{
+    ccsip_publish_cb_t *pcb_p;
+
+    pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, NULL);
+    while (pcb_p != NULL) {
+        send_resp_to_app(PUBLISH_FAILED_RESET, pcb_p->pub_handle, pcb_p->app_handle,
+                         pcb_p->callback_task, pcb_p->resp_msg_id);
+        free_pcb(pcb_p);
+        pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, NULL);
+    }
+}
+
+/**
+ * This function will print the stats (invoked by show command)
+ *
+ * @param[in] none
+ *
+ * @return 0 always
+ */
+cc_int32_t show_publish_stats (cc_int32_t argc, const char *argv[])
+{
+    debugif_printf("------ Current PUBLISH Statistics ------\n");
+    if (s_PCB_list != NULL) {
+        debugif_printf("Number of PCBs allocated: %d\n", sll_count(s_PCB_list));
+    } else {
+        debugif_printf("Number of PCBs allocated: 0\n");
+    }
+    debugif_printf("Total outgoing PUBLISH requests: %d\n", outgoingPublishes);
+    return 0;
+}
+
diff --git a/libs/sipcc/core/sipstack/ccsip_register.c b/libs/sipcc/core/sipstack/ccsip_register.c
new file mode 100644 (file)
index 0000000..8f79d62
--- /dev/null
@@ -0,0 +1,3079 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_timers.h"
+#include "cpr_string.h"
+#include "cpr_memory.h"
+#include "cpr_ipc.h"
+#include "cpr_in.h"
+#include "util_string.h"
+#include "task.h"
+#include "phntask.h"
+#include "ccsip_core.h"
+#include "ccsip_messaging.h"
+#include "phone_debug.h"
+#include "ccsip_platform.h"
+#include "ccsip_platform_timers.h"
+#include "ccsip_macros.h"
+#include "ccsip_pmh.h"
+#include "ccsip_register.h"
+#include "ccsip_task.h"
+#include "ccsip_credentials.h"
+#include "debug.h"
+#include "logmsg.h"
+#include "ccsip_pmh.h"
+#include "ccsip_credentials.h"
+#include "dns_utils.h"
+#include "config.h"
+#include "sip_common_transport.h"
+#include "uiapi.h"
+#include "sip_common_regmgr.h"
+#include "text_strings.h"
+#include "sip_interface_regmgr.h"
+#include "phone_platform_constants.h"
+#include "ccsip_common_cb.h"
+#include "misc_util.h"
+
+extern sipPlatformUITimer_t sipPlatformUISMTimers[];
+extern void *new_standby_available;
+extern boolean regall_fail_attempt;
+extern boolean registration_reject;
+
+extern void ui_set_sip_registration_state(line_t line, boolean registered);
+extern void ui_update_registration_state_all_lines(boolean registered);
+
+boolean dump_reg_msg = TRUE;
+boolean refresh_reg_msg = FALSE;
+
+static ccsip_register_states_t ccsip_register_state = SIP_REG_IDLE;
+
+/* Need an additional ack timer for the backup proxy */
+static cprTimer_t ack_tmrs[MAX_REG_LINES + 1];
+ccm_act_stdby_table_t CCM_Active_Standby_Table;
+extern ccm_failover_table_t CCM_Failover_Table;
+static boolean start_standby_monitor = TRUE;
+extern boolean Is794x;
+
+static void show_register_data(void);
+
+#define SIP_REG_TMR_EXPIRE_TICKS 55000 /* msec; re-registration timer */
+#define REGISTER_CMD "register"
+#define SIP_REG_TMR_ACK_TICKS    32000 /* msec; supervision timer equivalent to Timer F */
+
+static const char *ccsip_register_state_names[] = {
+    "IDLE",
+    "REGISTERING",
+    "REGISTERED",
+    "UNREGISTERING",
+    "PRE_FALLBACK",
+    "IN_FAILOVER",
+    "POST_FAILOVER",
+    "STANDBY_FAILOVER",
+    "NO_CC",
+    "NO_STANDBY",
+    "NO_REGISTER"
+};
+
+static const char *ccsip_register_reg_state_names[] = {
+    "NONE",
+    "IDLE",
+    "REGISTERING",
+    "REGISTERED",
+    "UNREGISTERING",
+    "PRE_FALLBACK",
+    "IN_FAILOVER",
+    "POST_FAILOVER",
+    "STANDBY_FAILOVER",
+    "NO_CC",
+    "NO_STANDBY",
+    "NO_REGISTER"
+};
+
+
+void ccsip_handle_ev_reg_req(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_reg_cancel(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_unreg_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_1xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_3xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_4xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_failure_response(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_tmr_expire(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_tmr_ack(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_tmr_retry(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_unreg_tmr_ack(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_standby_keepalive_tmr_expire(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_idle_tmr_expire(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+const char *ccsip_register_state_name(ccsip_register_states_t state);
+const char *ccsip_register_reg_state_name(sipRegSMStateType_t state);
+const char *sip_util_reg_event2string(sipRegSMEventType_t event);
+char *sip_util_reg_state2string(sipRegSMStateType_t state);
+void ccsip_register_retry_timer_start(ccsipCCB_t *ccb);
+void ccsip_register_cleanup(ccsipCCB_t *ccb, boolean start);
+cc_int32_t ccsip_register_cmd(cc_int32_t argc, const char *argv[]);
+void ccsip_register_clear_all_logs(void);
+boolean ccsip_register_all_registered(void);
+
+/* convert sip line to reg line  */
+#define SIP_REG_LINE2REGLINE(line)  ((line) - (REG_CCB_START))
+
+/* convert reg line to sip line  */
+#define SIP_REG_REGLINE2LINE(line)  ((line) + (REG_CCB_START))
+
+// Date holder
+static struct {
+    boolean valid;
+    char datestring[MAX_SIP_DATE_LENGTH];
+} ccm_date;
+
+static const sipSMEventActionFn_t
+gSIPRegSMTable[SIP_REG_STATE_END - SIP_REG_STATE_BASE + 1]
+              [SIPSPI_REG_EV_END - SIPSPI_REG_EV_BASE + 1] =
+{
+    /* SIP_REG_STATE_IDLE
+     */
+    {
+     /* E_SIP_REG_REG_REQ          */ ccsip_handle_ev_reg_req,
+     /* E_SIP_REG_CANCEL           */ ccsip_handle_ev_default,
+     /* E_SIP_REG_1xx              */ ccsip_handle_ev_default,
+     /* E_SIP_REG_2xx              */ ccsip_handle_ev_default,
+     /* E_SIP_REG_3xx              */ ccsip_handle_ev_default,
+     /* E_SIP_REG_4xx              */ ccsip_handle_ev_default,
+     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_default,
+     /* E_SIP_REG_TMR_ACK          */ ccsip_handle_ev_default,
+     /* E_SIP_REG_TMR_EXPIRE       */ ccsip_handle_ev_idle_tmr_expire,
+     /* E_SIP_REG_TMR_WAIT         */ ccsip_handle_ev_default,
+     /* E_SIP_REG_TMR_RETRY        */ ccsip_handle_ev_default,
+     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
+     },
+
+    /* SIP_REG_STATE_REGISTERING
+     */
+    {
+     /* E_SIP_REG_REG_REQ          */ ccsip_handle_ev_reg_req,
+     /* E_SIP_REG_CANCEL           */ ccsip_handle_ev_reg_cancel,
+     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
+     /* E_SIP_REG_2xx              */ ccsip_handle_ev_2xx,
+     /* E_SIP_REG_3xx              */ ccsip_handle_ev_3xx,
+     /* E_SIP_REG_4xx              */ ccsip_handle_ev_4xx,
+     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_failure_response,
+     /* E_SIP_REG_TMR_ACK          */ ccsip_handle_ev_tmr_ack,
+     /* E_SIP_REG_TMR_EXPIRE       */ ccsip_handle_ev_tmr_expire,
+     /* E_SIP_REG_TMR_WAIT         */ ccsip_handle_ev_default,
+     /* E_SIP_REG_TMR_RETRY        */ ccsip_handle_ev_tmr_retry,
+     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
+     },
+
+    /* SIP_REG_STATE_REGISTERED
+     */
+    {
+     /* E_SIP_REG_REG_REQ          */ ccsip_handle_ev_default,
+     /* E_SIP_REG_CANCEL           */ ccsip_handle_ev_reg_cancel,
+     /* E_SIP_REG_1xx              */ ccsip_handle_ev_default,
+     /* E_SIP_REG_2xx              */ ccsip_handle_ev_default,
+     /* E_SIP_REG_3xx              */ ccsip_handle_ev_default,
+     /* E_SIP_REG_4xx              */ ccsip_handle_ev_default,
+     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_default,
+     /* E_SIP_REG_TMR_ACK          */ ccsip_handle_ev_default,
+     /* E_SIP_REG_TMR_EXPIRE       */ ccsip_handle_ev_tmr_expire,
+     /* E_SIP_REG_TMR_WAIT         */ ccsip_handle_ev_default,
+     /* E_SIP_REG_TMR_RETRY        */ ccsip_handle_ev_tmr_retry,
+     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
+     },
+
+    /* SIP_REG_STATE_UNREGISTERING
+     */
+    {
+     /* E_SIP_REG_REG_REQ          */ ccsip_handle_ev_default,
+     /* E_SIP_REG_CANCEL           */ ccsip_handle_ev_reg_cancel,
+     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
+     /* E_SIP_REG_2xx              */ ccsip_handle_ev_unreg_2xx,
+     /* E_SIP_REG_3xx              */ ccsip_handle_ev_3xx,
+     /* E_SIP_REG_4xx              */ ccsip_handle_ev_4xx,
+     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_failure_response,
+     /* E_SIP_REG_TMR_ACK          */ ccsip_handle_ev_unreg_tmr_ack,
+     /* E_SIP_REG_TMR_EXPIRE       */ ccsip_handle_ev_default,
+     /* E_SIP_REG_TMR_WAIT         */ ccsip_handle_ev_standby_keepalive_tmr_expire,
+     /* E_SIP_REG_TMR_RETRY        */ ccsip_handle_ev_tmr_retry,
+     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
+     },
+
+    /* SIP_REG_STATE_IN_FALLBACK
+     */
+    {
+     /* E_SIP_REG_REG_REQ          */ sip_regmgr_ev_default,
+     /* E_SIP_REG_CANCEL           */ sip_regmgr_ev_cancel,
+     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
+     /* E_SIP_REG_2xx              */ sip_regmgr_ev_in_fallback_2xx,
+     /* E_SIP_REG_3xx              */ sip_regmgr_ev_default,
+     /* E_SIP_REG_4xx              */ sip_regmgr_ev_fallback_retry,
+     /* E_SIP_REG_FAILURE_RESPONSE */ sip_regmgr_ev_fallback_retry,
+     /* E_SIP_REG_TMR_ACK          */ sip_regmgr_ev_tmr_ack_retry,
+     /* E_SIP_REG_TMR_EXPIRE       */ sip_regmgr_ev_default,
+     /* E_SIP_REG_TMR_WAIT         */ sip_regmgr_ev_default,
+     /* E_SIP_REG_TMR_RETRY        */ sip_regmgr_ev_tmr_ack_retry,
+     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
+     },
+    /* SIP_REG_STATE_STABILITY_CHECK
+     */
+    {
+     /* E_SIP_REG_REG_REQ          */ sip_regmgr_ev_default,
+     /* E_SIP_REG_CANCEL           */ sip_regmgr_ev_cancel,
+     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
+     /* E_SIP_REG_2xx              */ sip_regmgr_ev_stability_check_2xx,
+     /* E_SIP_REG_3xx              */ sip_regmgr_ev_default,
+     /* E_SIP_REG_4xx              */ sip_regmgr_ev_default,
+     /* E_SIP_REG_FAILURE_RESPONSE */ sip_regmgr_ev_default,
+     /* E_SIP_REG_TMR_ACK          */ sip_regmgr_ev_tmr_ack_retry,
+     /* E_SIP_REG_TMR_EXPIRE       */ sip_regmgr_ev_default,
+     /* E_SIP_REG_TMR_WAIT         */ sip_regmgr_ev_stability_check_tmr_wait,
+     /* E_SIP_REG_TMR_RETRY        */ sip_regmgr_ev_tmr_ack_retry,
+     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
+     },
+    /* SIP_REG_STATE_TOKEN_WAIT
+     */
+    {
+     /* E_SIP_REG_REG_REQ          */ sip_regmgr_ev_default,
+     /* E_SIP_REG_CANCEL           */ sip_regmgr_ev_default,
+     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
+     /* E_SIP_REG_2xx              */ sip_regmgr_ev_token_wait_2xx,
+     /* E_SIP_REG_3xx              */ sip_regmgr_ev_default,
+     /* E_SIP_REG_4xx              */ ccsip_handle_ev_4xx,
+     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_failure_response,
+     /* E_SIP_REG_TMR_ACK          */ sip_regmgr_ev_tmr_ack_retry,
+     /* E_SIP_REG_TMR_EXPIRE       */ sip_regmgr_ev_default,
+     /* E_SIP_REG_TMR_WAIT         */ sip_regmgr_ev_token_wait_tmr_wait,
+     /* E_SIP_REG_TMR_RETRY        */ sip_regmgr_ev_tmr_ack_retry,
+     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_cleanup,
+     }
+};
+
+/*
+ * This function gets the supported option tags from the 200 OK response
+ * to the REG message sent.
+ */
+void
+sip_get_supported_options_2xx (ccsipCCB_t *ccb, sipMessage_t *response)
+{
+    const char *supported;
+
+    ccb->supported_tags = 0;
+    supported = sippmh_get_cached_header_val(response, SUPPORTED);
+    if (supported != NULL) {
+        /*
+         * Supported header is found, find the interrested supported
+         * function from 2xx response from the REG msg. sent. Note that
+         * the sippmh_parse_supported_require() can be used but
+         * decided to minimize the impact on performance parsing all other
+         * tags that are not interested during registration times and
+         * vice versa making changes to sippmh_parse_supported_require()
+         * to parse the tags that are not interested for other messages
+         * also impact the processing time for other messages.
+         */
+        if (strcasestr(supported, REQ_SUPP_PARAM_CISCO_SRTP_FALLBACK)) {
+            /* cisco SRTP fallback is supported by the other end */
+            ccb->supported_tags |= cisco_srtp_fallback_tag;
+        }
+    }
+}
+
+/*
+ * This function starts the Register message
+ * ack timer. If no response is received from
+ * the register message within four minutes,
+ * this timer will pop and resend the Register
+ * message.
+ */
+void
+sip_start_ack_timer (ccsipCCB_t *ccb)
+{
+    uint16_t ack_timer_index;
+
+    if (ccb->index == REG_BACKUP_CCB) {
+        ack_timer_index = MAX_REG_LINES;
+    } else {
+        ack_timer_index = ccb->dn_line - 1;
+    }
+
+    CCSIP_DEBUG_REG_STATE( DEB_L_C_F_PREFIX " ccb->index=%d ack_timer_index=%d ",
+        DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->dn_line, 0, "sip_start_ack_timer"),
+        ccb->index, ack_timer_index);
+
+    if (cprStartTimer(ack_tmrs[ack_timer_index], SIP_REG_TMR_ACK_TICKS,
+                      (void *)(long)ccb->index) == CPR_FAILURE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          "sip_start_ack_timer", "cprStartTimer");
+    }
+}
+
+
+/*
+ * This function stops the Register message
+ * ack timer.
+ */
+void
+sip_stop_ack_timer (ccsipCCB_t *ccb)
+{
+    uint16_t ack_timer_index;
+
+    if (ccb->index == REG_BACKUP_CCB) {
+        ack_timer_index = MAX_REG_LINES;
+    } else {
+        ack_timer_index = ccb->dn_line - 1;
+    }
+
+    CCSIP_DEBUG_REG_STATE( DEB_L_C_F_PREFIX " ccb->index=%d ack_timer_index=%d ",
+        DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->dn_line, 0, "sip_stop_ack_timer"),
+        ccb->index, ack_timer_index);
+
+    if (cprCancelTimer(ack_tmrs[ack_timer_index]) == CPR_FAILURE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          "sip_stop_ack_timer", "cprCancelTimer");
+    }
+}
+
+
+void
+sip_reg_sm_change_state (ccsipCCB_t *ccb, sipRegSMStateType_t new_state)
+{
+    if (g_disable_mass_reg_debug_print == FALSE) {
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Registration state change: %s ---> "
+                          "%s\n", DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->index, ccb->dn_line, "sip_reg_sm_change_state"),
+                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
+                          sip_util_reg_state2string(new_state));
+    }
+    ccb->state = (sipSMStateType_t) new_state;
+
+    if (ccb->index == REG_CCB_START) {
+        if ((new_state > SIP_REG_STATE_REGISTERED) || (!refresh_reg_msg)) {
+            dump_reg_msg = TRUE;
+        } else {
+            dump_reg_msg = FALSE;
+        }
+    }
+}
+
+
+void
+ccsip_handle_ev_reg_req (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "ccsip_handle_ev_reg_req";
+    char line_name[MAX_LINE_NAME_SIZE];
+    int value;
+
+    config_get_value(CFGID_PROXY_REGISTER, &value, sizeof(value));
+    if (value == 0) {
+        ui_set_sip_registration_state(ccb->dn_line, FALSE);
+        CCSIP_DEBUG_REG_STATE(get_debug_string(DEBUG_REG_DISABLED),
+                              ccb->index, ccb->dn_line, fname);
+
+        return;
+    }
+
+    ccsip_register_clear_all_logs();
+
+    sip_stop_ack_timer(ccb);
+    sip_start_ack_timer(ccb);
+
+    (void) sip_platform_register_expires_timer_stop(ccb->index);
+
+
+    sip_util_get_new_call_id(ccb);
+
+    ccb->authen.cred_type = 0;
+    ccb->retx_counter = 0;
+    config_get_line_string(CFGID_LINE_NAME, line_name, ccb->dn_line,
+                           sizeof(line_name));
+
+    config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &ccb->reg.tmr_expire,
+                     sizeof(ccb->reg.tmr_expire));
+    ccb->reg.act_time = (int) time(NULL);
+
+    if (sipSPISendRegister(ccb, 0, line_name, ccb->reg.tmr_expire) != TRUE) {
+        // set expire timer and cleanup ccb in non-ccm mode.
+        // do not change the expire timer value in ccm mode
+        if (ccb->cc_type != CC_CCM) {
+            ccsip_register_cleanup(ccb, TRUE);
+        }
+        log_clear(LOG_REG_MSG);
+        log_msg(LOG_REG_MSG);
+    }
+
+    sip_reg_sm_change_state(ccb, SIP_REG_STATE_REGISTERING);
+}
+
+
+
+void
+ccsip_handle_ev_default (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    if ((event->type == (int) E_SIP_REG_CANCEL) && (ccb->state == (int) SIP_REG_STATE_IDLE)) {
+        (void) sip_platform_register_expires_timer_stop(ccb->index);
+        ccb->authen.cred_type = 0;
+        ccb->retx_counter     = 0;
+        ccb->reg.tmr_expire   = 0;
+        ccb->reg.act_time     = 0;
+        ccsip_register_cleanup(ccb, FALSE);
+    }
+
+    /* only free SIP messages, timeouts are internal */
+    if (event->type < (int) E_SIP_REG_TMR_ACK) {
+        free_sip_message(event->u.pSipMessage);
+    }
+}
+
+
+void
+ccsip_handle_ev_tmr_expire (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    // Initiate registration if prime line or not in failover state
+    if ((!CCM_Failover_Table.failover_started) ||
+        (ccb->index == REG_CCB_START)) {
+        /*If the phone is registered with CSPS, the failover_started flag
+         * will always be false. Therefore, this code will always be
+         * executed. */
+
+        /* Cleanup and re-initialize CCB */
+        sip_sm_call_cleanup(ccb);
+
+        refresh_reg_msg = TRUE;
+        /* send a message to the SIP_REG SM to initiate registration */
+        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, TRUE);
+        }
+    } else {
+        ccsip_handle_ev_default(ccb, event);
+    }
+}
+
+void
+ccsip_handle_ev_idle_tmr_expire (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+
+    if (ccb->cc_type == CC_CCM) {
+        if (ccb->index == REG_BACKUP_CCB) {
+            ccsip_handle_ev_tmr_expire(ccb, event);
+        } else {
+            ccsip_handle_ev_default(ccb, event);
+        }
+    } else {
+        /* assuming CSPS */
+        /* Cleanup and re-initialize CCB */
+        sip_sm_call_cleanup(ccb);
+
+        /* send a message to the SIP_REG SM to initiate registration */
+        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, TRUE);
+        }
+    }
+}
+
+
+void
+ccsip_handle_ev_tmr_ack (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    log_clear(LOG_REG_AUTH_ACK_TMR);
+    log_msg(LOG_REG_AUTH_ACK_TMR);
+
+    if (ccb->cc_type == CC_CCM) {
+        /*
+         * regmgr - Send tmr ack event to the regmgr/
+         */
+        sip_regmgr_ev_tmr_ack_retry(ccb, event);
+    } else {
+        /*
+         * regmgr - Assume it is csps
+         */
+        /* Cleanup and re-initialize CCB */
+        sip_sm_call_cleanup(ccb);
+        /* send a message to the SIP_REG SM to initiate registration */
+        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, TRUE);
+        }
+    }
+}
+
+
+void
+ccsip_handle_ev_1xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "ccsip_handle_ev_1xx";
+    sipMessage_t   *response = NULL;
+    int             status_code = 0;
+    char            status[LOG_MAX_LEN];
+
+    response = event->u.pSipMessage;
+
+    if (sipGetResponseCode(response, &status_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE),
+                          ccb->index, ccb->dn_line, fname);
+        return;
+    }
+
+    free_sip_message(response);
+
+    switch (status_code) {
+    case SIP_1XX_TRYING:
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
+                          "SIP", status_code);
+        return;
+
+    default:
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2), ccb->index,
+                          ccb->dn_line, fname,
+                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
+                          "SIP BAD", status_code);
+
+        snprintf(status, sizeof(status), "in %d, information", status_code);
+        log_clear(LOG_REG_UNSUPPORTED);
+        log_msg(LOG_REG_UNSUPPORTED, status);
+
+        ccsip_register_cleanup(ccb, TRUE);
+
+        return;
+    }
+}
+
+static boolean
+ccsip_get_exp_time_2xx (ccsipCCB_t *ccb, sipContact_t *contact_info,
+                        const char *expires, uint32_t *exp_time_ret)
+{
+    static const char fname[] = "ccsip_get_exp_time_2xx";
+    boolean         exp_found = FALSE;
+    char            src_addr_str[MAX_IPADDR_STR_LEN];
+    char            user[MAX_LINE_NAME_SIZE];
+    int             i;
+    uint32_t        gmt_time; //TODO convert to time_t
+    //uint32_t          diff_time; //TODO convert to time_t
+    int32_t         gmt_rc;
+    uint32_t        exp_time;
+    uint32_t        register_delta;
+
+    /* did the server change the expires time? */
+
+    /*
+     * Form the contact header.
+     * The proxy can return multiple contacts so we will need to see which one
+     * matches this REGISTER.
+     */
+    config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &exp_time, sizeof(uint32_t));
+    if (contact_info != NULL) {
+        ipaddr2dotted(src_addr_str, &ccb->src_addr);
+        config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line,
+                               sizeof(user));
+
+        /* is expires header for this line included in the contact? */
+        for (i = 0; i < contact_info->num_locations; i++) {
+            if ((sippmh_cmpURLStrings(contact_info->locations[i]->genUrl->u.sipUrl->user,
+                                      user, FALSE) == 0) &&
+                (strcmp(contact_info->locations[i]->genUrl->u.sipUrl->host,
+                        src_addr_str) == 0)) {
+                if ((contact_info->params[i].expires > 0) &&
+                    (contact_info->params[i].expires < exp_time)) {
+                    exp_time = contact_info->params[i].expires;
+                    exp_found = TRUE;
+                    CCSIP_DEBUG_REG_STATE(get_debug_string(DEBUG_REG_PROXY_EXPIRES),
+                                          ccb->index, ccb->dn_line, fname);
+                    break;
+                }
+            }
+        }
+    }
+
+
+    if (exp_found != TRUE) {
+        //expires param is not found in the CONTACT header
+        // look for Expires header.
+        config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &exp_time,
+                         sizeof(exp_time));
+        if (expires) {
+            gmt_rc = gmt_string_to_seconds((char *)expires,
+                                           (unsigned long *)&gmt_time);
+            if (gmt_rc != -1) {
+                // We only want to update the expires timeout if it is lower
+                // than our predefined threshold.  We don't want to allow
+                // people to keep us hung up for infinite periods of time
+                if (gmt_rc == 1) {
+                    // We got a numeric entry in the expires field
+                    if (gmt_time < exp_time) {
+                        exp_time = gmt_time;
+                        exp_found = TRUE;
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * Subtract some user defined period of time to account for network delay
+     * or the possibility that the registration server is down and this
+     * request has to be processed by a backup server after the timeout
+     * occurs. If the user has not defined this value in the config, it
+     * defaults to 5 seconds.
+     */
+    config_get_value(CFGID_TIMER_REGISTER_DELTA, &register_delta,
+                     sizeof(register_delta));
+
+    /*
+     * Sanity check the register delta value as the proxy could have returned
+     * a much lower registration period in its' response. The minimum
+     * registration expiration is 60 seconds to prevent the phone from being
+     * flooded with msgs.
+     */
+  if ((exp_time - register_delta) < MIN_REGISTRATION_PERIOD) {
+        CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX
+                "Warning - Registration period received (%d) "
+                "minus configured timer_register_delta (%d) is less than "
+                "%d seconds.\n", DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname), exp_time,
+                register_delta, MIN_REGISTRATION_PERIOD);
+        exp_found = FALSE;
+    } else {
+        exp_time = exp_time - register_delta;
+        exp_found = TRUE;
+    }
+    *exp_time_ret = exp_time;
+    return (exp_found);
+}
+
+/*
+ *  Function: ccsip_check_ccm_restarted
+ *
+ *  Parameters:
+ *      new_reg_ccb  - pointer to ccsipCCB_t of REG CCB.
+ *      contact_info - Pointer to sipContact_t.
+ *
+ *  Description:
+ *      The function detects CCM just came up i.e. it sees the phone
+ *  registration as a new registration. This means CCM has restarted
+ *  between a REG refresh cycle.
+ *
+ *  Note:
+ *      CCM only sends x-cisco-newreg to the first REG that it sees
+ *  from the phone as a new REG. If the phone has more than one lines,
+ *  the REG of the rest of the lines will not have the x-cisco-newreg.
+ *
+ *  Returns:
+ *      TRUE - CCM restarted is detected.
+ *     FALSE - CCM restarted is not detected.
+ */
+static boolean
+ccsip_check_ccm_restarted (sipContact_t *contact_info)
+{
+    int i;
+
+    if (contact_info == NULL) {
+        /* No contact info. */
+        return (FALSE);
+    }
+
+    for (i = 0; i < contact_info->num_locations; i++) {
+        if (contact_info->params[i].flags & SIP_CONTACT_PARM_X_CISCO_NEWREG) {
+            /*
+             * CCM or proxy just indicates that it sees the REG
+             * as a new registration to it.  This means that the CCM or
+             * proxy has restarted.
+             */
+            return (TRUE);
+        }
+    }
+    return (FALSE);
+}
+
+/*
+ *  Function: update_sis_protocol_version
+ *
+ *  Parameters:
+ *      response - 200 OK message pointer
+ *
+ *  Description:
+ *      The function parses theupported header in response and updates
+ *  SIS protocol version received. If no version is received the default
+ *  SEADRAGON version gets updated
+ *
+ *  Returns:
+ *     void
+ */
+
+void
+update_sis_protocol_version (sipMessage_t *response)
+{
+    const char *supported;
+    char * sipver;
+    supported = sippmh_get_cached_header_val(response, SUPPORTED);
+    if (supported != NULL) {
+       sipver = strcasestr(supported, REQ_SUPP_PARAM_CISCO_SISTAG);
+       if (sipver) {
+          cc_uint32_t major = SIS_PROTOCOL_MAJOR_VERSION_SEADRAGON, minor = 0, addtnl = 0;
+          if ( sscanf ( &sipver[strlen(REQ_SUPP_PARAM_CISCO_SISTAG)], "%d.%d.%d",
+                                &major, &minor, &addtnl) == 3) {
+            platSetSISProtocolVer ( major, minor, addtnl,REQ_SUPP_PARAM_CISCO_SISTAG);
+            return;
+          }
+        }
+    }
+    /* All other cases we set the version to 1.0.0 */
+    platSetSISProtocolVer ( SIS_PROTOCOL_MAJOR_VERSION_SEADRAGON, 0, 0,REQ_SUPP_PARAM_CISCO_SISTAG);
+}
+
+void
+update_cme_sis_version (sipMessage_t *response)
+{
+    const char *supported;
+    char * sipver;
+
+    supported = sippmh_get_cached_header_val(response, SUPPORTED);
+    if (supported != NULL) {
+      sipver = strcasestr(supported, REQ_SUPP_PARAM_CISCO_CME_SISTAG);
+      if (sipver) {
+        cc_uint32_t major = 0, minor = 0, addtnl = 0;
+        if ( sscanf( &sipver[strlen(REQ_SUPP_PARAM_CISCO_CME_SISTAG)], "%d.%d.%d",
+                            &major, &minor, &addtnl) ==3) {
+          platSetSISProtocolVer(major, minor, addtnl,REQ_SUPP_PARAM_CISCO_CME_SISTAG);
+        }
+      }
+    }
+}
+
+void
+ccsip_handle_ev_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "ccsip_handle_ev_2xx";
+    sipMessage_t   *response = event->u.pSipMessage;
+    const char     *pViaHeaderStr = NULL;
+    sipVia_t       *via = NULL;
+    uint32_t        exp_time;
+    //uint32_t        line_feature;
+    int             dns_err_code;
+    int             nat_enable = 0;
+    int             nat_rx_proc_enable = 0;
+    //int            last_button_index =0;
+    const char     *datehdr = NULL;
+    const char     *contact = NULL;
+    const char     *expires = NULL;
+    sipContact_t   *contact_info = NULL;
+    //line_t          line_index = 0;
+    //ccsipCCB_t     *line_ccb = NULL;
+
+
+    // Extract Date header and store
+    datehdr = sippmh_get_header_val(response, SIP_HEADER_DATE, NULL);
+    if (datehdr) {
+        // Copy date header in a global variable since it would be needed
+        // to set system time
+        sstrncpy(ccm_date.datestring, datehdr, sizeof(ccm_date.datestring));
+        ccm_date.valid = TRUE;
+
+        // call ntp set time handler (TNP/cnu specific; stub for legacy)
+        // do not call the handler if 2xx is from StdBy CCM
+        // We do not need to set the time when in the browser
+        //if (ccb->index != REG_BACKUP_CCB) {
+        //    SipNtpUpdateClockFromCCM();
+        //}
+    }
+
+    contact = sippmh_get_cached_header_val(response, CONTACT);
+    if (contact != NULL) {
+        /* There is a contact header pass it */
+        contact_info = sippmh_parse_contact(contact);
+    }
+
+       /* Update the sip interface protocol version */
+       update_sis_protocol_version(response);
+
+    /*update the CME for the version negotiation feature */
+    update_cme_sis_version(response);
+
+    if (ccb->dn_line == PRIMARY_LINE && ccsip_check_ccm_restarted(contact_info)) {
+        /* CCM has restarted detected */
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Detected server has restarted via line %d\n",
+                            DEB_F_PREFIX_ARGS(SIP_CCM_RESTART, fname), ccb->dn_line);
+        sip_regmgr_ccm_restarted(ccb);
+    }
+
+    sip_stop_ack_timer(ccb);
+    clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+
+    if (ccb->send_reason_header) {
+        ccb->send_reason_header = FALSE;
+        sipUnregisterReason[0] = '\0';
+    }
+
+    /*
+     * Get expiration time. The expriation time are from contact info. or
+     * from the expire header.
+     */
+    expires = sippmh_get_header_val(response, SIP_HEADER_EXPIRES, NULL);
+    if (ccsip_get_exp_time_2xx(ccb, contact_info, expires, &exp_time)
+            == FALSE) {
+        /*
+         * The expire time received in 200OK is smaller than the minimum
+         * allowed.  The phone will be unregistered and a message will be
+         * displayed under the Status Messages.  The phone will retry to
+         * register after the configured expire-timer interval.
+         */
+        config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &exp_time,
+                         sizeof(uint32_t));
+        ccb->reg.tmr_expire = exp_time;
+        ccb->reg.act_time = (int) time(NULL);
+        sip_reg_sm_change_state(ccb, SIP_REG_STATE_UNREGISTERING);
+
+        if (ccsip_register_send_msg(SIP_REG_CANCEL, ccb->index)
+            != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, FALSE);
+        }
+        (void) sip_platform_register_expires_timer_start(ccb->reg.tmr_expire * 1000,
+                                                         ccb->index);
+        if (contact_info != NULL) {
+            sippmh_free_contact(contact_info);
+        }
+        free_sip_message(response);
+        log_clear(LOG_REG_EXPIRE);
+        log_msg(LOG_REG_EXPIRE);
+        return;
+    }
+
+
+           ccb->reg.registered = 1;
+           sip_reg_sm_change_state(ccb, SIP_REG_STATE_REGISTERED);
+           regall_fail_attempt = FALSE;
+
+           if (ccb->index != REG_BACKUP_CCB) {
+                  registration_reject = FALSE;
+               ui_set_sip_registration_state(ccb->dn_line, TRUE);
+               CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Setting Reg state to TRUE for line=%d\n",
+                              DEB_F_PREFIX_ARGS(SIP_REG_STATE, fname), ccb->dn_line);
+              /* Get supported options from the supported header from the response */
+               sip_get_supported_options_2xx(ccb, response);
+           } else {
+               log_clear(LOG_REG_BACKUP);
+           }
+
+
+    if (contact_info != NULL) {
+        sippmh_free_contact(contact_info);
+    }
+
+    ccb->reg.tmr_expire = exp_time;
+
+    ccb->reg.act_time = (int) time(NULL);
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Starting expires timer (%d "
+                          "sec)\n", DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->index, ccb->dn_line, fname),
+                          ccb->reg.tmr_expire);
+    (void) sip_platform_register_expires_timer_start(ccb->reg.tmr_expire * 1000,
+                                                     ccb->index);
+
+
+    if (ccb->cc_type == CC_CCM) {
+        /*
+         * regmgr - A 200 OK will reach here only if the state is
+         * REGISTERING, the unreg_200ok eve is handled
+         * separately.
+         */
+        ti_config_table_t *ccm_table_ptr;
+
+        ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+
+        if ((ccm_table_ptr == CCM_Active_Standby_Table.active_ccm_entry) &&
+                start_standby_monitor) {
+            ccsipCCB_t     *standby_ccb;
+
+            start_standby_monitor = FALSE;
+            sip_platform_set_ccm_status();
+
+            /*
+             * Kickoff monitoring the standby
+             */
+
+            standby_ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+            if (CCM_Active_Standby_Table.standby_ccm_entry) {
+                config_get_value(CFGID_TIMER_REGISTER_EXPIRES,
+                                 &exp_time, sizeof(uint32_t));
+                standby_ccb->reg.tmr_expire = exp_time;
+                standby_ccb->reg.act_time = (int) time(NULL);
+                sip_reg_sm_change_state(standby_ccb,
+                                        SIP_REG_STATE_UNREGISTERING);
+                (void) ccsip_register_send_msg(SIP_REG_CANCEL, standby_ccb->index);
+                (void) sip_platform_register_expires_timer_start(standby_ccb->reg.tmr_expire * 1000,
+                                                                 standby_ccb->index);
+            }
+        } else if (ccm_table_ptr ==
+                   CCM_Active_Standby_Table.standby_ccm_entry) {
+            /*
+             * REGMGR - Update stats Got a 200OK for a expires 0
+             * Keepalive msg to standby, update stats ?
+             */
+        }
+    }
+
+
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    config_get_value(CFGID_NAT_RECEIVED_PROCESSING, &nat_rx_proc_enable,
+                     sizeof(nat_rx_proc_enable));
+    if (nat_enable == 1 && nat_rx_proc_enable == 1) {
+        pViaHeaderStr = sippmh_get_cached_header_val(response, VIA);
+        if (pViaHeaderStr != NULL) {
+            via = sippmh_parse_via(pViaHeaderStr);
+            if (via != NULL) {
+                if (via->recd_host != NULL) {
+                    cpr_ip_addr_t received_ip;
+
+                    memset(&received_ip, 0, sizeof(cpr_ip_addr_t));
+                    dns_err_code = dnsGetHostByName(via->recd_host,
+                                                       &received_ip, 100, 1);
+                    if (dns_err_code == 0) {
+                        util_ntohl(&received_ip, &received_ip);
+                    } else {
+                        sip_config_get_nat_ipaddr(&received_ip);
+                    }
+
+                    if (util_compare_ip(&received_ip, &(ccb->src_addr)) == FALSE) {
+                        /*
+                         * The address the proxy/registration server received
+                         * is not the same as the one that we think we have
+                         * so we'll update our address and re-register with
+                         * the proxy/registration server
+                         */
+
+                        ccb->reg.rereg_pending = 1;
+
+                        // Make sure our running config has the correct
+                        // IP address that we got from the proxy server
+                        sip_config_set_nat_ipaddr(&received_ip);
+
+                        if (ccsip_register_send_msg(SIP_REG_CANCEL, ccb->index)
+                            != SIP_REG_OK) {
+                            ccsip_register_cleanup(ccb, FALSE);
+                        }
+
+                        // Update the tel ccb that this reg line belongs to
+                        ccb = sip_sm_get_ccb_by_index((line_t)(ccb->index - REG_CCB_START + 1));
+                        if (ccb) {
+                            ccb->src_addr = received_ip;
+                        }
+                    }
+                }
+
+                // Cleanup memory
+                sippmh_free_via(via);
+            }
+        }
+    }
+
+    free_sip_message(response);
+}
+
+void
+ccsip_handle_ev_3xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "ccsip_handle_ev_3xx";
+    sipMessage_t   *response = NULL;
+    int             status_code = 0;
+    char            status[LOG_MAX_LEN];
+    char            user[MAX_LINE_NAME_SIZE];
+    sipUrl_t       *url;
+    const char     *contact = NULL;
+
+    response = event->u.pSipMessage;
+    clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+
+    if (sipGetResponseCode(response, &status_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE),
+                          ccb->index, ccb->dn_line, fname);
+        free_sip_message(response);
+        return;
+    }
+
+    switch (status_code) {
+    case SIP_RED_MULT_CHOICES /* 300 */:
+    case SIP_RED_MOVED_PERM   /* 301 */:
+    case SIP_RED_MOVED_TEMP   /* 302 */:
+    case SIP_RED_USE_PROXY    /* 305 */:
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
+                          "SIP", status_code);
+
+        sip_sm_200and300_update(ccb, response, status_code);
+
+        /*
+         * Record the Contact header.  For 3xx messages we are recording
+         * the Contact header here, not in the sipSPICheckResponse() header,
+         * so that the ACK response to 3xx is sent to the original INVITE
+         * destination (or to the Record-Route specified destination), rather
+         * than to the new 302 Contact
+         */
+        contact = sippmh_get_cached_header_val(response, CONTACT);
+        if (contact) {
+            if (ccb->contact_info) {
+                sippmh_free_contact(ccb->contact_info);
+            }
+            ccb->contact_info = sippmh_parse_contact(contact);
+        }
+
+
+        /*
+         * Check the validity of Contact header
+         */
+        if (!ccb->contact_info) {
+            CCSIP_DEBUG_ERROR("%s: Error: Invalid Contact field.  Aborting "
+                              "REGISTER.\n", fname);
+            free_sip_message(response);
+            ccsip_register_cleanup(ccb, TRUE);
+            return;
+        }
+
+        if (ccb->contact_info->locations[0]->genUrl->schema == URL_TYPE_SIP) {
+            url = ccb->contact_info->locations[0]->genUrl->u.sipUrl;
+        } else {
+            CCSIP_DEBUG_ERROR("%s: Error: URL is not Sip.  Aborting "
+                              "REGISTER.\n", fname);
+            free_sip_message(response);
+            ccsip_register_cleanup(ccb, TRUE);
+            return;
+        }
+
+        sstrncpy(ccb->reg.proxy, url->host, MAX_IPADDR_STR_LEN);
+        ccb->reg.port = url->port;
+
+        if (ccb->contact_info) {
+            sippmh_free_contact(ccb->contact_info);
+            ccb->contact_info = NULL;
+        }
+
+        sip_util_get_new_call_id(ccb);
+
+        /*
+         * Send out a new REGISTER
+         */
+        ccb->authen.cred_type = 0;
+        config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line,
+                               sizeof(user));
+
+        if (sipSPISendRegister(ccb, 0, user, ccb->reg.tmr_expire) != TRUE) {
+            ccsip_register_cleanup(ccb, TRUE);
+            free_sip_message(response);
+
+            log_clear(LOG_REG_RED_MSG);
+            log_msg(LOG_REG_RED_MSG);
+        }
+        return;
+
+    default:
+        free_sip_message(event->u.pSipMessage);
+
+        snprintf(status, sizeof(status), "in %d, redirection", status_code);
+        log_clear(LOG_REG_UNSUPPORTED);
+        log_msg(LOG_REG_UNSUPPORTED, status);
+
+        ccsip_register_cleanup(ccb, TRUE);
+    }
+}
+
+
+void
+ccsip_handle_ev_4xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "ccsip_handle_ev_4xx";
+    sipMessage_t   *response = NULL;
+    int             status_code = 0;
+    const char     *authenticate = NULL;
+    char            user[MAX_LINE_NAME_SIZE];
+    credentials_t   credentials;
+    char            status[LOG_MAX_LEN];
+    sip_authen_t   *sip_authen = NULL;
+    char           *author_str = NULL;
+    char            uri[MAX_IPADDR_STR_LEN + 10]; /* Proxy IP address string */
+    char            tmp_str[STATUS_LINE_MAX_LEN];
+    ti_config_table_t *ccm_table_ptr = NULL;
+
+    ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+
+
+    response = event->u.pSipMessage;
+    clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+
+    if (sipGetResponseCode(response, &status_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE),
+                          ccb->index, ccb->dn_line, fname);
+        free_sip_message(response);
+        return;
+    }
+
+    /*
+     * Set reject flag if we get 4xx from publisher cucm.
+     * Note: if non-prime dn is congfigured with #, such as 1234##,
+     * then phone will get 404 for that DN, even though the prime DN is
+     * registered.
+     */
+    if (ccm_table_ptr &&
+        ccb->index != REG_BACKUP_CCB) {
+       DEF_DEBUG(DEB_F_PREFIX"Receive 4xx in ccm mode. set registration_reject to TRUE.\n",
+            DEB_F_PREFIX_ARGS(SIP_REG, fname));
+       registration_reject = TRUE;
+    }
+
+    switch (status_code) {
+    case SIP_CLI_ERR_UNAUTH:
+    case SIP_CLI_ERR_PROXY_REQD:
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
+                          "SIP", status_code);
+
+        if (cred_get_credentials_r(ccb, &credentials) == FALSE) {
+            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"no more credentials, retry %d, %d\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_CRED, ccb->index, ccb->dn_line, fname),
+                                  ccb->authen.retries_401_407,
+                                  MAX_RETRIES_401);
+
+
+            if (ccb->cc_type == CC_CCM) {
+                sip_regmgr_ev_failure_response(ccb, event);
+                free_sip_message(response);
+                return;
+            }
+            ccsip_register_cleanup(ccb, TRUE);
+            free_sip_message(response);
+
+            log_clear(LOG_REG_AUTH_NO_CRED);
+            log_msg(LOG_REG_AUTH_NO_CRED);
+
+            break;
+        }
+
+        authenticate = sippmh_get_header_val(response, AUTH_HDR(status_code),
+                                             NULL);
+        if (authenticate) {
+            sip_authen = sippmh_parse_authenticate(authenticate);
+        }
+
+        if (sip_authen) {
+            if (sip_authen->scheme == SIP_DIGEST) {
+                if (authenticate != NULL) {
+                    ccb->retx_counter = 0;
+                    config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line,
+                                           sizeof(user));
+                    ccb->authen.cnonce[0] = '\0';
+                    snprintf(uri, sizeof(uri), "sip:%s", ccb->reg.proxy); /* proxy */
+                    if (sipSPIGenerateAuthorizationResponse(sip_authen, uri,
+                                                            SIP_METHOD_REGISTER,
+                                                            credentials.id,
+                                                            credentials.pw,
+                                                            &author_str,
+                                                            &(ccb->authen.nc_count),
+                                                            ccb)) {
+
+                        if (author_str) {
+                            if (ccb->authen.authorization != NULL) {
+                                cpr_free(ccb->authen.authorization);
+                                ccb->authen.authorization = NULL;
+                            }
+
+                            ccb->authen.authorization =
+                                (char *) cpr_malloc(strlen(author_str) *
+                                                    sizeof(char) + 1);
+
+                            if (ccb->authen.authorization != NULL) {
+                                sstrncpy(ccb->authen.authorization, author_str,
+                                         strlen(author_str) * sizeof(char) + 1);
+                                ccb->authen.status_code = status_code;
+                            }
+                            cpr_free(author_str);
+                        }
+                    } else {
+                        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                          ccb->index, ccb->dn_line, fname,
+                                          "sipSPIGenerateAuthorizationResponse");
+                    }
+
+                    if (!sipSPISendRegister(ccb, TRUE, user,
+                                            ccb->reg.tmr_expire)){
+                          ccsip_register_cleanup(ccb, TRUE);
+
+                          log_clear(LOG_REG_AUTH_MSG);
+                          log_msg(LOG_REG_AUTH_MSG);
+                    }
+
+                } else {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                      ccb->index, ccb->dn_line, fname,
+                                      "sippmh_parse_authenticate");
+
+                    ccsip_register_cleanup(ccb, TRUE);
+                    log_clear(LOG_REG_AUTH_HDR_MSG);
+                    log_msg(LOG_REG_AUTH_HDR_MSG);
+                }
+            } else {
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"auth scheme unsupported= %d\n",
+                                      DEB_F_PREFIX_ARGS(SIP_AUTH, fname), sip_authen->scheme);
+
+                ccsip_register_cleanup(ccb, TRUE);
+                log_clear(LOG_REG_AUTH_SCH_MSG);
+                log_msg(LOG_REG_AUTH_SCH_MSG);
+            }
+            sippmh_free_authen(sip_authen);
+        }
+
+        free_sip_message(response);
+        return;
+
+    case SIP_CLI_ERR_INTERVAL_TOO_SMALL:
+        {
+            int         new_expires = MAX_REG_EXPIRES;
+            const char *msg_ptr = NULL;
+            char        line_name[MAX_LINE_NAME_SIZE];
+
+            msg_ptr = sippmh_get_header_val(response,
+                                            (const char *)SIP_HEADER_MIN_EXPIRES,
+                                            NULL);
+            if (msg_ptr) {
+                new_expires = strtoul(msg_ptr, NULL, 10);
+            }
+
+            //ensure new Min-Expires is > what we set before in Expires
+            if (new_expires > ccb->reg.tmr_expire) {
+                ccb->reg.tmr_expire = new_expires;
+            } else {
+                ccb->reg.tmr_expire = MAX_REG_EXPIRES;
+            }
+
+            //We have to make a fresh request
+            sip_stop_ack_timer(ccb);
+            sip_start_ack_timer(ccb);
+
+            (void) sip_platform_register_expires_timer_stop(ccb->index);
+
+            sip_util_get_new_call_id(ccb);
+
+            ccb->authen.cred_type = 0;
+            ccb->retx_counter = 0;
+            config_get_line_string(CFGID_LINE_NAME, line_name, ccb->dn_line,
+                                   sizeof(line_name));
+
+            ccb->reg.act_time = (int) time(NULL);
+
+                 if (sipSPISendRegister(ccb, 0, line_name, ccb->reg.tmr_expire) != TRUE) {
+                     // set expire timer and cleanup ccb in non-ccm mode.
+                     // do not change the expire timer value in ccm mode
+
+                     if (ccb->cc_type != CC_CCM) {
+                         ccsip_register_cleanup(ccb, TRUE);
+                     }
+
+                     log_clear(LOG_REG_MSG);
+                     log_msg(LOG_REG_MSG);
+                 }
+
+            //no change in state
+        }
+        free_sip_message(response);
+        return;
+
+
+        /*
+         * 404 (Not Found)
+         * 413 (Request Entity Too Large)
+         * 480 (Temporarily Unavailable)
+         * 486 (Busy here)
+         */
+    case SIP_CLI_ERR_NOT_FOUND:
+    case SIP_CLI_ERR_LARGE_MSG:
+    case SIP_CLI_ERR_NOT_AVAIL:
+    case SIP_CLI_ERR_BUSY_HERE:
+        if (ccb->cc_type == CC_CCM) {
+            if (ccb->state == (int) SIP_REG_STATE_TOKEN_WAIT) {
+                clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+                sip_regmgr_ev_token_wait_4xx_n_5xx(ccb, event);
+            } else {
+                /*
+                 * Phone sends a REGISTER message with expires=0 as a
+                 * Keepalive msg. Proxy and CCM will respond with a
+                 * 200 OK.
+                 */
+                if (ccb->index != REG_BACKUP_CCB) {
+                    if (ccsip_register_get_register_state() != SIP_REG_UNREGISTERING) {
+                        if (((ti_config_table_t *)(ccb->cc_cfg_table_entry)) ||
+                            ccb->dn_line == PRIMARY_LINE) {
+                            if (platGetPhraseText(STR_INDEX_REGISTRATION_REJECTED,
+                                 (char *)tmp_str, STATUS_LINE_MAX_LEN - 1)== CPR_SUCCESS) {
+                                ui_set_notification(CC_NO_LINE, CC_NO_CALL_ID, tmp_str, 15, FALSE,
+                                                DEF_NOTIFY_PRI);
+                            }
+                        }
+                        log_clear(STR_INDEX_REGISTRATION_REJECTED);
+                        log_msg(STR_INDEX_REGISTRATION_REJECTED);
+                    }
+                }
+                if (process_retry_after(ccb, response) == FALSE) {
+                    sip_regmgr_ev_failure_response(ccb, event);
+                }
+            }
+        } else {
+            if (process_retry_after(ccb, response) == FALSE) {
+                ccsip_register_cleanup(ccb, TRUE);
+            }
+        }
+        free_sip_message(response);
+        return;
+
+    default:
+        if (ccb->cc_type == CC_CCM) {
+            if (ccb->state == (int) SIP_REG_STATE_TOKEN_WAIT) {
+                clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+                sip_regmgr_ev_token_wait_4xx_n_5xx(ccb, event);
+            } else {
+                sip_regmgr_ev_failure_response(ccb, event);
+            }
+            free_sip_message(response);
+            return;
+        }
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2),
+                          ccb->index, ccb->dn_line, fname,
+                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
+                          "SIP BAD", status_code);
+
+        snprintf(status, sizeof(status), "in %d, client error", status_code);
+        ccsip_register_cleanup(ccb, TRUE);
+
+        snprintf(status, sizeof(status), "in %d, request failure", status_code);
+        log_clear(LOG_REG_UNSUPPORTED);
+        log_msg(LOG_REG_UNSUPPORTED, status);
+        free_sip_message(response);
+        break;
+    }
+
+    if (ccb->reg.rereg_pending != 0) {
+        ccb->reg.rereg_pending = 0;
+        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, TRUE);
+        }
+    }
+}
+
+
+void
+ccsip_handle_ev_failure_response (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "ccsip_handle_ev_failure_response";
+    sipMessage_t   *response = NULL;
+    int             status_code = 0;
+    char            status[LOG_MAX_LEN];
+    sipStatusCodeClass_t code_class = codeClassInvalid;
+    ti_config_table_t *ccm_table_ptr = NULL;
+
+    /*
+     * Set reject flag if we get 5xx/6xx from publisher cucm.
+     */
+    ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+    if (ccm_table_ptr &&
+        ccb->index != REG_BACKUP_CCB) {
+        registration_reject = TRUE;
+        DEF_DEBUG(DEB_F_PREFIX"registration has been rejected. Set registration_reject to TRUE.\n",
+            DEB_F_PREFIX_ARGS(SIP_REG, fname));
+    }
+
+    response = event->u.pSipMessage;
+    clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+
+    if (sipGetResponseCode(response, &status_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE),
+                          ccb->index, ccb->dn_line, fname);
+        free_sip_message(response);
+        ccsip_register_cleanup(ccb, TRUE);
+        return;
+    }
+
+    code_class = sippmh_get_code_class((uint16_t)status_code);
+
+    log_clear(LOG_REG_AUTH);
+
+    switch (code_class) {
+    case codeClass5xx:
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_FAILURE),
+                          ccb->index, ccb->dn_line, fname, status_code);
+
+        log_msg(LOG_REG_AUTH_SERVER_ERR, status_code);
+
+        break;
+
+    case codeClass6xx:
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_FAILURE),
+                          ccb->index, ccb->dn_line, fname, status_code);
+
+        log_msg(LOG_REG_AUTH_GLOBAL_ERR, status_code);
+
+        break;
+
+    default:
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_FAILURE),
+                          ccb->index, ccb->dn_line, fname, status_code);
+
+        snprintf(status, sizeof(status), "in %d, ??? error", status_code);
+        log_msg(LOG_REG_AUTH_UNKN_ERR, status_code);
+
+        break;
+    }
+
+    if (ccb->cc_type == CC_CCM) {
+        if (ccb->state == (int) SIP_REG_STATE_TOKEN_WAIT) {
+
+            /* Handle only 503 error condition for other errors
+             * use default state
+             */
+            if (status_code == SIP_SERV_ERR_UNAVAIL) {
+                clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+                sip_regmgr_ev_token_wait_4xx_n_5xx(ccb, event);
+                free_sip_message(response);
+            } else {
+                sip_regmgr_ev_default(ccb, event);
+            }
+            return;
+        }
+
+        /* retry registration using value in the Retry-After header
+         * if response code is 503 with Retry-After
+         */
+        if ((status_code == SIP_SERV_ERR_UNAVAIL) &&
+            (process_retry_after(ccb, response) == TRUE)) {
+            free_sip_message(response);
+            return;
+        }
+        /*
+         * regmgr - Pass the event to the regmgr
+         */
+        sip_regmgr_ev_failure_response(ccb, event);
+        free_sip_message(response);
+    } else {
+        /*
+         * Assume CSPS for now
+         */
+
+        switch (status_code) {
+
+            /*
+             * 500 (Server Internal Error)
+             * 503 (Service Unavailable)
+             * 600 (Busy),
+             * 603 (Declined
+             */
+        case SIP_SERV_ERR_INTERNAL:
+        case SIP_SERV_ERR_UNAVAIL:
+
+        case SIP_FAIL_BUSY:
+        case SIP_FAIL_DECLINE:
+            if (process_retry_after(ccb, response) == FALSE) {
+                ccsip_register_cleanup(ccb, TRUE);
+            }
+            free_sip_message(response);
+            return;
+
+        default:
+            break;
+        }
+        ccsip_register_cleanup(ccb, TRUE);
+        free_sip_message(response);
+
+        if (ccb->reg.rereg_pending != 0) {
+            ccb->reg.rereg_pending = 0;
+            if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index)
+                    != SIP_REG_OK) {
+                ccsip_register_cleanup(ccb, TRUE);
+            }
+        }
+    }
+}
+
+/*
+ * ccsip_handle_ev_tmr_retry()
+ *
+ * This function is called when registration re-try timer
+ * pops or an ICMP unreachable is received after sending
+ * out the registration. The ICMP unreachable code sets the
+ * retx_counter to MAX to force the code to try the next
+ * proxy in the list. Event.u.usrInfo is set to the IP
+ * address that bounced or -1 if is a timeout.
+ */
+void
+ccsip_handle_ev_tmr_retry (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "ccsip_handle_ev_tmr_retry";
+    boolean         start_timer;
+    uint32_t        value;
+    int             dns_err_code = DNS_ERR_HOST_UNAVAIL;
+    cpr_ip_addr_t   ip_addr;
+
+    CPR_IP_ADDR_INIT(ip_addr);
+
+    start_timer = (ccsip_register_get_register_state() == SIP_REG_UNREGISTERING) ?
+        (FALSE) : (TRUE);
+
+    config_get_value(CFGID_SIP_RETX, &value, sizeof(value));
+    if (value > MAX_NON_INVITE_RETRY_ATTEMPTS) {
+        value = MAX_NON_INVITE_RETRY_ATTEMPTS;
+    }
+    if (ccb->retx_counter >= value) {
+        if (ccb->cc_type == CC_CCM /* RAMC Some other state */) {
+            /*
+             * regmgr - Send event to the regmgr
+             */
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Reached here for ccb->index=%d \n",
+                    DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname), ccb->index);
+            sip_regmgr_ev_tmr_ack_retry(ccb, event);
+            return;
+        } else {
+            /*
+             * Assume CSPS for now
+             */
+            ccb->retx_counter = 0;
+
+            /*
+             * Did the registration msg bounce trying to reach the outbound
+             * proxy (1st check) or did it timeout trying to reach the
+             * outbound proxy (2nd check)?
+             */
+            util_ntohl(&ip_addr, &(event->u.UsrInfo));
+            if (util_compare_ip(&ip_addr, &(ccb->outBoundProxyAddr)) ||
+                ((event->u.UsrInfo.type == CPR_IP_ADDR_INVALID) &&
+                util_check_if_ip_valid(&(ccb->outBoundProxyAddr)))) {
+                /*
+                 * If there are more records, reset address to force code
+                 * to use next entry. If not then bail.
+                 */
+                ccb->outBoundProxyPort = 0;
+                dns_err_code = DNS_OK;
+
+                /*
+                 * Trouble reaching the normal proxy. Get another proxy
+                 * if records are available
+                 */
+            } else if (str2ip(ccb->reg.proxy, &ccb->reg.addr) != 0) {
+                dns_err_code = sipTransportGetServerAddrPort(ccb->reg.proxy,
+                                                             &ccb->reg.addr,
+                                                             (uint16_t *) &ccb->reg.port,
+                                                             &ccb->SRVhandle,
+                                                             TRUE);
+                if (dns_err_code == 0) {
+                    util_ntohl(&(ccb->reg.addr), &(ccb->reg.addr));
+                } else {
+                    ccb->reg.addr = ip_addr_invalid;
+                }
+
+                /*
+                 * Modify destination fields in call back timer struct
+                 */
+                (void) sip_platform_msg_timer_update_destination(ccb->index,
+                                                                 &(ccb->reg.addr),
+                                                                 ccb->reg.port);
+            } else {
+                /* No other proxy to try */
+                dns_err_code = DNS_ERR_HOST_UNAVAIL;
+            }
+
+            /*
+             * Cleanup if unable to find another proxy
+             */
+            if (dns_err_code != DNS_OK) {
+                /*
+                 * All retransmit attempts have been exhausted.
+                 * Cleanup the call and move to the IDLE state
+                 */
+                ccsip_register_cleanup(ccb, start_timer);
+
+                log_clear(LOG_REG_RETRY);
+                log_msg(LOG_REG_RETRY);
+                if ((ccb->index == REG_BACKUP_CCB) &&
+                    (ccb->cc_type != CC_CCM)) {
+                    log_clear(LOG_REG_BACKUP);
+                    log_msg(LOG_REG_BACKUP);
+                }
+                if (ccb->reg.rereg_pending != 0) {
+                    ccb->reg.rereg_pending = 0;
+                    if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index)
+                        != SIP_REG_OK) {
+                        ccsip_register_cleanup(ccb, TRUE);
+                    }
+                }
+
+                return;
+            }
+        }
+    }
+
+    if (ccb->state != (int) SIP_REG_STATE_REGISTERED) {
+        /*
+         * Handling Retry timer in REGISTERED state is not needed
+         * the event has come due to race condition
+         */
+        ccb->retx_counter++;
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Resending message: #%d\n",
+                              DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname), ccb->retx_counter);
+
+        if (sipSPISendLastMessage(ccb) != TRUE) {
+            if (ccb->cc_type == CC_CCM) {
+                sip_regmgr_ev_tmr_ack_retry(ccb, event);
+            } else {
+                ccsip_register_cleanup(ccb, start_timer);
+            }
+            return;
+        }
+
+        ccsip_register_retry_timer_start(ccb);
+    }
+
+}
+
+
+void
+ccsip_handle_ev_reg_cancel (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    char user[MAX_LINE_NAME_SIZE];
+    int  exp_time = 0;
+
+    ccsip_register_clear_all_logs();
+
+    sip_stop_ack_timer(ccb);
+    sip_start_ack_timer(ccb);
+
+    (void) sip_platform_register_expires_timer_stop(ccb->index);
+
+    sip_util_get_new_call_id(ccb);
+
+    ccb->authen.cred_type = 0;
+    ccb->retx_counter     = 0;
+    ccb->reg.tmr_expire   = 0;
+    ccb->reg.act_time     = 0;
+    config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line, sizeof(user));
+
+    if (sipSPISendRegister(ccb, 0, user, exp_time) != TRUE) {
+        log_clear(LOG_REG_CANCEL_MSG);
+        log_msg(LOG_REG_CANCEL_MSG);
+    } else if ((ccb->index == REG_BACKUP_CCB) && (ccb->cc_type != CC_CCM)) {
+        log_clear(LOG_REG_BACKUP);
+        log_msg(LOG_REG_BACKUP);
+    }
+
+    sip_reg_sm_change_state(ccb, SIP_REG_STATE_UNREGISTERING);
+}
+
+
+void
+ccsip_handle_ev_unreg_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "ccsip_handle_ev_unreg_2xx";
+    sipMessage_t *response = event->u.pSipMessage;
+    int           timeout;
+
+    free_sip_message(response);
+
+    ccb->reg.registered = 0;
+    ccb->reg.tmr_expire = 0;
+    ccb->reg.act_time = 0;
+    clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+
+    if (ccb->cc_type == CC_CCM) {
+        if (ccb->index == REG_BACKUP_CCB) {
+            /*
+             * Stay in unregistering state and wait for the
+             * expires timer to pop to send the next keepalive.
+             */
+            sip_stop_ack_timer(ccb);
+            if (new_standby_available) {
+                sip_regmgr_replace_standby(ccb);
+            }
+
+            timeout = sip_config_get_keepalive_expires();
+
+            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Keep alive timer (%d sec)\n",
+                    DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->index, ccb->dn_line, fname), timeout);
+            (void) sip_platform_standby_keepalive_timer_start(timeout * 1000);
+        } else {
+            sip_reg_sm_change_state(ccb, SIP_REG_STATE_IDLE);
+
+            sip_stop_ack_timer(ccb);
+
+            if (ccsip_register_all_unregistered() == TRUE) {
+                ccsip_register_set_register_state(SIP_REG_IDLE);
+                /*
+                 * regmgr - FALLBACK: Pick the ccb of the ccm that has
+                 * come up (falling back) and post the fallback event,
+                 * now that the unregistering is done.
+                 * Will commit change after integration with ccm.
+                 */
+            }
+            platSetSISProtocolVer(1,0,0,NULL);
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"set[1] the SIS protocol ver to 1.0.0\n",
+                                  DEB_F_PREFIX_ARGS(SIP_REG, fname));
+        }
+    } else {
+        sip_reg_sm_change_state(ccb, SIP_REG_STATE_IDLE);
+
+        sip_stop_ack_timer(ccb);
+
+        if (ccsip_register_all_unregistered() == TRUE) {
+            ccsip_register_set_register_state(SIP_REG_IDLE);
+        }
+
+        /*
+         * Our unregistration from the proxy has succeeded now we
+         * can reregister again
+         */
+        if (ccb->reg.rereg_pending != 0) {
+            ccb->reg.rereg_pending = 0;
+            if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index)
+                    != SIP_REG_OK) {
+                ccsip_register_cleanup(ccb, TRUE);
+            }
+        }
+        platSetSISProtocolVer(1,0,0,NULL);
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"set[2] the SIS protocol ver to 1.0.0\n",
+                              DEB_F_PREFIX_ARGS(SIP_REG, fname));
+    }
+}
+
+
+void
+ccsip_handle_ev_unreg_tmr_ack (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    log_clear(LOG_REG_AUTH_UNREG_TMR);
+    log_msg(LOG_REG_AUTH_UNREG_TMR);
+
+    if (ccb->cc_type == CC_CCM) {
+        /*
+         * regmgr - Send tmr ack event to the regmgr
+         */
+        sip_regmgr_ev_tmr_ack_retry(ccb, event);
+        return;
+    }
+    ccsip_register_cleanup(ccb, FALSE);
+    /*
+     * We are unregistering ourselves from the proxy when our own registration
+     * timer has expired.  This means that we do not need to complete our
+     * unregistration from the proxy since it is being done automatically.
+     * We can just send our new registration now
+     */
+    if (ccb->reg.rereg_pending != 0) {
+        ccb->reg.rereg_pending = 0;
+        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, TRUE);
+        }
+    }
+}
+
+void
+ccsip_handle_ev_standby_keepalive_tmr_expire (ccsipCCB_t *ccb,
+                                              sipSMEvent_t *event)
+{
+    /*
+     * regmgr - If there is a fallback ccb in TOKEN_WAIT
+     * state waiting to fallback notify it so it can
+     * continue with the fallback procedure.
+     */
+    if (ccb->cc_type == CC_CCM) {
+        /*
+         * REGMGR - CHECK if this event gets called.
+         * Will get in here only for REG_BACKUP_CCB in
+         * unregistering state i.e, the standby cc which
+         * is periodically monitored.
+         */
+        /*
+         * Stay in unregistering state and post a message
+         * to send another keep-alive message.
+         */
+        (void) sip_platform_standby_keepalive_timer_stop();
+        (void) ccsip_register_send_msg(SIP_REG_CANCEL, ccb->index);
+    } else {
+        ccsip_handle_ev_default(ccb, event);
+    }
+}
+
+int
+sip_reg_sm_process_event (sipSMEvent_t *pEvent)
+{
+    static const char fname[] = "sip_reg_sm_process_event";
+    ccsipCCB_t *ccb = NULL;
+
+    ccb = pEvent->ccb;
+    if (!ccb) {
+        CCSIP_DEBUG_ERROR("%s: Error: ccb is null. Unable to process event "
+                          "<%d>\n", fname, pEvent->type);
+        return (-1);
+    }
+
+    /* Unbind UDP ICMP response handler */
+    if ((REG_CHECK_EVENT_SANITY((int) ccb->state, (int) pEvent->type)) &&
+        (REG_EVENT_ACTION(ccb->state, (int) pEvent->type))) {
+        if (dump_reg_msg == TRUE) {
+            DEF_DEBUG(DEB_L_C_F_PREFIX"%s <- %s\n",
+                    DEB_L_C_F_PREFIX_ARGS(SIP_REG_STATE, ccb->dn_line, ccb->index, fname),
+                    sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
+                    sip_util_reg_event2string((sipRegSMEventType_t)pEvent->type));
+        }
+        REG_EVENT_ACTION(ccb->state, pEvent->type) (ccb, pEvent);
+    } else {
+        /* Invalid State/Event pair */
+        CCSIP_DEBUG_ERROR("%s: Error: illegal state/event pair: (%d <-- %d)\n",
+                          fname, ccb->state, pEvent->type);
+        return (-1);
+    }
+
+    return (0);
+}
+
+
+const char *
+sip_util_reg_event2string (sipRegSMEventType_t event)
+{
+    switch (event) {
+    case E_SIP_REG_REG_REQ:
+        return ("E_SIP_REG_REG_REQ");
+
+    case E_SIP_REG_CANCEL:
+        return ("E_SIP_REG_CANCEL");
+
+    case E_SIP_REG_1xx:
+        return ("E_SIP_REG_1xx");
+
+    case E_SIP_REG_2xx:
+        return ("E_SIP_REG_2xx");
+
+    case E_SIP_REG_3xx:
+        return ("E_SIP_REG_3xx");
+
+    case E_SIP_REG_4xx:
+        return ("E_SIP_REG_4xx");
+
+    case E_SIP_REG_FAILURE_RESPONSE:
+        return ("E_SIP_REG_FAILURE_RESPONSE");
+
+    case E_SIP_REG_TMR_EXPIRE:
+        return ("E_SIP_REG_TMR_EXPIRE");
+
+    case E_SIP_REG_TMR_ACK:
+        return ("E_SIP_REG_TMR_ACK");
+
+    case E_SIP_REG_TMR_WAIT:
+        return ("E_SIP_REG_TMR_WAIT");
+
+    case E_SIP_REG_TMR_RETRY:
+        return ("E_SIP_REG_TMR_RETRY");
+
+    case E_SIP_REG_CLEANUP:
+        return ("E_SIP_REG_CLEANUP");
+
+    default:
+        return ("SIP_REG_STATE_UNKNOWN");
+
+    }
+}
+
+
+char *
+sip_util_reg_state2string (sipRegSMStateType_t state)
+{
+    switch (state) {
+    case SIP_REG_STATE_NONE:
+        return ("SIP_REG_STATE_NONE");
+
+    case SIP_REG_STATE_IDLE:
+        return ("SIP_REG_STATE_IDLE");
+
+    case SIP_REG_STATE_REGISTERING:
+        return ("SIP_REG_STATE_REGISTERING");
+
+    case SIP_REG_STATE_REGISTERED:
+        return ("SIP_REG_STATE_REGISTERED");
+
+    case SIP_REG_STATE_UNREGISTERING:
+        return ("SIP_REG_STATE_UNREGISTERING");
+
+    case SIP_REG_STATE_IN_FALLBACK:
+        return ("SIP_REG_STATE_IN_FALLBACK");
+
+    case SIP_REG_STATE_STABILITY_CHECK:
+        return ("SIP_REG_STATE_STABILITY_CHECK");
+
+    case SIP_REG_STATE_TOKEN_WAIT:
+        return ("SIP_REG_STATE_TOKEN_WAIT");
+
+    default:
+        return ("SIP_REG_STATE_UNKNOWN");
+
+    }
+}
+
+
+sipRegSMEventType_t
+ccsip_register_sip2sipreg_event (int sip_event)
+{
+    static const char fname[] = "ccsip_register_sip2sipreg";
+    sipRegSMEventType_t reg_event = E_SIP_REG_NONE;
+
+    switch (sip_event) {
+    case E_SIP_1xx:
+        reg_event = E_SIP_REG_1xx;
+        break;
+
+    case E_SIP_2xx:
+        reg_event = E_SIP_REG_2xx;
+        break;
+
+    case E_SIP_3xx:
+        reg_event = E_SIP_REG_3xx;
+        break;
+
+    case E_SIP_FAILURE_RESPONSE:
+        reg_event = E_SIP_REG_4xx;
+        break;
+
+    default:
+        CCSIP_DEBUG_ERROR("%s: Error: Unknown event.\n", fname);
+        reg_event = E_SIP_REG_NONE;
+        break;
+    }
+    return (reg_event);
+}
+
+
+void
+ccsip_register_timeout_retry (void *data)
+{
+    static const char fname[] = "ccsip_register_timeout_retry";
+    ccsipCCB_t *ccb;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+
+    ccb = (ccsipCCB_t *) data;
+    if (ccb == NULL) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "ccsip_register_timeout_retry");
+        return;
+    }
+
+    if (ccsip_register_send_msg(SIP_TMR_REG_RETRY, ccb->index) != SIP_REG_OK) {
+        ccsip_register_cleanup(ccb, TRUE);
+    }
+}
+
+
+/*
+ * timer callback function
+ *
+ * The function will search for the line that set the timer and then
+ * (1) send expire event to sm or
+ * (2) set another 60s timer. The setting of another timer is necessary
+ *     because the max ticks for an OS timer is 10m. The expire timer can
+ *     be larger. Therefore, set x 1m timers and count down.
+ */
+int
+ccsip_register_send_msg (uint32_t cmd, line_t ndx)
+{
+    static const char fname[] = "ccsip_register_send_msg";
+    ccsip_registration_msg_t *register_msg;
+    ccsipCCB_t *ccb = NULL;
+    CCM_ID ccm_id = UNUSED_PARAM;
+    ti_config_table_t * cc_cfg_table;
+
+    /*
+     * Get the ccm_id type from ccb corresponding to ndx.
+     * Currently, only SIP_TMR_REG_RETRY msg in SIPTaskProcessListEvent is
+     * making use of ccm_id field. No other reg msg make use of ccm_id. Also in
+     * case of SIP_REG_UPDATE, ccm_id will not be determined below but remain
+     * initialized to UNUSED_PARAM because, in case of SIP_REG_UPDATE, ndx is
+     * not ccb->index but number of available lines when lkem is attached.
+     */
+    if (cmd != SIP_REG_UPDATE) {
+        ccb = sip_sm_get_ccb_by_index(ndx);
+        if (ccb != NULL) {
+            cc_cfg_table =  (ti_config_table_t *)(ccb->cc_cfg_table_entry);
+            if (cc_cfg_table != NULL) {
+                ccm_id = cc_cfg_table->ti_specific.ti_ccm.ccm_id;
+            }
+            else {
+                CCSIP_DEBUG_ERROR("%s: Error: cc_cfg_table is null.\n", fname);
+                return SIP_ERROR;
+            }
+        }
+        else {
+            CCSIP_DEBUG_ERROR("%s: Error: ccb is null.\n", fname);
+            return SIP_ERROR;
+        }
+    }
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cmd=%d=%s ccb->index=%d ccm_id=%s\n",
+        DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname),
+        cmd, REG_CMD_PRINT(cmd), ndx, CCM_ID_PRINT(ccm_id));
+
+    register_msg = (ccsip_registration_msg_t *)
+        SIPTaskGetBuffer(sizeof(ccsip_registration_msg_t));
+
+    if (!register_msg) {
+        CCSIP_DEBUG_ERROR("%s: Error: get buffer failed.\n", fname);
+        return SIP_ERROR;
+    }
+    register_msg->ccb_index = ndx;
+    register_msg->ccm_id = ccm_id;
+
+    if (SIPTaskSendMsg(cmd, register_msg, sizeof(ccsip_registration_msg_t), NULL)
+            == CPR_FAILURE) {
+        cpr_free(register_msg);
+        CCSIP_DEBUG_ERROR("%s: Error: send buffer failed.\n", fname);
+        return SIP_ERROR;
+    }
+
+    return SIP_REG_OK;
+}
+
+void
+ccsip_register_retry_timer_start (ccsipCCB_t *ccb)
+{
+    static const char fname[] = "ccsip_register_retry_timer_start";
+    int timeout;
+    int time_t1;
+    int time_t2;
+
+    /* Double the timeout value and do exponential time increases
+     * The doubling is used because the invite timeout is too short
+     */
+    config_get_value(CFGID_TIMER_T1, &time_t1, sizeof(time_t1));
+    timeout = time_t1 * (1 << ccb->retx_counter);
+    config_get_value(CFGID_TIMER_T2, &time_t2, sizeof(time_t2));
+    if (timeout > time_t2) {
+        timeout = time_t2;
+    }
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Starting reTx timer (%d msec)\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->index, ccb->dn_line, fname), timeout);
+
+    ccb->retx_flag = TRUE;
+    if (sip_platform_msg_timer_start(timeout, (void *)ccb, ccb->index,
+                                     sipPlatformUISMTimers[ccb->index].message_buffer,
+                                     sipPlatformUISMTimers[ccb->index].message_buffer_len,
+                                     sipMethodRegister,
+                                     &(sipPlatformUISMTimers[ccb->index].ipaddr),
+                                     sipPlatformUISMTimers[ccb->index].port,
+                                     TRUE) != SIP_OK) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, ccb->dn_line, fname,
+                          "sip_platform_msg_timer_start");
+        ccb->retx_flag = FALSE;
+    }
+}
+
+
+void
+ccsip_register_cleanup (ccsipCCB_t *ccb, boolean start)
+{
+    static const char fname[] = "ccsip_register_cleanup";
+    int exp_time;
+
+    config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &exp_time, sizeof(exp_time));
+
+    ccb->reg.registered = 0;
+
+    if (ccb->index != REG_BACKUP_CCB) {
+        ui_set_sip_registration_state(ccb->dn_line, FALSE);
+    }
+
+    sip_stop_ack_timer(ccb);
+
+    if ((start) && (ccb->state != (int) SIP_REG_STATE_UNREGISTERING)) {
+        if (ccb->index == REG_BACKUP_CCB) {
+            ccb->reg.tmr_expire = (exp_time > 5) ? exp_time - 5 : exp_time;
+        } else {
+            ccb->reg.tmr_expire = 60;
+        }
+        ccb->reg.act_time = (int) time(NULL);
+
+        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Starting expires timer (%d "
+                          "sec)\n", DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->index, ccb->dn_line, fname),
+                          ccb->reg.tmr_expire);
+        (void) sip_platform_register_expires_timer_start(ccb->reg.tmr_expire * 1000,
+                                                         ccb->index);
+    }
+
+    sip_reg_sm_change_state(ccb, SIP_REG_STATE_IDLE);
+
+    if (ccsip_register_all_unregistered() == TRUE) {
+        ccsip_register_set_register_state(SIP_REG_IDLE);
+    }
+
+    /*
+     * Always check how sip_sm_call_cleanup is implemented.
+     * The function has a tendency to change.
+     */
+
+    /* Cleanup and re-initialize CCB */
+    sip_sm_call_cleanup(ccb);
+}
+
+
+cc_int32_t
+ccsip_register_cmd (cc_int32_t argc, const char *argv[])
+{
+    /*
+     * On 7970 phones the command is reg line [option] [line]
+     * On 7940/60 the command is reg [option] [line]
+     * Thus the location in the argument array is different
+     * based on the phone type. OFFSET is used to get around
+     * this difference while still having common code.
+     */
+#define OFFSET 1
+    ccsipCCB_t *ccb = NULL;
+    line_t ndx = 0;
+    line_t temp_line = 0;
+    char str_val[MAX_LINE_NAME_SIZE];
+    const char *line_string;
+    char *strtol_end;
+
+    /*
+     * check if need help
+     */
+    if ((argc == 2 + OFFSET) && (argv[1 + OFFSET][0] == '?')) {
+        debugif_printf("reg [option] [line]\n");
+        debugif_printf("    options = 0: unregister\n");
+        debugif_printf("              1: register\n");
+        debugif_printf("    line    = 1 through 6\n");
+        debugif_printf("            = backup (line 1 to backup proxy)\n");
+        debugif_printf("\n");
+        return (0);
+    }else if (cpr_strcasecmp(argv[0 + OFFSET], "line") == 0) {
+
+       /*
+        make sure the user entered a complete command
+       */
+        if (argc < 3 + OFFSET) {
+            debugif_printf("Incomplete command\n");
+            debugif_printf("reg [option] [line]\n");
+            debugif_printf("    options = 0: unregister\n");
+            debugif_printf("              1: register\n");
+            debugif_printf("    line    = 1 through 6\n");
+            debugif_printf("            = backup (line 1 to backup proxy)\n");
+            debugif_printf("\n");
+            return (0);
+        }
+
+        /* make sure we have at least one line configured */
+        config_get_line_string(CFGID_LINE_NAME, str_val, 1, sizeof(str_val));
+        if ((strcmp(str_val, UNPROVISIONED) == 0) || (str_val[0] == '\0')) {
+            debugif_printf("Error:No lines configured\n");
+            return (0);
+        }
+
+        line_string = argv[2 + OFFSET];
+        if (cpr_strcasecmp(line_string, "backup") == 0) {
+            temp_line = REG_BACKUP_LINE;
+        } else {
+            errno = 0;
+            temp_line = (line_t) strtol(line_string, &strtol_end, 10);
+            if (errno || line_string == strtol_end || !sip_config_check_line(temp_line)) {
+                debugif_printf("Error:Invalid Line\n");
+                return (0);
+            }
+        }
+
+
+            ndx = SIP_REG_REGLINE2LINE(temp_line - 1);
+            ccb = sip_sm_get_ccb_by_index(ndx);
+
+        if (ccb == NULL) {
+            debugif_printf("Unable to retrieve registration information for this line.\n");
+            debugif_printf("Command aborted.\n");
+            return (0);
+        }
+
+        if ((temp_line == REG_BACKUP_LINE) && util_check_if_ip_valid(&(ccb->dest_sip_addr)) == FALSE) {
+            debugif_printf("Backup Proxy is invalid or not configured.\n");
+            return (0);
+        }
+
+        switch (argv[1 + OFFSET][0]) {
+        case '0':
+
+            if (ccb->index != REG_BACKUP_CCB) {
+                ui_set_sip_registration_state(ccb->dn_line, FALSE);
+            }
+
+            if (temp_line != REG_BACKUP_LINE) {
+                debugif_printf("Unregistering line %d\n", temp_line);
+            } else {
+                debugif_printf("Unregistering line 1 to backup proxy\n");
+            }
+            (void) sip_platform_register_expires_timer_stop(ccb->index);
+            sip_stop_ack_timer(ccb);
+
+            if (ccsip_register_send_msg(SIP_REG_CANCEL, ndx) != SIP_REG_OK) {
+                ccsip_register_cleanup(ccb, FALSE);
+            }
+            break;
+
+        case '1':
+
+            /* Cleanup and re-initialize CCB */
+            sip_sm_call_cleanup(ccb);
+
+            if (temp_line != REG_BACKUP_LINE) {
+                debugif_printf("Registering line %d\n", temp_line);
+            } else {
+                debugif_printf("Registering line 1 to backup proxy\n");
+            }
+            (void) ccsip_register_send_msg(SIP_REG_REQ, ndx);
+            break;
+
+        default:
+            debugif_printf("Invalid Option Value - please choose 1 or 0\n");
+            return (0);
+        }
+    }
+    return (0);
+}
+
+/*
+ *  Function: build_reg_flags
+ *
+ *  Parameters:
+ *
+ *  Description: builds a 3 character string representing various flags
+ *               for use by the show_reg command
+ *
+ *  Returns: none
+ *
+ */
+static void
+build_reg_flags (char *buf, int buf_size, int prox_reg, ccsipCCB_t *ccb,
+                 boolean provisioned)
+{
+    sstrncpy(buf, "...", buf_size);
+    if (!provisioned || !prox_reg) {
+        return;
+    }
+
+    buf[1] = '1'; // Set the Provisioned bit
+
+    if (ccb->authen.authorization != NULL) {
+        buf[0] = '1';
+    } else {
+        if (ccb->authen.status_code != 0) {
+            buf[0] = 'x';
+        }
+    }
+    if (ccb->reg.registered) {
+        buf[2] = '1';
+    } else {
+        buf[2] = 'x';
+    }
+}
+
+/*
+ *  Function: show_register_data
+ *
+ *  Parameters:
+ *
+ *  Description:  Internal print routine for show_register_cmd. Also
+ *                called by reg command
+ *
+ *  Returns:
+ *
+ */
+#define TMP_BUF_SIZE 8
+static void
+show_register_data (void)
+{
+    ccsipCCB_t *ccb;
+    char       buf[TMP_BUF_SIZE];
+    line_t     ndx = 0;
+    int        proxy_register = 0;
+    boolean    valid_line = FALSE;
+    int        timer_count_down = 0;
+
+    config_get_value(CFGID_PROXY_REGISTER, &proxy_register,
+                     sizeof(proxy_register));
+
+    debugif_printf("\nLINE REGISTRATION TABLE\nProxy Registration: ");
+    if (proxy_register == 1) {
+        debugif_printf("ENABLED");
+    } else {
+        debugif_printf("DISABLED");
+    }
+    debugif_printf(", state: %s\n",
+                   ccsip_register_state_name(ccsip_register_get_register_state()));
+    debugif_printf("line  APR  state          timer       expires     proxy:port\n");
+    debugif_printf("----  ---  -------------  ----------  ----------  ----------------------------\n");
+    for (ndx = REG_CCB_START; ndx <= REG_BACKUP_CCB; ndx++) {
+        ccb = sip_sm_get_ccb_by_index(ndx);
+        valid_line = FALSE;
+        if (ndx == REG_BACKUP_CCB) {
+            valid_line = TRUE;
+        } else {
+            if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
+                valid_line = TRUE;
+            }
+        }
+        // calc the time until registration expires
+        if (ccb && valid_line) {
+            if (!proxy_register || !valid_line) {
+                timer_count_down = 0;
+            } else {
+                if (ccb->reg.act_time == 0) {
+                    timer_count_down = 0;
+                } else if ((sipRegSMStateType_t)ccb->state ==
+                        SIP_REG_STATE_REGISTERING) {
+                    timer_count_down =
+                        (SIP_REG_TMR_ACK_TICKS / 1000) -
+                        (((int) time(NULL)) - ccb->reg.act_time);
+                } else {
+                    timer_count_down = ccb->reg.tmr_expire -
+                        (((int) time(NULL)) - ccb->reg.act_time);
+                }
+            }
+
+           /*
+             * Ensure that the timer_count_down value is valid. There
+             * are cases where the phone completes registration before an
+             * SNTP/NTP response is received which makes the act_time and
+             * time(NULL) values inconsistent.
+             */
+           if ((timer_count_down < 0) ||
+               (timer_count_down > ccb->reg.tmr_expire)) {
+
+               timer_count_down = 0;
+           }
+        }
+
+        // build the flag string
+        if (ccb) {
+            build_reg_flags((char *)&buf, TMP_BUF_SIZE, proxy_register,
+                            ccb, valid_line);
+        }
+
+        if (ndx != REG_BACKUP_CCB) {
+            debugif_printf("%-4d", ndx - TEL_CCB_END);
+        } else {
+            debugif_printf("1-BU");
+        }
+        if (ccb && valid_line) {
+            debugif_printf("  %-3s  %-13s  %-10d  %-10d  %s:%d\n",
+                           buf,
+                           ccsip_register_reg_state_name((sipRegSMStateType_t)ccb->state),
+                           ccb->reg.tmr_expire,
+                           timer_count_down,
+                           ((ccb->reg.proxy[0] != '\0') ? (ccb->reg.proxy) : ("undefined")),
+                           ccb->reg.port);
+        } else {
+            debugif_printf("  %-3s  %-13s  %-10d  %-10d  %s:%d\n",
+                           buf, "NONE", 0, 0, "undefined", 0);
+        }
+    }
+    debugif_printf("\nNote: APR is Authenticated, Provisioned, Registered\n");
+}
+
+/*
+ *  Function: show_register_cmd
+ *
+ *  Parameters:  argc,argv
+ *
+ *  Description:  Shows the current registration table
+ *
+ *  Returns:
+ *
+ */
+cc_int32_t
+show_register_cmd (cc_int32_t argc, const char *argv[])
+{
+
+    /*
+     * check if need help
+     */
+    if ((argc == 2) && (argv[1][0] == '?')) {
+        debugif_printf("sh reg\n");
+        return (0);
+    }
+    show_register_data();
+
+    return (0);
+}
+
+void
+ccsip_register_clear_all_logs (void)
+{
+    log_clear(LOG_REG_MSG);
+    log_clear(LOG_REG_AUTH);
+    log_clear(LOG_REG_RETRY);
+    log_clear(LOG_REG_UNSUPPORTED);
+}
+
+
+void
+ccsip_register_reset_proxy (void)
+{
+    ccsipCCB_t *ccb;
+    line_t ndx;
+    line_t line_end;
+
+    /*
+     * If this is a restart (phone is resetting) then reset the proxy
+     * string for each line.  The proxy string is used to determine
+     * what address to use when registering. The proxy remains the
+     * same throughout a boot cycle and on restarts there is a new
+     * boot cycle so the proxy address is reset.
+     */
+    line_end = 1;
+
+    /*
+     * REG CCBs start after the TEL CCBs.
+     * EG., TEL CCBs are at 1 and 2 and REG CCBs are at 3 and 4.
+     * So, line_end equals the number of lines and then add the TEL_CCB_END
+     * to get the ending of the REG CCBs
+     */
+    line_end += TEL_CCB_END;
+
+    //ccsip_register_lines = line_end - REG_CCB_START + 1;
+    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
+        if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
+            ccb = sip_sm_get_ccb_by_index(ndx);
+            if (ccb) {
+                ccb->reg.proxy[0] = '\0';
+            }
+        }
+    }
+
+    //reset backup proxy registration;
+    ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+    if (ccb) {
+        ccb->reg.proxy[0] = '\0';
+    }
+}
+
+int
+ccsip_register_init (void)
+{
+    static const char fname[] = "ccsip_register_init";
+    static const char sipAckTimerName[] = "sipAck";
+    int i;
+
+    ccsip_register_set_register_state(SIP_REG_IDLE);
+
+    /*
+     * Create acknowledgement timers
+     */
+    for (i = 0; i < MAX_REG_LINES + 1; i++) {
+        ack_tmrs[i] = cprCreateTimer(sipAckTimerName, SIP_ACK_TIMER,
+                                     TIMER_EXPIRATION, sip_msgq);
+        if (ack_tmrs[i] == NULL) {
+            CCSIP_DEBUG_ERROR("%s: timer NOT created: %d\n",
+                              fname, i);
+            return SIP_ERROR;
+        }
+    }
+
+    // Initialize date holder structure
+    ccm_date.valid = FALSE;
+    ccm_date.datestring[0] = '\0';
+    start_standby_monitor = TRUE;
+
+    return SIP_OK;
+}
+
+
+ccsip_register_states_t
+ccsip_register_get_register_state (void)
+{
+    return (ccsip_register_state);
+}
+
+
+void
+ccsip_register_set_register_state (ccsip_register_states_t state)
+{
+    ccsip_register_state = state;
+}
+
+void
+ccsip_register_cancel (boolean cancel_reg, boolean backup_proxy)
+{
+    static const char fname[] = "ccsip_register_cancel";
+    ccsipCCB_t *ccb;
+    line_t     ndx;
+    line_t     line_end;
+
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+
+    if (ccsip_register_get_register_state() == SIP_REG_IDLE) {
+        return;
+    }
+
+    ccsip_register_set_register_state(SIP_REG_UNREGISTERING);
+
+    line_end = 1;
+
+    ui_set_sip_registration_state((line_t) (line_end + 1), FALSE);
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"unregistering %d lines\n", DEB_F_PREFIX_ARGS(SIP_REG, fname), line_end);
+
+    /*
+     * REG CCBs start after the TEL CCBs.
+     * EG., TEL CCBs are at 1 and 2 and REG CCBs are at 3 and 4.
+     * So, line_end equals the number of lines and then add the TEL_CCB_END
+     * to get the ending of the REG CCBs
+     */
+    line_end += TEL_CCB_END;
+
+    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
+        if (sip_config_check_line((line_t) (ndx - TEL_CCB_END))) {
+            ccb = sip_sm_get_ccb_by_index(ndx);
+            if (!ccb) {
+                continue;
+            }
+            ui_set_sip_registration_state(ccb->dn_line, FALSE);
+
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cancelling timers, line= %d\n",
+                                  DEB_F_PREFIX_ARGS(SIP_TIMER, fname), ccb->index);
+            (void) sip_platform_register_expires_timer_stop(ccb->index);
+            sip_stop_ack_timer(ccb);
+
+            if (cancel_reg) {
+                if (ccb->index == REG_CCB_START) {
+                    ccb->send_reason_header = TRUE;
+                }
+                if (ccb->index == REG_CCB_START) {
+                     ccb->send_reason_header = TRUE;
+                }
+
+                    if (ccsip_register_send_msg(SIP_REG_CANCEL, ndx)
+                        != SIP_REG_OK) {
+                        ccsip_register_cleanup(ccb, FALSE);
+                    }
+
+            } else {
+                /* Cleanup and re-initialize CCB */
+                sip_sm_call_cleanup(ccb);
+                if (ndx == line_end) {
+                    ccsip_register_set_register_state(SIP_REG_IDLE);
+                }
+            }
+        }
+    }
+
+    if (backup_proxy == FALSE) {
+        return;
+    }
+
+    /* cancel backup registration as well */
+    ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cancelling timers, line= %d\n",
+                          DEB_F_PREFIX_ARGS(SIP_TIMER, fname), ccb->index);
+
+    (void) sip_platform_register_expires_timer_stop(ccb->index);
+    sip_stop_ack_timer(ccb);
+
+    if (cancel_reg) {
+        if (ccsip_register_send_msg(SIP_REG_CANCEL, REG_BACKUP_CCB)
+                != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, FALSE);
+        }
+    } else {
+        /* Cleanup and re-initialize CCB */
+        sip_sm_call_cleanup(ccb);
+    }
+}
+
+
+void
+ccsip_register_all_lines (void)
+{
+    static const char fname[] = "ccsip_register_all_lines";
+    ccsipCCB_t     *ccb = 0;
+    line_t          ndx;
+    line_t          line;
+    line_t          line_end = 1;
+    int             value = 0;
+    char            proxyaddress[MAX_IPADDR_STR_LEN];
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+
+    /* Do not register if
+     * - no need to register
+     * - and already registered
+     */
+
+    line_end = 1;
+    config_get_value(CFGID_PROXY_REGISTER, &value, sizeof(value));
+    if (value == 0) {
+        CCSIP_DEBUG_REG_STATE(get_debug_string(DEBUG_REG_DISABLED), NULL,
+                              NULL, fname);
+
+        // mark all unregistered
+        for (line = 1; line <= line_end; line++) {
+            if (sip_config_check_line(line)) {
+                ui_set_sip_registration_state(line, FALSE);
+            }
+        }
+        ccsip_register_reset_proxy();
+        return;
+    } else if (ccsip_register_get_register_state() == SIP_REG_REGISTERED) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"lines already registered\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+
+        return;
+    }
+
+
+    ccsip_register_reset_proxy();
+    ccsip_register_set_register_state(SIP_REG_REGISTERING);
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"registering %d line%c\n",
+                          DEB_F_PREFIX_ARGS(SIP_REG, fname), line_end, line_end > 1 ? 's' : ' ');
+
+    /* register line 1 to the backup proxy.
+     * Start with the backup as DNS lookups hold
+     * the task meaning it could take a while to
+     * register the six regular lines.
+     */
+    ndx = REG_BACKUP_CCB;
+    ccb = sip_sm_get_ccb_by_index(ndx);
+    /* Cleanup and re-initialize CCB */
+    sip_sm_call_cleanup(ccb);
+    if (ccb->cc_type == CC_CCM) {
+        /*
+         * regmgr - Set the Backup (standby ccm) info.
+         */
+        ti_config_table_t *standby_ccm =
+            CCM_Active_Standby_Table.standby_ccm_entry;
+
+        if (standby_ccm) {
+            ti_ccm_t *ti_ccm = &standby_ccm->ti_specific.ti_ccm;
+
+            sip_regmgr_setup_new_standby_ccb(ti_ccm->ccm_id);
+        } else {
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"ERROR: Standby ccm entry is NULL\n",
+                                  DEB_F_PREFIX_ARGS(SIP_REG, fname));
+        }
+    } else {
+        /*
+         * Assume CSPS since only those two for now.
+         */
+        if (util_check_if_ip_valid(&(ccb->dest_sip_addr))) {
+            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d, 0x%x\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname),
+                                  ndx, ccb);
+
+            ccb->reg.addr = ccb->dest_sip_addr;
+            ccb->reg.port = (uint16_t) ccb->dest_sip_port;
+
+            if (ccb->index == REG_CCB_START) {
+                ccb->send_reason_header = TRUE;
+            } else {
+                ccb->send_reason_header = FALSE;
+            }
+
+            if (ccsip_register_send_msg(SIP_REG_REQ, ndx) != SIP_REG_OK) {
+                ccsip_register_cleanup(ccb, TRUE);
+            }
+        } else {
+            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d: Backup Proxy not configured\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname), ndx);
+        }
+    }
+
+    /*
+     * REG CCBs start after the TEL CCBs.
+     * EG., TEL CCBs are at 1 and 2 and REG CCBs are at 3 and 4.
+     * So, line_end equals the number of lines and then add the TEL_CCB_END to
+     * get the ending of the REG CCBs
+     */
+    line_end += TEL_CCB_END;
+
+    DEF_DEBUG(DEB_F_PREFIX"Disabling mass reg state", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
+        if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
+            ccb = sip_sm_get_ccb_by_index(ndx);
+            if (!ccb) {
+                continue;
+            }
+
+            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d, 0x%x\n",
+                                  DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname),
+                                  ndx, ccb);
+
+
+            /*
+             * Clean up and re-initialize the proxy address.
+             * It is possible that the REGISTER can be redirected
+             * and this address needs to be reused for later register attempts.
+             * But, on restarts and re-enabling of registering we reset back
+             * to the original configured proxy
+             */
+            if (ndx == REG_CCB_START || ndx == (line_end)) {
+                g_disable_mass_reg_debug_print = FALSE;
+            } else {
+                g_disable_mass_reg_debug_print = TRUE;
+            }
+            sip_sm_call_cleanup(ccb);
+
+            /*
+             *  Look up the name of the proxy
+             */
+            sipTransportGetPrimServerAddress(ccb->dn_line, proxyaddress);
+
+            sstrncpy(ccb->reg.proxy, proxyaddress, MAX_IPADDR_STR_LEN);
+
+            ccb->reg.addr = ccb->dest_sip_addr;
+            ccb->reg.port = (uint16_t) ccb->dest_sip_port;
+
+            if (ccb->index == REG_CCB_START) {
+                ccb->send_reason_header = TRUE;
+            } else {
+                ccb->send_reason_header = FALSE;
+            }
+
+                ui_set_sip_registration_state(ccb->dn_line, FALSE);
+                if (ccsip_register_send_msg(SIP_REG_REQ, ndx) != SIP_REG_OK) {
+                    ccsip_register_cleanup(ccb, TRUE);
+                }
+
+        }
+    }
+    g_disable_mass_reg_debug_print = FALSE;
+
+    sip_platform_cc_mode_notify();
+}
+
+
+boolean
+ccsip_register_all_registered (void)
+{
+    ccsipCCB_t *ccb;
+    line_t ndx;
+    line_t line_end;
+
+    line_end = 1;
+
+    /*
+     * REG CCBs start after the TEL CCBs.
+     * EG., TEL CCBs are at 1 and 2 and REG CCBs are at 3 and 4.
+     * So, line_end equals the number of lines and then add the TEL_CCB_END
+     * to get the ending of the REG CCBs
+     */
+    line_end += TEL_CCB_END;
+
+    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
+        if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
+            ccb = sip_sm_get_ccb_by_index(ndx);
+
+            if (ccb && (ccb->reg.registered == 0)) {
+                return (FALSE);
+            }
+        }
+    }
+    return (TRUE);
+}
+
+
+boolean
+ccsip_register_all_unregistered (void)
+{
+    ccsipCCB_t *ccb;
+    line_t ndx;
+    line_t line_end;
+
+    line_end = 1;
+
+    /*
+     * REG CCBs start after the TEL CCBs.
+     * EG., TEL CCBs are at 0 and 1 and REG CCBs are at 6 and 7.
+     * So, line_end equals the number of lines and then add the TEL_CCB_END
+     * to get the ending of the REG CCBs
+     */
+    line_end += TEL_CCB_END;
+
+    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
+        if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
+            ccb = sip_sm_get_ccb_by_index(ndx);
+
+            if (ccb && (ccb->reg.registered == 1)) {
+                return (FALSE);
+            }
+        }
+    }
+    return (TRUE);
+}
+
+
+void
+ccsip_register_commit (void)
+{
+    static const char fname[] = "ccsip_register_commit";
+    int register_get;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+
+    /*
+     * Determine if lines need to register or unregister
+     *
+     * register if
+     * - proxy_register was changed from 0 to 1
+     *
+     * unregister if
+     * - proxy_register was changed from 1 to 0 and already registered
+     */
+    config_get_value(CFGID_PROXY_REGISTER, &register_get,
+                     sizeof(register_get));
+
+    switch (register_get) {
+    case 0:
+        if (ccsip_register_get_register_state() != SIP_REG_IDLE) {
+            ccsip_register_cancel(TRUE, TRUE);
+        } else {
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"lines already unregistered.\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+        }
+
+        break;
+
+    case 1:
+        if (ccsip_register_get_register_state() != SIP_REG_REGISTERED) {
+            ccsip_register_cancel(FALSE, TRUE);
+            ccsip_register_all_lines();
+        } else {
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"lines already registered.\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+        }
+        break;
+
+    default:
+        CCSIP_DEBUG_ERROR(DEB_F_PREFIX"Error: invalid register_get= %d\n", DEB_F_PREFIX_ARGS(SIP_REG, fname),
+                          register_get);
+    }
+}
+
+void
+ccsip_backup_register_commit (void)
+{
+    static const char fname[] = "ccsip_backup_register_commit";
+    line_t ndx;
+    ccsipCCB_t *ccb = 0;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+
+    ndx = REG_BACKUP_CCB;
+    ccb = sip_sm_get_ccb_by_index(ndx);
+
+    /* cancel an existing registration if it exists */
+    if (util_check_if_ip_valid(&(ccb->reg.addr))) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cancelling registration, line= %d\n",
+                              DEB_F_PREFIX_ARGS(SIP_REG, fname), ccb->index);
+        if (ccsip_register_send_msg(SIP_REG_CANCEL, ndx) != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, FALSE);
+        }
+    }
+
+    /* Cleanup and re-initialize CCB */
+    sip_sm_call_cleanup(ccb);
+
+    if (util_check_if_ip_valid(&(ccb->dest_sip_addr))) {
+        CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d, 0x%x\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname),
+                              ndx, ccb);
+        ccb->reg.addr = ccb->dest_sip_addr;
+        ccb->reg.port = (uint16_t) ccb->dest_sip_port;
+
+        if (ccsip_register_send_msg(SIP_REG_REQ, ndx) != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, TRUE);
+        }
+    } else {
+        log_clear(LOG_REG_BACKUP);
+    }
+}
+
+
+void
+cred_get_line_credentials (line_t line, credentials_t *pcredentials,
+                           int id_len, int pw_len)
+{
+    config_get_line_string(CFGID_LINE_AUTHNAME, pcredentials->id, line,
+                           id_len);
+
+    if ((strcmp(pcredentials->id, "") == 0) ||
+        (strcmp(pcredentials->id, UNPROVISIONED) == 0)) {
+        config_get_line_string((CFGID_LINE_AUTHNAME), pcredentials->id, 1,
+                               id_len);
+    }
+
+    config_get_line_string(CFGID_LINE_PASSWORD, pcredentials->pw, line,
+                           pw_len);
+    if ((strcmp(pcredentials->pw, "") == 0) ||
+        (strcmp(pcredentials->pw, UNPROVISIONED) == 0)) {
+        config_get_line_string(CFGID_LINE_PASSWORD, pcredentials->pw, 1,
+                               pw_len);
+    }
+}
+
+
+boolean
+cred_get_credentials_r (ccsipCCB_t *ccb, credentials_t *pcredentials)
+{
+    if (!(ccb->authen.cred_type & CRED_LINE) ||
+        (ccb->authen.retries_401_407 < MAX_RETRIES_401)) {
+        ccb->authen.cred_type |= CRED_LINE;
+        ccb->authen.retries_401_407++;
+        cred_get_line_credentials(ccb->dn_line, pcredentials,
+                                  sizeof(pcredentials->id),
+                                  sizeof(pcredentials->pw));
+        return (TRUE);
+    }
+
+    return (FALSE);
+}
+
+
+const char *
+ccsip_register_state_name (ccsip_register_states_t state)
+{
+    if ((state < SIP_REG_IDLE) || (state >= SIP_REG_NO_REGISTER)) {
+        return ("UNDEFINED");
+    }
+
+    return (ccsip_register_state_names[state]);
+}
+
+
+const char *
+ccsip_register_reg_state_name (sipRegSMStateType_t state)
+{
+    if ((state < SIP_REG_STATE_NONE) || (state >= SIP_REG_STATE_UNREGISTERING)) {
+        return ("UNDEFINED");
+    }
+
+    return (ccsip_register_reg_state_names[state]);
+}
+
+void
+ccsip_register_shutdown ()
+{
+    int i;
+
+    for (i = 0; i < MAX_REG_LINES + 1; i++) {
+        (void) cprCancelTimer(ack_tmrs[i]);
+        (void) cprDestroyTimer(ack_tmrs[i]);
+        ack_tmrs[i] = NULL;
+    }
+}
+
+boolean
+ccsip_get_ccm_date (char *date_value)
+{
+    if (date_value) {
+        if (ccm_date.valid) {
+            sstrncpy(date_value, ccm_date.datestring,
+                     sizeof(ccm_date.datestring));
+            return (TRUE);
+        } else {
+            date_value[0] = '\0';
+        }
+    }
+    return (FALSE);
+}
+
+/*
+ *  Function: ccsip_is_line_registered
+ *
+ *  Parameters:
+ *      dn_line - line number.
+ *
+ *  Description:  The function determines whether the current line
+ *      is registered or not.
+ *
+ *  Returns:
+ *      TRUE if the line is registered.
+ *      FALSE if the line is not registered or the given line is invalid.
+ *
+ */
+boolean
+ccsip_is_line_registered (line_t dn_line)
+{
+    line_t ndx;
+    line_t line_end;
+    ccsipCCB_t *ccb;
+
+    /* Get the number of lines configured */
+    line_end = 1;
+    /*
+     * REG CCBs start after the TEL CCBs.
+     * So, line_end equals the number of lines and then add the TEL_CCB_END
+     * to get the ending of the REG CCBs
+     */
+    line_end += TEL_CCB_END;
+
+    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
+        ccb = sip_sm_get_ccb_by_index(ndx);
+        if ((ccb != NULL) && (ccb->dn_line == dn_line)) {
+            /* found the requested REG ccb */
+            if (ccb->state == (int) SIP_REG_STATE_REGISTERED) {
+                return (TRUE);
+            } else {
+                /* the line is not in registered state */
+                break;
+            }
+        }
+    }
+
+    /* Found no matching REG ccb or the line is not in registered state */
+    return (FALSE);
+}
+
+/*
+ *  Function: process_retry_after
+ *
+ *  Parameters:
+ *      ccsipCCB_t *ccb - The reg ccb.
+ *      sipMessage_t *response - The received response.
+ *
+ *  Description:  The function processes the Retry-After header that may
+ *       be present in the response.
+ *
+ *  Returns:
+ *      TRUE if a valid Retry-After header was present and processed correctly.
+ *      FALSE if a valid Retry-After header was not present and therefore
+ *            was not processed.
+ *
+ */
+boolean
+process_retry_after (ccsipCCB_t *ccb, sipMessage_t *response)
+{
+    const char *msg_ptr = NULL;
+    int32_t    retry_after = 0;
+    static const char fname[] = "process_retry_after";
+
+    msg_ptr = sippmh_get_header_val(response,
+                                    (const char *)SIP_HEADER_RETRY_AFTER,
+                                    NULL);
+
+    if (msg_ptr) {
+        retry_after = strtoul(msg_ptr, NULL, 10);
+
+    } else {
+        return (FALSE);
+    }
+
+    if (retry_after > 0) {
+        sip_stop_ack_timer(ccb);
+        (void) sip_platform_register_expires_timer_start(retry_after * 1000,
+                                                         ccb->index);
+        CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Retrying after %d\n",
+                              DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname), retry_after);
+        return (TRUE);
+    } else {
+        CCSIP_DEBUG_ERROR("REG %d/%d: %-35s: Error: invalid Retry-After "
+                          "header in response.\n",
+                          ccb->index, ccb->dn_line, fname);
+        return (FALSE);
+    }
+}
+
+
diff --git a/libs/sipcc/core/sipstack/ccsip_reldev.c b/libs/sipcc/core/sipstack/ccsip_reldev.c
new file mode 100644 (file)
index 0000000..24d19ab
--- /dev/null
@@ -0,0 +1,284 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_string.h"
+#include "ccsip_reldev.h"
+#include "ccsip_macros.h"
+#include "phone_debug.h"
+#include "ccsip_pmh.h"
+#include "ccsip_messaging.h"
+#include "sip_common_transport.h"
+#include "util_string.h"
+
+/* Constants */
+#define SIP_RRLIST_LENGTH (MAX_TEL_LINES)
+
+/* Global variables */
+sipRelDevMessageRecord_t gSIPRRList[SIP_RRLIST_LENGTH];
+
+
+void
+sipRelDevMessageStore (sipRelDevMessageRecord_t * pMessageRecord)
+{
+    static int counter = 0;
+
+    /* Loop around */
+    if (counter > (SIP_RRLIST_LENGTH - 1)) {
+        counter = 0;
+    }
+
+    gSIPRRList[counter] = *pMessageRecord;
+    gSIPRRList[counter].valid_coupled_message = FALSE;
+    counter++;
+}
+
+
+boolean
+sipRelDevMessageIsDuplicate (sipRelDevMessageRecord_t *pMessageRecord,
+                             int *idx)
+{
+    int i = 0;
+
+    for (i = 0; i < SIP_RRLIST_LENGTH; i++) {
+        if ((strcmp(pMessageRecord->call_id, gSIPRRList[i].call_id) == 0) &&
+            (pMessageRecord->cseq_number == gSIPRRList[i].cseq_number) &&
+            (pMessageRecord->cseq_method == gSIPRRList[i].cseq_method) &&
+            (strcasecmp_ignorewhitespace(pMessageRecord->tag, gSIPRRList[i].tag) == 0) &&
+            (strcmp(pMessageRecord->from_user, gSIPRRList[i].from_user) == 0) &&
+            (strcmp(pMessageRecord->from_host, gSIPRRList[i].from_host) == 0) &&
+            (strcmp(pMessageRecord->to_user, gSIPRRList[i].to_user) == 0)) {
+
+            if (pMessageRecord->is_request) {
+                *idx = i;
+                return (TRUE);
+            } else {
+                if (pMessageRecord->response_code == gSIPRRList[i].response_code) {
+                    *idx = i;
+                    return (TRUE);
+                }
+            }
+        }
+    }
+
+    *idx = -1;
+    return (FALSE);
+}
+
+
+/*
+ *  Function: sipRelDevCoupledMessageStore
+ *
+ *  Parameters:
+ *      pCoupledMessage - pointer to sipMessage_t for the message that
+ *                        needed reliable delivery store.
+ *      call_id         - pointer to const. char for the SIP call ID.
+ *      cseq_number     - the uint32_t for CSeq of the message.
+ *      sipMethod_t     - sipMethod_t the SIP method of the message.
+ *      dest_ipaddr     - uint32_t IP address of the destination address.
+ *      dest_port       - uint16_t destination port
+ *      ignore_tag      - boolean to ignore tag.
+ *
+ *  Description:
+ *      The function finds the corresponding entry in the gSIPRRList
+ *  array that matches call_id, cseq number, method and possibly tag.
+ *  If a match entry is found, the SIP message is composed and stored
+ *  in the corresponding entry.
+ *
+ *  Returns:
+ *      idx - When the entry is found and successfully stored the
+ *              SIP message.
+ *      RELDEV_NO_STORED_MSG - encounter errors or no message is
+ *                             stored.
+ */
+int
+sipRelDevCoupledMessageStore (sipMessage_t *pCoupledMessage,
+                              const char *call_id,
+                              uint32_t cseq_number,
+                              sipMethod_t cseq_method,
+                              boolean is_request,
+                              int response_code,
+                              cpr_ip_addr_t *dest_ipaddr,
+                              uint16_t dest_port,
+                              boolean ignore_tag)
+{
+    static const char *fname = "sipRelDevCoupledMessageStore";
+    int i = 0;
+    char to_tag[MAX_SIP_TAG_LENGTH];
+
+    sipGetMessageToTag(pCoupledMessage, to_tag, MAX_SIP_TAG_LENGTH);
+    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Storing for reTx (cseq=%d, method=%s, "
+                        "to_tag=<%s>)\n", DEB_F_PREFIX_ARGS(SIP_STORE, fname), cseq_number,
+                        sipGetMethodString(cseq_method), to_tag);
+
+    for (i = 0; i < SIP_RRLIST_LENGTH; i++) {
+        if ((strcmp(call_id, gSIPRRList[i].call_id) == 0) &&
+            (cseq_number == gSIPRRList[i].cseq_number) &&
+            (cseq_method == gSIPRRList[i].cseq_method) &&
+            ((ignore_tag) ? TRUE : (strcasecmp_ignorewhitespace(to_tag,
+                                                     gSIPRRList[i].tag)
+                                    == 0))) {
+            hStatus_t sippmh_write_status = STATUS_FAILURE;
+            uint32_t nbytes = SIP_UDP_MESSAGE_SIZE;
+
+            /*
+              When storing the ACK message check that you are storing it
+              coupled with the correct response code and not transitional responses
+            */
+            if (is_request == FALSE ||
+                (is_request == TRUE && gSIPRRList[i].response_code == response_code)) {
+                gSIPRRList[i].coupled_message.message_buf[0] = '\0';
+                sippmh_write_status =
+                    sippmh_write(pCoupledMessage,
+                                 gSIPRRList[i].coupled_message.message_buf,
+                                 &nbytes);
+                if (sippmh_write_status == STATUS_FAILURE) {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sippmh_write() failed.\n", fname);
+                    return (RELDEV_NO_STORED_MSG);
+                }
+                if ((gSIPRRList[i].coupled_message.message_buf[0] == '\0') ||
+                    (nbytes == 0)) {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sippmh_write() returned empty buffer string.\n",
+                                      fname);
+                    return (RELDEV_NO_STORED_MSG);
+                }
+                gSIPRRList[i].coupled_message.message_buf_len = nbytes;
+                gSIPRRList[i].coupled_message.dest_ipaddr = *dest_ipaddr;
+                gSIPRRList[i].coupled_message.dest_port = dest_port;
+                gSIPRRList[i].valid_coupled_message = TRUE;
+                /* Return the stored idx to the caller */
+                return (i);
+            }
+        }
+    }
+    return (RELDEV_NO_STORED_MSG);
+}
+
+/*
+ *  Function: sipRelDevGetStoredCoupledMessage
+ *
+ *  Parameters:
+ *      idx         - int for the idx to retrieve stored SIP message.
+ *      dest_buffer   - pointer to char to retrieve the copy of the
+ *                      stored SIP message.
+ *      dest_buf_size - uint32_t for the size of the dest_buffer.
+ *
+ *  Description:
+ *      The function gets the stored SIP message for the request entry.
+ *
+ *  Returns:
+ *      The function returns the number of the bytes of the SIP message
+ *  obtained if SIP message is found otherwise it returns zero (
+ *  no SIP message available).
+ */
+uint32_t
+sipRelDevGetStoredCoupledMessage (int idx,
+                                  char *dest_buffer,
+                                  uint32_t dest_buf_size)
+{
+    sipRelDevMessageRecord_t *record;
+
+    if (dest_buffer == NULL) {
+        /* No destination buffer given, can not provide any result */
+        return (0);
+    }
+    if ((idx < 0) || (idx >= SIP_RRLIST_LENGTH)) {
+        /* the stored message is out of range */
+        return (0);
+    }
+
+    record = &gSIPRRList[idx];
+
+    if (!record->valid_coupled_message) {
+        /* No message stored for the given idx */
+        return (0);
+    }
+    if ((record->coupled_message.message_buf_len > dest_buf_size) ||
+        (record->coupled_message.message_buf_len == 0)) {
+        /*
+         * The stored message size is larger than the give buffer or
+         * stored message length is zero?
+         */
+        return (0);
+    }
+    /* Get the stored message to the caller */
+    memcpy(dest_buffer, &record->coupled_message.message_buf[0],
+           record->coupled_message.message_buf_len);
+    return (record->coupled_message.message_buf_len);
+}
+
+int
+sipRelDevCoupledMessageSend (int idx)
+{
+    static const char *fname = "sipRelDevCoupledMessageSend";
+    char dest_ipaddr_str[MAX_IPADDR_STR_LEN];
+
+    if ((idx < 0) || (idx >= SIP_RRLIST_LENGTH)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Argument Check: idx (=%d) out of bounds.\n",
+                          fname, idx);
+        return SIP_ERROR;
+    }
+
+    if (gSIPRRList[idx].valid_coupled_message) {
+        ipaddr2dotted(dest_ipaddr_str,
+                      &gSIPRRList[idx].coupled_message.dest_ipaddr);
+
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Sending stored coupled message (idx=%d) to "
+                            "<%s>:<%d>\n", DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname), idx, dest_ipaddr_str,
+                            gSIPRRList[idx].coupled_message.dest_port);
+        if (sipTransportChannelSend(NULL,
+                       gSIPRRList[idx].coupled_message.message_buf,
+                       gSIPRRList[idx].coupled_message.message_buf_len,
+                       sipMethodInvalid,
+                       &(gSIPRRList[idx].coupled_message.dest_ipaddr),
+                       gSIPRRList[idx].coupled_message.dest_port,
+                       0) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipTransportChannelSend() failed."
+                              " Stored message not sent.\n", fname);
+            return SIP_ERROR;
+        }
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Duplicate message detected but failed to"
+                          " find valid coupled message. Stored message not sent.\n",
+                          fname);
+        return SIP_ERROR;
+    }
+    return SIP_OK;
+}
+
+
+/*
+ * Function for clearing all the messages belonging to the
+ * associated with the specified call_id, from_user, from_host,
+ * and to_user
+ */
+void
+sipRelDevMessagesClear (const char *call_id,
+                        const char *from_user,
+                        const char *from_host,
+                        const char *to_user)
+{
+    int i = 0;
+
+    for (i = 0; i < SIP_RRLIST_LENGTH; i++) {
+        if ((strcmp(call_id, gSIPRRList[i].call_id) == 0) &&
+            (strcmp(from_user, gSIPRRList[i].from_user) == 0) &&
+            (strcmp(from_host, gSIPRRList[i].from_host) == 0) &&
+            (strcmp(to_user, gSIPRRList[i].to_user) == 0)) {
+            memset(&gSIPRRList[i], 0, sizeof(sipRelDevMessageRecord_t));
+        }
+    }
+    return;
+}
+
+/*
+ * Function for clearing all the messages
+ */
+void sipRelDevAllMessagesClear(){
+       int i = 0;
+       for (i = 0; i < SIP_RRLIST_LENGTH; i++) {
+               memset(&gSIPRRList[i], 0, sizeof(sipRelDevMessageRecord_t));
+       }
+       return;
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_sdp.c b/libs/sipcc/core/sipstack/ccsip_sdp.c
new file mode 100644 (file)
index 0000000..b0c9514
--- /dev/null
@@ -0,0 +1,354 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implements functions to parse and create SDP(Session Description Protocol)
+ * (RFC 2327) messages. Coded to be sufficient for SIP only.
+ * Note: The SDP body fields as per the RFC are :
+ * Optional items are marked with a `*'.
+ *
+ * Session description
+ *       v=  (protocol version)
+ *       o=  (owner/creator and session identifier).
+ *       s=  (session name)
+ *       i=* (session information)
+ *       u=* (URI of description)
+ *       e=* (email address)
+ *       p=* (phone number)
+ *       c=* (connection information - not required if included in all media)
+ *       b=* (bandwidth information)
+ *       One or more time descriptions (see below)
+ *       z=* (time zone adjustments)
+ *       k=* (encryption key)
+ *       a=* (zero or more session attribute lines)
+ *       Zero or more media descriptions (see below)
+ *
+ *Time description
+ *       t=  (time the session is active)
+ *       r=* (zero or more repeat times)
+ *
+ *Media description
+ *       m=  (media name and transport address)
+ *       i=* (media title)
+ *       c=* (connection information - optional if included at session-level)
+ *       b=* (bandwidth information)
+ *       k=* (encryption key)
+ *       a=* (zero or more media attribute lines)
+ *
+ * Note that the mandatory fields are :
+ *  v= , o=, s=, t=, m=
+ * Email address and phone number (e=, p=) fields would very soon be useful
+ * to SDP in large scale SIP networks.
+ *
+ * t= 0 0
+ * would indicate an unbound or a permanent session . This would be changed
+ * to specific (or probably user-configured values) once the time duration
+ * of SIP call sessions are recorded and are used for scheduling.
+ */
+
+
+#include "cpr_types.h"
+#include "cpr_memory.h"
+#include "sdp.h"
+#include "text_strings.h"
+#include "ccsip_sdp.h"
+#include "ccsip_core.h"
+#include "phone_debug.h"
+
+
+
+static void *ccsip_sdp_config = NULL; /* SDP parser/builder configuration */
+
+/*
+ * sip_sdp_init
+ *
+ * This function initializes SDP parser with SIP-specific parameters.
+ * This includes supported media, network types, address types,
+ * transports and codecs.
+ *
+ * The function must be called once.
+ *
+ * Returns TRUE  - successful
+ *         FALSE - failed
+ */
+boolean
+sip_sdp_init (void)
+{
+    ccsip_sdp_config = sdp_init_config();
+    if (!ccsip_sdp_config) {
+        CCSIP_ERR_MSG("sdp_init_config() failure");
+        return FALSE;
+    }
+
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_AUDIO, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_VIDEO, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_APPLICATION, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_DATA, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_CONTROL, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_NAS_RADIUS, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_NAS_TACACS, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_NAS_DIAMETER, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_NAS_L2TP, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_NAS_LOGIN, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_NAS_NONE, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_IMAGE, TRUE);
+    sdp_media_supported(ccsip_sdp_config, SDP_MEDIA_TEXT, TRUE);
+    sdp_nettype_supported(ccsip_sdp_config, SDP_NT_INTERNET, TRUE);
+    sdp_addrtype_supported(ccsip_sdp_config, SDP_AT_IP4, TRUE);
+    sdp_addrtype_supported(ccsip_sdp_config, SDP_AT_IP6, TRUE);
+    sdp_transport_supported(ccsip_sdp_config, SDP_TRANSPORT_RTPAVP, TRUE);
+    sdp_transport_supported(ccsip_sdp_config, SDP_TRANSPORT_UDPTL, TRUE);
+    sdp_require_session_name(ccsip_sdp_config, FALSE);
+
+    return (TRUE);
+}
+
+/*
+ * sipsdp_create()
+ *
+ * Allocate a standard SDP with SIP config and set debug options
+ * based on ccsip debug settings
+ */
+sdp_t *
+sipsdp_create (const char *peerconnection)
+{
+    sdp_t *sdp;
+
+    sdp = sdp_init_description(peerconnection, ccsip_sdp_config);
+    if (!sdp) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SDP allocation failure\n", __FUNCTION__);
+        return (NULL);
+    }
+
+    /*
+     * Map "ccsip debug events (or info)" to SDP warnings
+     *     "ccsip debug errors to SDP errors
+     */
+    CCSIP_INFO_DEBUG {
+        sdp_debug(sdp, SDP_DEBUG_WARNINGS, TRUE);
+    }
+
+    CCSIP_ERR_DEBUG {
+        sdp_debug(sdp, SDP_DEBUG_ERRORS, TRUE);
+    }
+
+
+#ifdef DEBUG_SDP_LIB
+    /*
+     * Enabling this is redundant to the existing message printing
+     * available for the entire SIP text messages, so it is not
+     * enabled here. It is useful for debugging the SDP parser,
+     * however.
+     */
+    CCSIP_MESSAGES_DEBUG {
+        sdp_debug(sdp, SDP_DEBUG_TRACE, TRUE);
+    }
+#endif
+
+    return (sdp);
+}
+
+/*
+ * sipsdp_free()
+ *
+ * Frees the SIP SDP structure, the contained common SDP structure,
+ * and each stream structure in the linked stream list.
+ *
+ * sip_sdp is set to NULL after being freed.
+ */
+void
+sipsdp_free (cc_sdp_t **sip_sdp)
+{
+    const char *fname = "sipsdp_free: ";
+    sdp_result_e sdp_ret;
+
+    if (!*sip_sdp) {
+        return;
+    }
+
+    if ((*sip_sdp)->src_sdp) {
+        sdp_ret = sdp_free_description((*sip_sdp)->src_sdp);
+        if (sdp_ret != SDP_SUCCESS) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"%d while freeing src_sdp\n",
+                          fname, sdp_ret);
+        }
+    }
+    if ((*sip_sdp)->dest_sdp) {
+        sdp_ret = sdp_free_description((*sip_sdp)->dest_sdp);
+        if (sdp_ret != SDP_SUCCESS) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"%d while freeing dest_sdp\n",
+                          fname, sdp_ret);
+        }
+    }
+
+    SDP_FREE(*sip_sdp);
+}
+
+/*
+ * sipsdp_info_create()
+ *
+ * Allocates and initializes a SIP SDP structure.  This function does
+ * not create the src_sdp, dest_sdp, or streams list.
+ *
+ * Returns: pointer to SIP SDP structure - if successful
+ *          NULL                         - failure to allocate storage
+ */
+cc_sdp_t *
+sipsdp_info_create (void)
+{
+    cc_sdp_t *sdp_info = (cc_sdp_t *) cpr_malloc(sizeof(cc_sdp_t));
+
+    if (sdp_info) {
+        sdp_info->src_sdp = NULL;
+        sdp_info->dest_sdp = NULL;
+    }
+
+    return (sdp_info);
+}
+
+
+/*
+ * sipsdp_src_dest_free()
+ *
+ * Frees the SRC and/or DEST SDP.  It will also free the sdp_info
+ * structure if both SRC and DEST SDP are freed.
+ *
+ * Input:
+ *   flags   - bitmask indicating if src and/or dest sdp should be
+ *             freed
+ *
+ * Returns:
+ *   sdp_info is set to NULL if freed.
+ */
+void
+sipsdp_src_dest_free (uint16_t flags, cc_sdp_t **sdp_info)
+{
+    const char *fname = "sipsdp_src_dest_free: ";
+    sdp_result_e sdp_ret;
+
+    if ((sdp_info == NULL) || (*sdp_info == NULL)) {
+        return;
+    }
+
+    /* Free the SRC and/or DEST SDP */
+    if (flags & CCSIP_SRC_SDP_BIT) {
+        if ((*sdp_info)->src_sdp) {
+            sdp_ret = sdp_free_description((*sdp_info)->src_sdp);
+            if (sdp_ret != SDP_SUCCESS) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"%d while freeing src_sdp\n",
+                              fname, sdp_ret);
+            }
+            (*sdp_info)->src_sdp = NULL;
+        }
+    }
+
+    if (flags & CCSIP_DEST_SDP_BIT) {
+        if ((*sdp_info)->dest_sdp) {
+            sdp_ret = sdp_free_description((*sdp_info)->dest_sdp);
+            if (sdp_ret != SDP_SUCCESS) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"%d while freeing dest_sdp\n",
+                              fname, sdp_ret);
+            }
+            (*sdp_info)->dest_sdp = NULL;
+        }
+
+    }
+
+    /*
+     * If both src and dest sdp are NULL, there is no need to keep the
+     * sdp_info structure around.  Free it.
+     */
+    if (((*sdp_info)->src_sdp == NULL) && ((*sdp_info)->dest_sdp == NULL)) {
+        sipsdp_free(sdp_info);
+        *sdp_info = NULL;
+    }
+}
+
+
+/*
+ * sipsdp_src_dest_create()
+ *
+ * Allocates and initializes a SRC and/or DEST SDP.  If the sdp_info
+ * structure has not yet been allocated, it is allocated here.
+ *
+ * Input:
+ *   flags   - bitmask indicating if src and/or dest sdp should be
+ *             created
+ *
+ * Returns: pointer to SIP SDP structure - if successful
+ *          NULL                         - failure to allocate storage
+ */
+void
+sipsdp_src_dest_create (const char *peerconnection,
+    uint16_t flags, cc_sdp_t **sdp_info)
+{
+
+    if (!(*sdp_info)) {
+        *sdp_info = sipsdp_info_create();
+        if (!(*sdp_info)) {
+            return;
+        }
+    }
+
+    /* Create the SRC and/or DEST SDP */
+    if (flags & CCSIP_SRC_SDP_BIT) {
+        (*sdp_info)->src_sdp = sipsdp_create(peerconnection);
+        if (!((*sdp_info)->src_sdp)) {
+            sipsdp_src_dest_free(flags, sdp_info);
+            return;
+        }
+    }
+
+    if (flags & CCSIP_DEST_SDP_BIT) {
+        (*sdp_info)->dest_sdp = sipsdp_create(peerconnection);
+        if (!((*sdp_info)->dest_sdp)) {
+            sipsdp_src_dest_free(flags, sdp_info);
+            return;
+        }
+    }
+}
+
+/*
+ * sipsdp_write_to_buf()
+ *
+ * This function builds the specified SDP in a text buffer and returns
+ * a pointer to this buffer.
+ *
+ * Returns: pointer to buffer - no errors
+ *          NULL              - errors were encountered while building
+ *                              the SDP. The details of the build failure
+ *                              can be determined by enabling SDP debugs
+ *                              and by examining SDP library error counters.
+ */
+char *
+sipsdp_write_to_buf (cc_sdp_t *sdp_info, uint32_t *retbytes)
+{
+    flex_string fs;
+    uint32_t sdp_len;
+    sdp_result_e rc;
+
+    flex_string_init(&fs);
+
+    if (!sdp_info || !sdp_info->src_sdp) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"NULL sdp_info or src_sdp\n", __FUNCTION__);
+        return (NULL);
+    }
+
+    if ((rc = sdp_build(sdp_info->src_sdp, &fs))
+        != SDP_SUCCESS) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"sdp_build rc=%s\n", DEB_F_PREFIX_ARGS(SIP_SDP, __FUNCTION__),
+                         sdp_get_result_name(rc));
+
+        flex_string_free(&fs);
+        *retbytes = 0;
+        return (NULL);
+    }
+
+    *retbytes = fs.string_length;
+
+    /* We are not calling flex_string_free on this, instead returning the buffer
+     * caller's responsibility to free
+     */
+    return fs.buffer;
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_spi_utils.c b/libs/sipcc/core/sipstack/ccsip_spi_utils.c
new file mode 100755 (executable)
index 0000000..97ce502
--- /dev/null
@@ -0,0 +1,134 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file implements a bunch of stuff used only by ccsip_spi.c,
+ * and this is done mostly to reduce the file size and some extra
+ * modularity. Includes things like tables/trees to hold SIP and
+ * CCAPI callids, conversion from CCAPI to SIP cause codes and the
+ * like.
+ */
+#include "plstr.h"
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "pmhutils.h"
+#include "ccsip_spi_utils.h"
+#include "util_string.h"
+#include "ccsip_core.h"
+
+extern uint32_t IPNameCk(char *name, char *addr_error);
+
+/*
+ * Checks a token which forms a part of the
+ * domain-name and makes sure that it conforms
+ * to the grammar specified in RFC 1034 (DNS)
+ */
+int
+sipSPICheckDomainToken (char *token)
+{
+    if (token == NULL) {
+        return FALSE;
+    }
+
+    if (*token) {
+        if (isalnum((int)token[strlen(token) - 1]) == FALSE) {
+            return FALSE;
+        }
+    }
+
+    if (isalnum((int)token[0]) == FALSE) {
+        return FALSE;
+    }
+
+    while (*token) {
+        if ((isalnum((int)*token) == FALSE) && (*token != '-')) {
+            return FALSE;
+        }
+        token++;
+    }
+    return TRUE;
+}
+
+/*
+ * Checks domain-name is valid syntax-wise
+ * Returns TRUE, if valid
+ */
+boolean
+sipSPI_validate_hostname (char *str)
+{
+    char *tok;
+    char ip_addr_out[MAX_IPADDR_STR_LEN];
+    char *strtok_state;
+
+    if (str == NULL) {
+        return FALSE;
+    }
+
+    /* Check if valid IPv6 address */
+    if (cpr_inet_pton(AF_INET6, str, ip_addr_out)) {
+        return TRUE;
+    }
+
+    if (*str) {
+        if (isalnum((int)str[strlen(str) - 1]) == FALSE) {
+            return FALSE;
+        }
+    }
+
+    if (isalnum((int)str[0]) == FALSE) {
+        return FALSE;
+    }
+
+    tok = PL_strtok_r(str, ".", &strtok_state);
+    if (tok == NULL) {
+        return FALSE;
+    } else {
+        if (sipSPICheckDomainToken(tok) == FALSE) {
+            return FALSE;
+        }
+    }
+
+    while ((tok = PL_strtok_r(NULL, ".", &strtok_state)) != NULL) {
+        if (sipSPICheckDomainToken(tok) == FALSE) {
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+/*
+ * Determines if the  IP address or domain-name is
+ * valid (syntax-wise only)
+ * Returns TRUE, if valid
+ */
+boolean
+sipSPI_validate_ip_addr_name (char *str)
+{
+    uint32_t sip_address;
+    boolean  retval = TRUE;
+    char    *target = NULL;
+    char     addr_error;
+
+    if (str == NULL) {
+        return FALSE;
+    } else {
+        target = cpr_strdup(str);
+        if (!target) {
+            return FALSE;
+        }
+    }
+    sip_address = IPNameCk(target, &addr_error);
+    if ((!sip_address) && (addr_error)) {
+        cpr_free(target);
+        return retval;
+    } else {
+        if (sipSPI_validate_hostname(target) == FALSE) {
+            retval = FALSE;
+        }
+    }
+    cpr_free(target);
+    return retval;
+}
diff --git a/libs/sipcc/core/sipstack/ccsip_subsmanager.c b/libs/sipcc/core/sipstack/ccsip_subsmanager.c
new file mode 100644 (file)
index 0000000..5189eb2
--- /dev/null
@@ -0,0 +1,5290 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_in.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_rand.h"
+#include "ccsip_subsmanager.h"
+#include "util_string.h"
+#include "ccapi.h"
+#include "phone_debug.h"
+#include "ccsip_messaging.h"
+#include "ccsip_platform_udp.h"
+#include "ccsip_macros.h"
+#include "ccsip_task.h"
+#include "sip_common_transport.h"
+#include "ccsip_platform_timers.h"
+#include "platform_api.h"
+#include "ccsip_register.h"
+#include "ccsip_reldev.h"
+#include "phntask.h"
+#include "subapi.h"
+#include "debug.h"
+#include "ccsip_callinfo.h"
+#include "regmgrapi.h"
+#include "text_strings.h"
+#include "configapp.h"
+#include "kpmlmap.h"
+
+/*
+ *  Global Variables
+ */
+sipSCB_t subsManagerSCBS[MAX_SCBS]; // Array of SCBS
+sipSubsHistory_t gSubHistory[MAX_SCB_HISTORY];
+sipTimerCallbackFn_t callbackFunctionSubNot = sip_platform_subnot_msg_timer_callback;
+sipTimerCallbackFn_t callbackFunctionPeriodic = sip_platform_subnot_periodic_timer_callback;
+extern sipPlatformUITimer_t sipPlatformUISMSubNotTimers[]; // Array of timers
+const char kpmlRequestAcceptHeader[]      = SIP_CONTENT_TYPE_KPML_REQUEST;
+const char kpmlResponseAcceptHeader[]     = SIP_CONTENT_TYPE_KPML_RESPONSE;
+const char dialogAcceptHeader[]           = SIP_CONTENT_TYPE_DIALOG;
+const char presenceAcceptHeader[]         = "application/cpim-pidf+xml";
+const char remoteccResponseAcceptHeader[] = SIP_CONTENT_TYPE_REMOTECC_RESPONSE;
+const char remoteccRequestAcceptHeader[]  = SIP_CONTENT_TYPE_REMOTECC_REQUEST;
+
+static sll_handle_t s_TCB_list = NULL; // signly linked list handle of TCBs
+
+// Externs
+extern int dns_error_code; // Global DNS error code
+
+// Status and statistics for the Subscription Manager
+int subsManagerRunning = 0;
+int internalRegistrations = 0;
+
+int incomingSubscribes = 0;
+int incomingRefers = 0;
+int incomingNotifies = 0;
+int incomingUnsolicitedNotifies = 0;
+int incomingSubscriptions = 0;
+
+int outgoingSubscribes = 0;
+int outgoingNotifies = 0;
+int outgoingUnsolicitedNotifies = 0;
+int outgoingSubscriptions = 0;
+
+int currentScbsAllocated = 0;
+int maxScbsAllocated = 0;
+
+#define MAX_SUB_EVENTS    5
+#define MAX_SUB_EVENT_NAME_LEN 16
+/*
+ * sub_id format:
+ *
+ * sub_id is a 32 bit quantity. The bit 32-16 holds a unique ID and
+ * the bits 15-0 holds a SCB index. The following definitions help
+ * support this formation.
+ *
+ * Assumption: The above format assumes that SCB index never be
+ *             larger than 16 bit unsigned value.
+ */
+#define SUB_ID_UNIQUE_ID_POSITION 16     /* shift position of unique id part*/
+#define SUB_IDSCB_INDEX_MASK      0xffff /* mask for obtaining scb index    */
+#define GET_SCB_INDEX_FROM_SUB_ID(sub_id) \
+     (sub_id & SUB_IDSCB_INDEX_MASK)
+
+/*
+ *  Event names
+ */
+const char eventNames[MAX_SUB_EVENTS][MAX_SUB_EVENT_NAME_LEN] =
+{
+    "dialog",
+    "sip-profile",
+    "kpml",
+    "presence",
+    "refer"
+};
+
+/*
+ * Forward Function Declarations
+ */
+boolean sipSPISendSubscribe(sipSCB_t *scbp, boolean renew, boolean authen);
+boolean sipSPISendSubNotify(ccsip_common_cb_t *cbp, boolean authen);
+boolean sipSPISendSubscribeNotifyResponse(sipSCB_t *scbp,
+                                          uint16_t response_code,
+                                          uint32_t cseq);
+boolean sipSPISendSubNotifyResponse(sipSCB_t *scbp, int response_code);
+void free_scb(int scb_index, const char *fname);
+
+// External Declarations
+extern uint32_t IPNameCk(char *name, char *addr_error);
+extern void kpml_init(void);
+extern void kpml_shutdown(void);
+
+
+extern void sip_platform_handle_service_control_notify(sipServiceControl_t * scp);
+cc_int32_t show_subsmanager_stats(cc_int32_t argc, const char *argv[]);
+static void show_scbs_inuse(void);
+static void tcb_reset(void);
+
+typedef struct {
+   unsigned long  cseq;
+   char           *via;
+} sub_not_trxn_t;
+
+/*
+ * Function: find_matching_trxn()
+ *
+ * Parameters: key - key to find the matching node.
+ *             data - node data.
+ *
+ * Descriotion: is invoked by sll_find() to find the matching node based on the key.
+ *
+ * Returns: either SLL_MATCH_FOUND or SLL_MATCH_NOT_FOUND.
+ */
+static sll_match_e
+find_matching_trxn (void *key, void *data)
+{
+    unsigned long cseq = *((unsigned long *)key);
+    sub_not_trxn_t *trxn_p = (sub_not_trxn_t *) data;
+
+    if (cseq == trxn_p->cseq) {
+        return SLL_MATCH_FOUND;
+    }
+
+    return SLL_MATCH_NOT_FOUND;
+}
+
+/*
+ * Function: store_incoming_trxn()
+ *
+ * Description: stores the via header in incoming_trxns
+ *
+ * Parameters: via, cseq and scbp
+ *
+ * Returns: TRUE if the via header is successfully stored,
+ *          FALSE otherwise.
+ */
+static boolean
+store_incoming_trxn (const char *via, unsigned long cseq, sipSCB_t *scbp)
+{
+    static const char *fname = "store_incoming_trxn";
+    size_t size = 0;
+
+    sub_not_trxn_t *sub_not_trxn_p;
+
+    if (scbp->incoming_trxns == NULL) {
+        scbp->incoming_trxns = sll_create(find_matching_trxn);
+        if (scbp->incoming_trxns == NULL) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sll_create() failed\n", fname);
+            return FALSE;
+        }
+    }
+    sub_not_trxn_p = (sub_not_trxn_t *)cpr_malloc(sizeof(sub_not_trxn_t));
+    if (sub_not_trxn_p == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed\n", fname);
+        return FALSE;
+    }
+    sub_not_trxn_p->cseq = cseq;
+    size = strlen(via) + 1;
+    sub_not_trxn_p->via = (char *) cpr_malloc(size);
+    if (sub_not_trxn_p->via) {
+        sstrncpy(sub_not_trxn_p->via, via, size);
+    }
+    (void) sll_append(scbp->incoming_trxns, sub_not_trxn_p);
+
+    return TRUE;
+}
+
+void
+free_event_data (ccsip_event_data_t *event_data)
+{
+    ccsip_event_data_t *tmp;
+
+    if (event_data == NULL) {
+        return;
+    }
+
+    while (event_data != NULL) {
+        tmp = event_data->next;
+        if (event_data->type == EVENT_DATA_RAW) {
+            if (event_data->u.raw_data.data)
+                cpr_free(event_data->u.raw_data.data);
+        }
+        cpr_free(event_data);
+        event_data = tmp;
+    }
+}
+
+void
+append_event_data (ccsip_event_data_t * event_data,
+                   ccsip_event_data_t *new_data)
+{
+    while (event_data->next != NULL) {
+        event_data = event_data->next;
+    }
+    event_data->next = new_data;
+    new_data->next = NULL;
+}
+
+void
+free_pending_requests (sipspi_msg_list_t *pendingRequests)
+{
+    sipspi_msg_list_t *tmp;
+
+    while (pendingRequests) {
+        switch (pendingRequests->cmd) {
+        case SIPSPI_EV_CC_NOTIFY:
+            {
+                sipspi_notify_t *notify = &(pendingRequests->msg->msg.notify);
+
+                free_event_data(notify->eventData);
+                cpr_free(pendingRequests->msg);
+            }
+            break;
+        case SIPSPI_EV_CC_SUBSCRIBE_REGISTER:
+            cpr_free(&pendingRequests->msg->msg.subs_reg);
+            break;
+        case SIPSPI_EV_CC_SUBSCRIBE:
+            {
+                sipspi_subscribe_t *subs = &(pendingRequests->msg->msg.subscribe);
+
+                free_event_data(subs->eventData);
+                cpr_free(pendingRequests->msg);
+            }
+            break;
+        case SIPSPI_EV_CC_SUBSCRIBE_RESPONSE:
+            cpr_free(pendingRequests->msg);
+            break;
+        case SIPSPI_EV_CC_NOTIFY_RESPONSE:
+            cpr_free(pendingRequests->msg);
+            break;
+        case SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED:
+            cpr_free(pendingRequests->msg);
+            break;
+        default:
+            break;
+        }
+        tmp = pendingRequests;
+        pendingRequests = pendingRequests->next;
+        cpr_free(tmp);
+    }
+}
+
+boolean
+append_pending_requests (sipSCB_t *scbp,
+                         sipspi_msg_t *newRequest,
+                         uint32_t cmd)
+{
+    static const char *fname = "append_pending_requests";
+    sipspi_msg_list_t *pendingRequest = NULL;
+    sipspi_msg_list_t *tmp = NULL;
+
+    if (!scbp)
+        return (FALSE);
+
+    pendingRequest = (sipspi_msg_list_t *)
+        cpr_malloc(sizeof(sipspi_msg_list_t));
+    if (!pendingRequest) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed\n", fname);
+        return (FALSE);
+    }
+    pendingRequest->cmd  = cmd;
+    pendingRequest->msg  = newRequest;
+    pendingRequest->next = NULL;
+
+    if (scbp->pendingRequests == NULL) {
+        scbp->pendingRequests = pendingRequest;
+        return (TRUE);
+    }
+
+    tmp = scbp->pendingRequests;
+    while (tmp->next != NULL) {
+        tmp = tmp->next;
+    }
+    tmp->next = pendingRequest;
+    return (TRUE);
+}
+
+void
+handle_pending_requests (sipSCB_t *scbp)
+{
+    sipspi_msg_list_t *pendingRequest = NULL;
+
+    if (scbp->pendingRequests) {
+        pendingRequest = scbp->pendingRequests;
+        scbp->pendingRequests = pendingRequest->next;
+        switch (pendingRequest->cmd) {
+        case SIPSPI_EV_CC_NOTIFY:
+            {
+                sipspi_msg_t *notify = pendingRequest->msg;
+
+                cpr_free(pendingRequest);
+                (void) subsmanager_handle_ev_app_notify(notify);
+                cpr_free(notify);
+            }
+            break;
+        case SIPSPI_EV_CC_SUBSCRIBE_REGISTER:
+            cpr_free(pendingRequest->msg);
+            cpr_free(pendingRequest);
+            break;
+        case SIPSPI_EV_CC_SUBSCRIBE:
+            {
+                sipspi_msg_t *subs = pendingRequest->msg;
+
+                cpr_free(pendingRequest);
+                (void) subsmanager_handle_ev_app_subscribe(subs);
+                cpr_free(subs);
+            }
+            break;
+        case SIPSPI_EV_CC_SUBSCRIBE_RESPONSE:
+            {
+                sipspi_msg_t *subs_resp = pendingRequest->msg;
+
+                cpr_free(pendingRequest);
+                (void) subsmanager_handle_ev_app_subscribe_response(subs_resp);
+                cpr_free(subs_resp);
+            }
+            break;
+        case SIPSPI_EV_CC_NOTIFY_RESPONSE:
+            {
+                sipspi_msg_t *not_resp = pendingRequest->msg;
+
+                cpr_free(pendingRequest);
+                (void) subsmanager_handle_ev_app_notify_response(not_resp);
+                cpr_free(not_resp);
+            }
+            break;
+        case SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED:
+            {
+                sipspi_msg_t *term = pendingRequest->msg;
+
+                cpr_free(pendingRequest);
+                (void) subsmanager_handle_ev_app_subscription_terminated(term);
+                cpr_free(term);
+            }
+            break;
+        default:
+            if (pendingRequest->msg) {
+                cpr_free(pendingRequest->msg);
+            }
+            cpr_free(pendingRequest);
+            break;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////// Test Only !!
+
+static void
+ccsip_api_subscribe_result (ccsip_sub_not_data_t * msg_data)
+{
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received Subscribe Response: request_id=%d, sub_id=%x\n",
+                     DEB_F_PREFIX_ARGS(SIP_SUB_RESP, "ccsip_api_subscribe_result"),
+                     msg_data->request_id, msg_data->sub_id);
+    if (msg_data->u.subs_result_data.status_code == REQUEST_TIMEOUT) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Request timed out\n", DEB_F_PREFIX_ARGS(SIP_SUB_RESP, "ccsip_api_subscribe_result"));
+    }
+}
+
+static void
+print_event_data (ccsip_event_data_t * eventDatap)
+{
+    static const char *fname = "print_event_data";
+
+    while (eventDatap) {
+        switch (eventDatap->type) {
+        case EVENT_DATA_INVALID:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Invalid Data Received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        case EVENT_DATA_KPML_REQUEST:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"KPML Request Event Data Received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        case EVENT_DATA_KPML_RESPONSE:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"KPML Response Event Data Received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        case EVENT_DATA_PRESENCE:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Presence Event Data Received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        case EVENT_DATA_DIALOG:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Dialog Event Data Received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        case EVENT_DATA_RAW:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Raw Event Data Received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        default:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Event Data Type Not Understood\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        }
+        eventDatap = eventDatap->next;
+    }
+}
+
+static void
+ccsip_api_notify_result (ccsip_sub_not_data_t *msg_data)
+{
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received Notify Response\n", DEB_F_PREFIX_ARGS(SIP_SUB_RESP, "ccsip_api_notify_result"));
+}
+
+static void
+ccsip_api_notify_ind (ccsip_sub_not_data_t *msg)
+{
+    static const char *fname = "ccsip_api_notify_ind";
+    sipspi_msg_t not_resp;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received Notify, request_id=%d, sub_id=%x\n",DEB_F_PREFIX_ARGS(SIP_SUB_RESP, fname),
+                     msg->request_id, msg->sub_id);
+
+    // Check out what's there in the notify indication
+    if (msg->u.notify_ind_data.eventData) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Event Data Received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+        print_event_data(msg->u.notify_ind_data.eventData);
+        free_event_data(msg->u.notify_ind_data.eventData);
+    } else {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"No event data received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+    }
+
+    // Respond with a 200OK
+    memset(&not_resp, 0, sizeof(sipspi_msg_t));
+    not_resp.msg.notify_resp.sub_id = msg->sub_id;
+    not_resp.msg.notify_resp.response_code = SIP_SUCCESS_SETUP;
+    not_resp.msg.notify_resp.duration = 3600;
+
+    (void) subsmanager_handle_ev_app_notify_response(&not_resp);
+}
+
+static void
+ccsip_api_subscribe_terminate (ccsip_sub_not_data_t *msg_data)
+{
+       sipspi_msg_t terminate;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received Terminate notice\n", DEB_F_PREFIX_ARGS(SIP_SUB, "ccsip_api_subscribe_terminate"));
+       if (msg_data->u.subs_term_data.status_code == NETWORK_SUBSCRIPTION_EXPIRED) {
+        terminate.msg.subs_term.sub_id = msg_data->sub_id;
+        terminate.msg.subs_term.immediate = TRUE;
+        (void) subsmanager_handle_ev_app_subscription_terminated(&terminate);
+       }
+}
+
+static void
+ccsip_api_subscribe_ind (ccsip_sub_not_data_t *msg)
+{
+    static const char *fname = "ccsip_api_subscribe_ind";
+    sipspi_msg_t subs_resp, notify, terminate;
+    ccsip_event_data_t *eventData;
+    char *junkdata;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received Subscription Request\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+
+    // Check out what's there in the subs indication
+    if (msg->u.subs_ind_data.eventData) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Event Data Received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+        print_event_data(msg->u.subs_ind_data.eventData);
+        free_event_data(msg->u.subs_ind_data.eventData);
+    } else {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"No event data received\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+    }
+
+    // Accept the subscription with a 200 OK response
+    subs_resp.msg.subscribe_resp.duration = msg->u.subs_ind_data.expires;
+    subs_resp.msg.subscribe_resp.response_code = SIP_SUCCESS_SETUP;
+    subs_resp.msg.subscribe_resp.sub_id = msg->sub_id;
+    (void) subsmanager_handle_ev_app_subscribe_response(&subs_resp);
+
+    // Now send a NOTIFY
+    notify.msg.notify.notifyResultCallback = ccsip_api_notify_result;
+    notify.msg.notify.sub_id = msg->sub_id;
+    notify.msg.notify.eventData = NULL;
+
+    // Now fill in some data in the kpml structure
+    eventData = (ccsip_event_data_t *) cpr_malloc(sizeof(ccsip_event_data_t));
+    if (eventData == NULL) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Malloc of ccsip event data structure failed.\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+        return;
+    }
+
+    memset(eventData, 0, sizeof(ccsip_event_data_t));
+    sstrncpy(eventData->u.kpml_request.pattern.regex.regexData, "012", 32);
+    sstrncpy(eventData->u.kpml_request.version, "1.0", 16);
+    eventData->type = EVENT_DATA_KPML_REQUEST;
+    notify.msg.notify.eventData = eventData;
+
+    // Now fill in some other junk data
+    eventData = (ccsip_event_data_t *) cpr_malloc(sizeof(ccsip_event_data_t));
+    if (eventData == NULL) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Malloc of ccsip event structure failed.\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+        cpr_free(notify.msg.notify.eventData);
+        return;
+    }
+
+    junkdata = (char *) cpr_malloc(20);
+    if (junkdata == NULL) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Malloc of junk data structure failed.\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+        cpr_free(eventData);
+        cpr_free(notify.msg.notify.eventData);
+        return;
+    }
+
+    memset(eventData, 0, sizeof(ccsip_event_data_t));
+    memset(junkdata, 0, 20);
+    sstrncpy(junkdata, "Hello", 20);
+
+    eventData->u.raw_data.data = junkdata;
+    eventData->u.raw_data.length = strlen(junkdata);
+    eventData->type = EVENT_DATA_RAW;
+    notify.msg.notify.eventData->next = eventData;
+    (void) subsmanager_handle_ev_app_notify(&notify);
+
+    // If expires is 0, terminate the subscription
+    if (msg->u.subs_ind_data.expires == 0) {
+        terminate.msg.subs_term.sub_id = msg->sub_id;
+        terminate.msg.subs_term.immediate = TRUE;
+        (void) subsmanager_handle_ev_app_subscription_terminated(&terminate);
+    }
+}
+
+static void
+test_send_subscribe ()
+{
+    sipspi_msg_t subscribe;
+    ccsip_event_data_t *eventData;
+
+    memset(&subscribe.msg.subscribe, 0, sizeof(sipspi_subscribe_t));
+
+    subscribe.msg.subscribe.eventPackage = CC_SUBSCRIPTIONS_KPML;
+    subscribe.msg.subscribe.duration = 15;
+    sstrncpy(subscribe.msg.subscribe.subscribe_uri, "19921", CC_MAX_DIALSTRING_LEN);
+    sstrncpy(subscribe.msg.subscribe.subscriber_uri, "12345", CC_MAX_DIALSTRING_LEN);
+    subscribe.msg.subscribe.request_id = 1003;
+    subscribe.msg.subscribe.dn_line = 2;
+    subscribe.msg.subscribe.sub_id = CCSIP_SUBS_INVALID_SUB_ID;
+    subscribe.msg.subscribe.subsResultCallback = ccsip_api_subscribe_result;
+    subscribe.msg.subscribe.subsTermCallback = ccsip_api_subscribe_terminate;
+    subscribe.msg.subscribe.notifyIndCallback = ccsip_api_notify_ind;
+    subscribe.msg.subscribe.auto_resubscribe = TRUE;
+
+    // Now fill in some data in the kpml structure
+    /*
+     * eventData = (ccsip_event_data_t *) cpr_malloc(sizeof(ccsip_event_data_t));
+     * if (eventData == NULL) {
+     * CCSIP_DEBUG_TASK("Malloc of ccsip event data failed.\n");
+     * return;
+     * }
+     *
+     * memset(eventData, 0, sizeof(ccsip_event_data_t));
+     * sstrncpy(eventData->u.kpml_request.pattern.regex.regexData, "012", sizeof(eventData->u.kpml_request.pattern.regex.regexData));
+     * sstrncpy(eventData->u.kpml_request.version, "1.0", sizeof(eventData->u.kpml_request.version));
+     * eventData->type = EVENT_DATA_KPML_REQUEST;
+     * subscribe.msg.subscribe.eventData = eventData;
+     */
+    // Now fill in some other junk data
+    eventData = (ccsip_event_data_t *) cpr_malloc(sizeof(ccsip_event_data_t));
+    if (eventData == NULL) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Malloc of ccsip event data failed.\n", DEB_F_PREFIX_ARGS(SIP_EVT, "test_send_subscribe"));
+        return;
+    }
+
+    memset(eventData, 0, sizeof(ccsip_event_data_t));
+    eventData->type = EVENT_DATA_RAW;
+    eventData->u.raw_data.data = (char *) cpr_malloc(150);
+    if (eventData->u.raw_data.data) {
+        eventData->u.raw_data.length = 150;
+        memset(eventData->u.raw_data.data, 'V', 150);
+    } else {
+        eventData->u.raw_data.length = 0;
+    }
+    eventData->next = NULL;
+    subscribe.msg.subscribe.eventData = eventData;
+
+    (void) subsmanager_handle_ev_app_subscribe(&subscribe);
+}
+
+static void
+test_send_register ()
+{
+    sipspi_msg_t register_msg;
+
+    memset(&register_msg, 0, sizeof(sipspi_msg_t));
+    register_msg.msg.subs_reg.eventPackage = CC_SUBSCRIPTIONS_PRESENCE;
+    register_msg.msg.subs_reg.subsIndCallback = ccsip_api_subscribe_ind;
+       register_msg.msg.subs_reg.subsTermCallback = ccsip_api_subscribe_terminate;
+    (void) subsmanager_handle_ev_app_subscribe_register(&register_msg);
+}
+
+int
+subsmanager_test_start_routine ()
+{
+    static int subscribe_sent = 0;
+    static int register_sent = 0;
+
+    if (subscribe_sent == 0) {
+        test_send_subscribe();
+        subscribe_sent = 1;
+    }
+
+    if (register_sent == 0) {
+        test_send_register();
+        register_sent = 1;
+    }
+    return 0;
+
+}
+
+/************************************************************
+ * Send local error to the calling application
+ * This function reuses the callback interface to pass the
+ * (bad) result of requests from the application
+ ************************************************************/
+void
+sip_send_error_message (ccsip_sub_not_data_t *msg_data,
+                        cc_srcs_t dest_task,
+                        int msgid,
+                        ccsipGenericCallbackFn_t callbackFn,
+                        const char *fname)
+{
+    if (!msg_data) {
+        return;
+    }
+    if (callbackFn) {
+        (*callbackFn) (msg_data);
+    } else if (dest_task != CC_SRC_MIN) {
+        (void) sip_send_message(msg_data, dest_task, msgid);
+    }
+}
+
+
+/********************************************************
+ * Shutdown the Subscription Manager
+ ********************************************************/
+int
+sip_subsManager_shut ()
+{
+    const char *fname = "sip_subsManager_shut";
+    int i;
+    sipSCB_t *scbp = NULL;
+    ccsip_sub_not_data_t error_data;
+
+    if (subsManagerRunning == 0) {
+        return (0);
+    }
+    error_data.reason_code = SM_REASON_CODE_SHUTDOWN;
+    // Send indication of subscription ended to internal apps
+    // then clean and free up subscriptions and SCB
+    for (i = 0; i < MAX_SCBS; i++) {
+        scbp = &(subsManagerSCBS[i]);
+        if (scbp->smState == SUBS_STATE_IDLE) {
+            continue;
+        }
+
+        error_data.sub_id = scbp->sub_id;
+        error_data.request_id = scbp->request_id;
+        error_data.sub_duration = 0;
+        error_data.event = scbp->hb.event_type;
+        error_data.msg_id = scbp->subsTermCallbackMsgID;
+        error_data.line_id = scbp->hb.dn_line;
+        error_data.gsm_id = scbp->gsm_id;
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Sending shutdown notification for scb=%d"
+                         " sub_id=%x\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname), i, scbp->sub_id);
+
+        sip_send_error_message(&error_data, scbp->subsNotCallbackTask,
+                               scbp->subsTermCallbackMsgID,
+                               scbp->subsTermCallback, fname);
+
+        free_scb(i, fname);
+    }
+
+    // Shut down the periodic timer
+    (void) sip_platform_subnot_periodic_timer_stop();
+
+    // Mark subsManager as stopped running
+    subsManagerRunning = 0;
+
+    tcb_reset();
+
+    return (0);
+}
+
+/********************************************************
+ * Common code notifying application for a failover/fallback
+ * or CCM new registration (reset) event. Send a notification to
+ * applications that have an active subscription
+ ********************************************************/
+static int
+sip_subsManager_reg_failure_common (ccsip_reason_code_e reason)
+{
+    const char *fname = "sip_subsManager_reg_failure_common";
+    int i;
+    sipSCB_t *scbp = NULL;
+    ccsip_sub_not_data_t error_data;
+
+    if (subsManagerRunning == 0) {
+        return (0);
+    }
+    error_data.reason_code = reason;
+    // Send indication of subscription ended to internal apps
+    // then clean and free up subscriptions and SCB
+    for (i = 0; i < MAX_SCBS; i++) {
+        scbp = &(subsManagerSCBS[i]);
+        if (scbp->smState == SUBS_STATE_IDLE ||
+            scbp->smState == SUBS_STATE_REGISTERED) {
+            // Update addr and port after rollover/ccm reset
+            scbp->hb.local_port = sipTransportGetListenPort(1, NULL);
+            sipTransportGetServerIPAddr(&(scbp->hb.dest_sip_addr),1);
+            scbp->hb.dest_sip_port = sipTransportGetPrimServerPort(1);
+            continue;
+        }
+
+        error_data.sub_id = scbp->sub_id;
+        error_data.line_id = scbp->hb.dn_line;
+        error_data.request_id = scbp->request_id;
+        error_data.sub_duration = 0;
+        error_data.event = scbp->hb.event_type;
+        error_data.msg_id = scbp->subsTermCallbackMsgID;
+
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Sending reg failure notification for "
+                         "scb=%d sub_id=%x reason=%d\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname), i,
+                         scbp->sub_id, reason);
+
+        sip_send_error_message(&error_data, scbp->subsNotCallbackTask,
+                               scbp->subsTermCallbackMsgID,
+                               scbp->subsTermCallback, fname);
+
+        if (scbp->internal) {
+            outgoingSubscriptions--;
+        } else {
+            incomingSubscriptions--;
+        }
+        free_scb(i, fname);
+    }
+
+    sipRelDevAllMessagesClear();
+    return (0);
+}
+
+/********************************************************
+ * Handle a failover/fallback event
+ * to handle this event, send a notification to all
+ * applications that have an active subscription
+ ********************************************************/
+int
+sip_subsManager_rollover ()
+{
+    tcb_reset();
+    return (sip_subsManager_reg_failure_common(SM_REASON_CODE_ROLLOVER));
+}
+
+/********************************************************
+ * Handle a (CCM or Proxy) server down and up event^M
+ * to handle this event, send a notification to all^M
+ * applications that have an active subscription^M
+ ********************************************************/
+int
+sip_subsManager_reset_reg (void)
+{
+    return (sip_subsManager_reg_failure_common(SM_REASON_CODE_RESET_REG));
+}
+
+
+/***********************************************************
+ * Send a protocol error message to application. The application
+ * decides whether to terminate and restart the subscription
+ * or otherwise
+ ***********************************************************/
+void
+sip_subsManager_send_protocol_error (sipSCB_t *scbp, int scb_index,
+                                     boolean terminate)
+{
+    const char *fname = "sip_subsManager_send_protocol_error";
+    ccsip_sub_not_data_t error_data;
+
+    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Protocol Error for scb=%d sub_id=%x\n", fname,
+                      scb_index, scbp->sub_id);
+
+    error_data.reason_code = SM_REASON_CODE_ERROR;
+    error_data.sub_id = scbp->sub_id;
+    error_data.request_id = scbp->request_id;
+    error_data.sub_duration = 0;
+    error_data.event = scbp->hb.event_type;
+    error_data.msg_id = scbp->subsTermCallbackMsgID;
+    error_data.line_id = scbp->hb.dn_line;
+
+    sip_send_error_message(&error_data, scbp->subsNotCallbackTask,
+                           scbp->subsTermCallbackMsgID, scbp->subsTermCallback,
+                           fname);
+
+    if (terminate) {
+        free_scb(scb_index, fname);
+    }
+
+}
+
+/********************************************************
+ * Start and initialize the Subscription Manager
+ ********************************************************/
+void
+initialize_scb (sipSCB_t *scbp)
+{
+    int nat_enable = 0;
+
+    if (!scbp) {
+        return;
+    }
+    memset(scbp, 0, sizeof(sipSCB_t));
+
+    scbp->sub_id = CCSIP_SUBS_INVALID_SUB_ID;
+    scbp->pendingClean = FALSE;
+    scbp->pendingCount = 0;
+    scbp->internal = FALSE;
+    scbp->subsIndCallback = NULL;
+    scbp->subsResultCallback = NULL;
+    scbp->notifyIndCallback = NULL;
+    scbp->notifyResultCallback = NULL;
+    scbp->subsTermCallback = NULL;
+    scbp->notIndCallbackMsgID = 0;
+    scbp->notResCallbackMsgID = 0;
+    scbp->subsIndCallbackMsgID = 0;
+    scbp->subsResCallbackMsgID = 0;
+    scbp->subsTermCallbackMsgID = 0;
+    scbp->subsIndCallbackTask = CC_SRC_MIN;
+    scbp->subsNotCallbackTask = CC_SRC_MIN;
+
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        sip_config_get_net_device_ipaddr(&(scbp->hb.src_addr));
+    } else {
+        sip_config_get_nat_ipaddr(&(scbp->hb.src_addr));
+    }
+    scbp->hb.cb_type = SUBNOT_CB;
+    scbp->hb.dn_line = 1;
+    scbp->hb.local_port = sipTransportGetListenPort(scbp->hb.dn_line, NULL);
+    sipTransportGetServerIPAddr(&(scbp->hb.dest_sip_addr),1);
+    scbp->hb.dest_sip_port = sipTransportGetPrimServerPort(1);
+
+    scbp->hb.sipCallID[0] = '\0';
+    scbp->smState = SUBS_STATE_IDLE;
+    scbp->SubURI[0] = '\0';
+    scbp->SubscriberURI[0] = '\0';
+    scbp->sip_from = strlib_empty();
+    scbp->sip_to = strlib_empty();
+    scbp->sip_to_tag = strlib_empty();
+    scbp->sip_from_tag = strlib_empty();
+    scbp->sip_contact = strlib_empty();
+    scbp->cached_record_route = strlib_empty();
+    scbp->callingNumber = strlib_empty();
+    scbp->subscription_state = SUBSCRIPTION_STATE_INVALID;
+    scbp->norefersub = FALSE;
+    scbp->request_id = -1;
+    scbp->hb.authen.cred_type = 0;
+    scbp->hb.authen.authorization = NULL;
+    scbp->hb.authen.status_code = 0;
+    scbp->hb.authen.nc_count = 0;
+    scbp->hb.authen.new_flag = FALSE;
+    scbp->hb.event_data_p = NULL;
+    scbp->pendingRequests = NULL;
+}
+
+int
+sip_subsManager_init ()
+{
+    // Initialize SCBS array
+    const char *fname = "sip_subsManager_init";
+    line_t   i = 0;
+    sipSCB_t *scbp;
+
+    if (subsManagerRunning == 1) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Subscription Manager already running!!\n", fname);
+        return SIP_OK;
+    }
+
+    for (i = 0; i < MAX_SCBS; i++) {
+        scbp = &(subsManagerSCBS[i]);
+        initialize_scb(scbp);
+        scbp->line = i;
+    }
+
+    for (i = 0; i < MAX_SCB_HISTORY; i++) {
+        gSubHistory[i].last_call_id[0] = '\0';
+        gSubHistory[i].last_from_tag[0] = '\0';
+        gSubHistory[i].eventPackage = CC_SUBSCRIPTIONS_NONE;
+    }
+
+    // reset status and stats
+    internalRegistrations = 0;
+
+    incomingSubscribes = 0;
+    incomingRefers = 0;
+    incomingNotifies = 0;
+    incomingUnsolicitedNotifies = 0;
+    incomingSubscriptions = 0;
+
+    outgoingSubscribes = 0;
+    outgoingNotifies = 0;
+    outgoingUnsolicitedNotifies = 0;
+    outgoingSubscriptions = 0;
+
+    currentScbsAllocated = 0;
+    maxScbsAllocated = 0;
+
+    // Start periodic timer
+    (void) sip_platform_subnot_periodic_timer_start(TMR_PERIODIC_SUBNOT_INTERVAL * 1000);
+
+    subsManagerRunning = 1;
+
+    // Print out the size of the SCB
+    // CCSIP_DEBUG_ERROR("SCB Size=%d\n", sizeof(sipSCB_t));
+    // CCSIP_DEBUG_ERROR("CCB Size=%d\n", sizeof(ccsipCCB_t));
+    // Kick-off the test routine
+    // subsmanager_test_start_routine();
+
+    /* Initialize modules which uses SUB/MANAGER */
+    kpml_init();
+    configapp_init();
+
+    return SIP_OK;
+}
+
+
+/********************************************************
+ * Functions to allocate, locate, free, and update SCBs
+ ********************************************************/
+
+/*
+ *  Function: find_scb_by_callid
+ *
+ *  Parameters:
+ *      callID    - pointer to const. char for the target call ID.
+ *      scb_index - pointer to int where the SCB index of the result
+ *                  SCB to be stored.
+ *
+ *  Description:
+ *      The find_scb_by_callid function searches for an SCB that
+ *  contains the matching callID. It is possible to have multiple
+ *  SCB that matches the given callID. The function only returns
+ *  the first SCB that has the matching callID.
+ *
+ *  Returns:
+ *     Pointer to SCB and its index if the SCB is found. Otherwise
+ *  it returns NULL and the index is undefined.
+ */
+sipSCB_t *
+find_scb_by_callid (const char *callID, int *scb_index)
+{
+    int       i;
+    int       num_scb = currentScbsAllocated;
+    sipSCB_t *scbp;
+
+    if (num_scb == 0) {
+        /* No active subscription */
+        return (NULL);
+    }
+    scbp = &subsManagerSCBS[0];
+    for (i = 0; (i < MAX_SCBS) && num_scb; i++, scbp++) {
+        if (scbp->smState != SUBS_STATE_IDLE) {
+            if ((scbp->smState != SUBS_STATE_REGISTERED) &&
+                (strcmp(callID, scbp->hb.sipCallID) == 0)) {
+                *scb_index = i;
+                return (scbp);
+            }
+            num_scb--;
+        }
+    }
+
+    return (NULL);
+}
+
+/*
+ *  Function: find_req_scb
+ *
+ *  Parameters:
+ *      callID    - pointer to const. char for the target call ID.
+ *      method    - SIP method to match.
+ *      cseq      - CSEQ value to match.
+ *      scb_index - pointer to int where the SCB index of the result
+ *                  SCB to be stored.
+ *
+ *  Description:
+ *      The find_req_scb function searches for an SCB that contains
+ *  the matching callID, last request method and last cseq sent. If
+ *  the match SCB is found, the function returns the pointer to the
+ *  sipSCB_t along with the corresponding index.
+ *
+ *  Returns:
+ *     Pointer to SCB and its index if the SCB is found. Otherwise
+ *  returns NULL and the index will be set to MAX_SCBS.
+ */
+static sipSCB_t *
+find_req_scb (const char *callID, sipMethod_t method,
+              uint32_t cseq, int *scb_index)
+{
+    int       idx, num_scb;
+    sipSCB_t *scbp;
+
+    scbp = &subsManagerSCBS[0];
+    num_scb = currentScbsAllocated;
+    /*
+     * Search SCB tables for all allocated SCBs.
+     */
+    for (idx = 0; (idx < MAX_SCBS) && num_scb; idx++, scbp++) {
+        if (scbp->smState != SUBS_STATE_IDLE) {
+            if ((scbp->smState != SUBS_STATE_REGISTERED) &&
+                (scbp->last_sent_request_cseq_method == method) &&
+                (scbp->last_sent_request_cseq == cseq) &&
+                (strcmp(callID, scbp->hb.sipCallID) == 0)) {
+                /* Found the matching scb */
+                *scb_index = idx;
+                return (scbp);
+            }
+            num_scb--;
+        }
+    }
+    *scb_index = MAX_SCBS;
+    return (NULL);
+}
+
+/*
+ *  Function: find_scb_by_sub_id
+ *
+ *  Parameters:
+ *      sub_id    - the sub_id_t parameter from which scb index
+ *                  to be obtained.
+ *      scb_index - pointer to int for index of the SCB entry to
+ *                  be returned if the pointer is provided (not
+ *                  NULL).
+ *
+ *  Description:
+ *      The function finds the SCB for the given sub_id.
+ *
+ *  Returns:
+ *      1) pointer to sipSCB_t or NULL if finding fails.
+ *      2) the index into the SCB array is also returned if the
+ *         scb_index parameter is provided.
+ */
+static sipSCB_t *
+find_scb_by_sub_id (sub_id_t sub_id, int *scb_index)
+{
+    int idx, ret_idx = MAX_SCBS;
+    sipSCB_t *scbp = NULL;
+
+    /*
+     * Use index part of the sub_id to find the SCB
+     */
+    idx = GET_SCB_INDEX_FROM_SUB_ID(sub_id);
+    if (idx < MAX_SCBS) {
+        /*
+         * SCB index is within a valid range, get the SCB by index.
+         * Match the SCB's sub_id and the one provided.
+         */
+        if (subsManagerSCBS[idx].sub_id == sub_id) {
+            /* The correct SCB is found */
+            scbp    = &(subsManagerSCBS[idx]);
+            ret_idx = idx;
+        }
+    }
+
+    if (scb_index != NULL) {
+        *scb_index = ret_idx;
+    }
+    return (scbp);
+}
+
+sipSCB_t *
+find_scb_by_registration (cc_subscriptions_t event, int *scb_index)
+{
+    int i;
+
+    for (i = 0; i < MAX_SCBS; i++) {
+        if ((subsManagerSCBS[i].hb.event_type == event) &&
+            (subsManagerSCBS[i].smState == SUBS_STATE_REGISTERED)) {
+            *scb_index = i;
+            return &(subsManagerSCBS[i]);
+        }
+    }
+
+    return (NULL);
+}
+
+sipSCB_t *
+find_scb_by_subscription (cc_subscriptions_t event, int *scb_index,
+                          const char *callID)
+{
+    int i;
+
+    for (i = 0; i < MAX_SCBS; i++) {
+        if (cpr_strcasecmp(subsManagerSCBS[i].hb.sipCallID, callID) == 0) {
+            *scb_index = i;
+            return &(subsManagerSCBS[i]);
+        }
+    }
+    return (NULL);
+}
+
+/*
+ *  Function: new_sub_id
+ *
+ *  Parameters:
+ *      scb_index - the SCB index.
+ *
+ *  Description:
+ *      The function allocates a new unique sub_id and return
+ *  it to the caller.
+ *
+ *  NOTE: the scb_index can not exceed 16 bit unsigned value.
+ *
+ *  Returns:
+ *     sub_id.
+ */
+static sub_id_t
+new_sub_id (int scb_index)
+{
+    static sub_id_t unique_id = 0;
+    sub_id_t sub_id;
+
+    /*
+     * Form the sub_id is encoded as the following:
+     *       bit 31 - bit 16 contains unique ID
+     *       bit 15 - bit  0 contains scb index.
+     */
+    sub_id = (unique_id << SUB_ID_UNIQUE_ID_POSITION) |
+             (sub_id_t)(scb_index & SUB_IDSCB_INDEX_MASK);
+    unique_id++;       /* next unique sub id */
+    if (sub_id == CCSIP_SUBS_INVALID_SUB_ID) {
+        /* sub_id becomes the invalid value marker, re-calcualte new id  */
+        sub_id = (unique_id << SUB_ID_UNIQUE_ID_POSITION) |
+                 (sub_id_t)(scb_index & SUB_IDSCB_INDEX_MASK);
+        unique_id++;
+    }
+    return (sub_id);
+}
+
+sipSCB_t *
+allocate_scb (int *scb_index)
+{
+    int i;
+
+    for (i = 0; i < MAX_SCBS; i++) {
+        if (subsManagerSCBS[i].smState == SUBS_STATE_IDLE) {
+            *scb_index = i;
+            currentScbsAllocated++;
+            if (currentScbsAllocated > maxScbsAllocated) {
+                maxScbsAllocated = currentScbsAllocated;
+            }
+            /*
+             * assigned sub_id to the allocated SCB.
+             */
+            subsManagerSCBS[i].sub_id = new_sub_id(i);
+            CCSIP_DEBUG_TASK("allocate_scb scb_index: %d, currentScbsAllocated: %d, "
+                    "maxScbsAllocated: %d, sub_id: %x\n", scb_index,
+                    currentScbsAllocated, maxScbsAllocated, subsManagerSCBS[i].sub_id);
+
+            /*
+             * local port may have changed because of failover/fallback.
+             * So update it with current info so that the Via & Contact headers
+             * in SUB/NOT are generated correctly.
+             */
+            subsManagerSCBS[i].hb.local_port =
+                sipTransportGetListenPort(subsManagerSCBS[i].hb.dn_line, NULL);
+            return &(subsManagerSCBS[i]);
+        }
+    }
+    return (NULL);
+}
+
+boolean
+is_previous_sub (const char *pCallID,
+                 char *pFromTag,
+                 cc_subscriptions_t event)
+{
+    int i;
+
+    if (!pCallID || !pFromTag) {
+        return FALSE;
+    }
+
+    for (i = 0; i < MAX_SCB_HISTORY; i++) {
+        if (strncmp(gSubHistory[i].last_call_id, pCallID, MAX_SIP_CALL_ID) == 0) {
+            if (strncmp(gSubHistory[i].last_from_tag, pFromTag, MAX_SIP_TAG_LENGTH) == 0) {
+                if (gSubHistory[i].eventPackage == event) {
+                    return TRUE;
+                }
+            }
+        }
+    }
+
+    return FALSE;
+}
+
+void
+clean_scb (sipSCB_t *scbp)
+{
+    sub_not_trxn_t *trxn_p;
+
+    if (scbp) {
+        strlib_free(scbp->sip_from);
+        strlib_free(scbp->sip_to);
+        strlib_free(scbp->sip_to_tag);
+        strlib_free(scbp->sip_from_tag);
+        strlib_free(scbp->callingNumber);
+        strlib_free(scbp->sip_contact);
+        strlib_free(scbp->cached_record_route);
+        if (scbp->contact_info)
+            sippmh_free_contact(scbp->contact_info);
+        if (scbp->record_route_info)
+            sippmh_free_record_route(scbp->record_route_info);
+        if (scbp->hb.event_data_p)
+            free_event_data(scbp->hb.event_data_p);
+        if (scbp->pendingRequests) {
+            free_pending_requests(scbp->pendingRequests);
+        }
+
+        scbp->hb.authen.cnonce[0] = '\0';
+        if (scbp->hb.authen.authorization != NULL) {
+            cpr_free(scbp->hb.authen.authorization);
+            scbp->hb.authen.authorization = NULL;
+        }
+        if (scbp->hb.authen.sip_authen != NULL) {
+            sippmh_free_authen(scbp->hb.authen.sip_authen);
+            scbp->hb.authen.sip_authen = NULL;
+        }
+
+        /* free all transactions */
+        if (scbp->incoming_trxns) {
+            while ((trxn_p = (sub_not_trxn_t *)
+                sll_next(scbp->incoming_trxns, NULL)) != NULL) {
+                (void) sll_remove(scbp->incoming_trxns, (void *)trxn_p);
+                cpr_free(trxn_p->via);
+                cpr_free(trxn_p);
+            }
+            sll_destroy(scbp->incoming_trxns);
+            scbp->incoming_trxns = NULL;
+        }
+
+    }
+}
+void
+store_scb_history (sipSCB_t *scbp)
+{
+    // Copy SCB parameters in the next available history array
+    static int next_history = 0;
+
+    next_history++;
+    if (next_history == MAX_SCB_HISTORY) {
+        next_history = 0;
+    }
+    sstrncpy(gSubHistory[next_history].last_call_id, scbp->hb.sipCallID, MAX_SIP_CALL_ID);
+    sstrncpy(gSubHistory[next_history].last_from_tag, scbp->sip_from_tag, MAX_SIP_TAG_LENGTH);
+    gSubHistory[next_history].eventPackage = scbp->hb.event_type;
+}
+
+/**
+ *
+ * frees the SCB. It will cleanup the scb (ie, free the memory allocated for any sub fields).
+ * It will also mark the SCB as free (smState = SUBS_STATE_IDLE).
+ * Please note this function may be invoked can be invoked even when smState == SUBS_STATE_IDLE.
+ * An example case is when parse_body() fails.
+ *
+ * @param[in] scb_index - indext of the SCB into SCB array.
+ * @param[in] fname -  name of the function calling this function.
+ *
+ * @return  none
+ *
+ * @pre     (scb_index >= 0) and (scb_index < MAX_SCBS)
+ * @post    (subsManagerSCBS[scb_index].smState equals FALSE)
+ */
+void
+free_scb (int scb_index, const char *fname)
+{
+    sipSCB_t *scbp = NULL;
+
+    if (scb_index >= MAX_SCBS || scb_index < 0) {
+        CCSIP_DEBUG_ERROR("%s Trying to free an invalid scb_index. Return.\n", fname);
+        return;
+    }
+    scbp = &(subsManagerSCBS[scb_index]);
+
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Freeing SCB: scb=%d sub_id=%x\n",
+                     DEB_F_PREFIX_ARGS(SIP_SUB, "free_scb"), scb_index, scbp->sub_id);
+
+    if (scbp->smState != SUBS_STATE_IDLE) {
+        currentScbsAllocated--;
+        /*
+         * This condition should never happen. If occurs, it is a strong indication
+         * something has gone wrong in our code logic and should be investigated.
+         */
+        if (currentScbsAllocated < 0) {
+            CCSIP_DEBUG_ERROR("%s: Error somewhere in scb accounting which results"
+                       "in negative currentScbsAllocated. Set it to 0.\n", fname);
+            currentScbsAllocated = 0;
+        }
+    }
+    // If this was an incoming subscription, store values in history first
+    if ((scbp->internal == FALSE) && (scbp->smState != SUBS_STATE_REGISTERED)) {
+        store_scb_history(scbp);
+    }
+
+    clean_scb(scbp);
+
+    // Stop associated message retry timer
+    if (sipPlatformUISMSubNotTimers[scb_index].outstanding) {
+        sip_platform_msg_timer_subnot_stop(&sipPlatformUISMSubNotTimers[scb_index]);
+    }
+
+    // Re-initialize
+    initialize_scb(scbp);
+    scbp->line = (line_t) scb_index;
+}
+
+/**
+ * This function will free up the TCB resources and removes it from the TCB list.
+ *
+ * @param[in] tcbp -  pointer to a TCB.
+ *
+ * @return  none
+ *
+ *  @pre    (tcbp != NULL)
+ */
+static void free_tcb (sipTCB_t *tcbp)
+{
+    if (tcbp->hb.authen.authorization != NULL) {
+        cpr_free(tcbp->hb.authen.authorization);
+    }
+    if (tcbp->hb.authen.sip_authen != NULL) {
+        sippmh_free_authen(tcbp->hb.authen.sip_authen);
+    }
+
+    (void)cprDestroyTimer(tcbp->timer);
+    free_event_data(tcbp->hb.event_data_p);
+    (void)sll_remove(s_TCB_list, (void *)tcbp);
+    cpr_free(tcbp);
+}
+
+/**
+ * This function will find matching TCB by the SIP Call-ID in the TCB list.
+ *
+ * @param[in] callID_p - SIP Call-ID
+ *
+ * @return  NULL if there is no matching TCB
+ *          Otherwise, pointer to the found TCB is returned.
+ *
+ *  @pre    (callID_p != NULL)
+ */
+sipTCB_t *find_tcb_by_sip_callid (const char *callID_p)
+{
+    sipTCB_t *tcb_p;
+
+    tcb_p = (sipTCB_t *)sll_next(s_TCB_list, NULL);
+    while (tcb_p != NULL) {
+        if (strncmp(callID_p, tcb_p->hb.sipCallID, (sizeof(tcb_p->hb.sipCallID) -1)) == 0) {
+            return tcb_p;
+        }
+        tcb_p = (sipTCB_t *)sll_next(s_TCB_list, tcb_p);
+    }
+    return NULL;
+}
+
+/**
+ * This function will free up TCBs when
+ * 1. restarting or
+ * 2. failing over/ falling back
+ *
+ * @param[in] none
+ *
+ * @return none
+ */
+static void tcb_reset (void)
+{
+    sipTCB_t *tcb_p;
+
+    tcb_p = (sipTCB_t *)sll_next(s_TCB_list, NULL);
+    while (tcb_p != NULL) {
+        free_tcb(tcb_p);
+        tcb_p = (sipTCB_t *)sll_next(s_TCB_list, NULL);
+    }
+}
+
+/*
+ * Function is called to remove ccb pointer from scb
+ * When the dialog is cleared then ccb associated with
+ * that dialog is cleared as well. Remove that from
+ * scb parameters as well
+ */
+void
+submanager_update_ccb_addr (ccsipCCB_t *ccb)
+{
+    sipSCB_t *scbp = NULL;
+    int       scb_index = 0;
+    int       num_scb;
+
+    if ((ccb == NULL) || (currentScbsAllocated == 0)) {
+        /* The CCB is NULL or no active SCBs */
+        return;
+    }
+
+    /* Once the dialog is cleared i.e ccb is removed, the subnot
+     * should continue to use from its last sequence number. So
+     * re-assign last sequence value to scbp.
+     *
+     * There are possibility of multiple subscriptions associate
+     * with a call dialog. Search all of the SCB for the given CCB.
+     */
+    scbp    = &subsManagerSCBS[0];
+    num_scb = currentScbsAllocated;
+    for (scb_index = 0; (scb_index < MAX_SCBS) && num_scb; scb_index++, scbp++) {
+        if (scbp->smState != SUBS_STATE_IDLE) {
+            if ((scbp->smState != SUBS_STATE_REGISTERED) &&
+                (scbp->ccbp == ccb)) {
+                scbp->last_sent_request_cseq = ccb->last_used_cseq;
+                scbp->ccbp = NULL;
+            }
+            num_scb--;
+        }
+    }
+
+}
+
+
+/*
+ * Takes care of all the parsing requirements
+ */
+static int
+parse_body (cc_subscriptions_t event_type, char *msgBody, int msgLength,
+            ccsip_event_data_t **eventDatapp,
+            const char *fname)
+{
+    const char     *fname1 = "parse_body";
+    ccsip_event_data_type_e type = EVENT_DATA_INVALID;
+
+    if (!msgBody) {
+        return SIP_ERROR;
+    }
+
+    switch (event_type) {
+        case CC_SUBSCRIPTIONS_KPML:
+            type = EVENT_DATA_KPML_REQUEST;
+        break;
+        case CC_SUBSCRIPTIONS_CONFIGAPP:
+            type = EVENT_DATA_CONFIGAPP_REQUEST;
+        break;
+        default:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"%s: unknown event type %d\n", fname1, fname, type);
+        return SIP_ERROR;
+    }
+
+    return SIP_OK;
+}
+
+boolean
+add_content (ccsip_event_data_t *eventData, sipMessage_t *request, const char *fname)
+{
+    return FALSE;
+
+/* This function requires XML handling */
+#if 0
+    const char     *fname1 = "add_content";
+    uint32_t        len;
+    char           *eventBody = NULL;
+
+    while (eventData) {
+        /* Encode eventData into eventBody here */
+        len = strlen(eventBody);
+
+        switch (eventData->type) {
+        case EVENT_DATA_RAW:
+            // Assume body is of type CMXML for now
+            (void) sippmh_add_message_body(request, eventBody, len,
+                                           SIP_CONTENT_TYPE_CMXML,
+                                           SIP_CONTENT_DISPOSITION_SESSION_VALUE, TRUE, NULL);
+            break;
+        case EVENT_DATA_KPML_REQUEST:
+            (void) sippmh_add_message_body(request, eventBody, len,
+                                           SIP_CONTENT_TYPE_KPML_REQUEST,
+                                           SIP_CONTENT_DISPOSITION_SESSION_VALUE, TRUE, NULL);
+            break;
+        case EVENT_DATA_KPML_RESPONSE:
+            (void) sippmh_add_message_body(request, eventBody, len,
+                                           SIP_CONTENT_TYPE_KPML_RESPONSE,
+                                           SIP_CONTENT_DISPOSITION_SESSION_VALUE, TRUE, NULL);
+            break;
+        default:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"%s: Data type not supported\n", fname1, fname);
+            cpr_free(eventBody);
+            break;
+        }
+
+        eventData = eventData->next;
+    }
+    return (TRUE);
+#endif
+}
+
+// Functions to handle requests from internal applications
+
+/************************************************************
+ * Applications Register to Receive an incoming subscribe(s)
+ ************************************************************/
+int
+subsmanager_handle_ev_app_subscribe_register (cprBuffer_t buf)
+{
+    const char     *fname = "subsmanager_handle_ev_app_register";
+    sipspi_subscribe_reg_t *reg_datap;
+    sipSCB_t       *scbp = NULL;
+    int             scb_index;
+    sipspi_msg_t   *pSIPSPIMsg = NULL;
+
+
+    pSIPSPIMsg = (sipspi_msg_t *) buf;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing a new subscription registration\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+
+    if (!subsManagerRunning) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Subscription Manager Not Initialized!\n", fname);
+        return SIP_ERROR;
+    }
+
+    reg_datap = &(pSIPSPIMsg->msg.subs_reg);
+    if (reg_datap->subsIndCallback == NULL &&
+        reg_datap->subsIndCallbackMsgID == 0) {
+        return SIP_ERROR;
+    }
+
+    scbp = find_scb_by_registration(reg_datap->eventPackage, &scb_index);
+    if (scbp) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Duplicate registration!\n", fname);
+        return SIP_ERROR;
+    } else {
+        scbp = allocate_scb(&scb_index);
+        if (!scbp) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Subscription control block allocation failed\n", fname);
+            return SIP_ERROR;
+        }
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Allocated SCB for App Registration,"
+                         " event=%d, scb=%d, sub_id=%x\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname),
+                         reg_datap->eventPackage,
+                         GET_SCB_INDEX_FROM_SUB_ID(scbp->sub_id),
+                         scbp->sub_id);
+    }
+
+    scbp->hb.dn_line = 1;
+    scbp->hb.event_type = reg_datap->eventPackage;
+    if (reg_datap->eventPackage - CC_SUBSCRIPTIONS_DIALOG > -1 &&
+        reg_datap->eventPackage - CC_SUBSCRIPTIONS_DIALOG < 5) {
+        sstrncpy(scbp->event_name, eventNames[reg_datap->eventPackage - CC_SUBSCRIPTIONS_DIALOG], MAX_EVENT_NAME_LEN);
+    }
+
+    // Get callback information (either event or function)
+    scbp->subsIndCallback = reg_datap->subsIndCallback;
+    scbp->subsIndCallbackTask = reg_datap->subsIndCallbackTask;
+    scbp->subsNotCallbackTask = reg_datap->subsIndCallbackTask;
+    scbp->subsIndCallbackMsgID = reg_datap->subsIndCallbackMsgID;
+    scbp->subsTermCallback = reg_datap->subsTermCallback;
+    scbp->subsTermCallbackMsgID = reg_datap->subsTermCallbackMsgID;
+    scbp->subsTermCallback = reg_datap->subsTermCallback;
+    scbp->subsTermCallbackMsgID = reg_datap->subsTermCallbackMsgID;
+
+    scbp->smState = SUBS_STATE_REGISTERED;
+    internalRegistrations++;
+    return (0);
+}
+
+/********************************************************
+ * Application Generated Subscribe Request
+ ********************************************************/
+int
+subsmanager_handle_ev_app_subscribe (cprBuffer_t buf)
+{
+    const char     *fname = "subsmanager_handle_ev_app_subscribe";
+    sipspi_subscribe_t *sub_datap;
+    sipSCB_t       *scbp = NULL;
+    int             scb_index;
+    ccsip_sub_not_data_t subs_result_data;
+    boolean         reSubscribe = FALSE;
+    ccsipCCB_t     *ccbp = NULL;
+    sipspi_msg_t   *pSIPSPIMsg = NULL;
+    int             subscription_expires;
+
+    pSIPSPIMsg = (sipspi_msg_t *) buf;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing a new App subscription request\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+
+    if (!subsManagerRunning) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Subscription Manager Not Initialized!\n", fname);
+        return SIP_ERROR;
+    }
+
+    // Get Data
+    sub_datap = &(pSIPSPIMsg->msg.subscribe);
+
+    /* Steps:
+     * * Extract subscription details from the event information
+     * * Check data to ensure we support the event package asked for
+     * * Allocate an SCB to hold subscription details
+     * * Create body of the SUBSCRIBE message, if needed
+     * * Prep the message by adding fields to the SCB
+     * * Call sipSPISendSubscribe to send the message out
+     */
+
+    // Get ready for any failure
+    subs_result_data.u.subs_result_data.expires = 0;
+    subs_result_data.u.subs_result_data.status_code = SUBSCRIBE_REQUEST_FAILED;
+    subs_result_data.sub_id = CCSIP_SUBS_INVALID_SUB_ID;
+    subs_result_data.request_id = sub_datap->request_id;
+    subs_result_data.msg_id = sub_datap->subsResCallbackMsgID;
+
+    /*
+     * Finding SCB by sub_id or request id and eventPakage if the
+     * sub_id is not known.
+     */
+    if (sub_datap->sub_id == CCSIP_SUBS_INVALID_SUB_ID) {
+        /*
+         * find scb based on request_id and event package.
+         * This scenario is possible if application decides to terminate a
+         * subscription before subsmanager provides app with sub_id.
+         */
+        for (scb_index = 0; scb_index < MAX_SCBS; scb_index++) {
+            if ((subsManagerSCBS[scb_index].request_id == sub_datap->request_id) &&
+                (subsManagerSCBS[scb_index].hb.event_type == sub_datap->eventPackage) &&
+                (!subsManagerSCBS[scb_index].pendingClean)) {
+                scbp = &(subsManagerSCBS[scb_index]);
+                break;
+            }
+        }
+    } else {
+        /* Find SCB from sub_id */
+        scbp = find_scb_by_sub_id(sub_datap->sub_id, &scb_index);
+    }
+    if (scbp == NULL) {
+        // Process new subscription
+        if ((sub_datap->eventPackage != CC_SUBSCRIPTIONS_DIALOG) &&
+            (sub_datap->eventPackage != CC_SUBSCRIPTIONS_KPML) &&
+            (sub_datap->eventPackage != CC_SUBSCRIPTIONS_PRESENCE)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Event %d not supported!\n",
+                              fname, sub_datap->eventPackage);
+            subs_result_data.u.subs_result_data.status_code = SUBSCRIBE_FAILED_BADEVENT;
+            sip_send_error_message(&subs_result_data, sub_datap->subsNotCallbackTask,
+                                   sub_datap->subsResCallbackMsgID, sub_datap->subsResultCallback,
+                                   fname);
+            return SIP_ERROR;
+        }
+
+        // Reject this if there is no way to get back to the subscriber
+        if (!((sub_datap->subsResultCallback) ||
+              ((sub_datap->subsNotCallbackTask != CC_SRC_MIN) &&
+               sub_datap->subsResCallbackMsgID))) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No callback info provided by the App\n", fname);
+            subs_result_data.u.subs_result_data.status_code =
+                SUBSCRIBE_FAILED_BADINFO;
+            sip_send_error_message(&subs_result_data,
+                                   sub_datap->subsNotCallbackTask,
+                                   sub_datap->subsResCallbackMsgID,
+                                   sub_datap->subsResultCallback,
+                                   fname);
+            return SIP_ERROR;
+        }
+
+        // If this is a presence request - check if sufficient SCBs will still
+        // be available for other "more important" functions
+        if (sub_datap->eventPackage == CC_SUBSCRIPTIONS_PRESENCE) {
+            if (currentScbsAllocated >= LIMIT_SCBS_USAGE) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"reached Presence SCBs threshold\n", fname);
+                subs_result_data.u.subs_result_data.status_code =
+                    SUBSCRIBE_FAILED_NORESOURCE;
+                sip_send_error_message(&subs_result_data,
+                                       sub_datap->subsNotCallbackTask,
+                                       sub_datap->subsResCallbackMsgID,
+                                       sub_datap->subsResultCallback, fname);
+                return SIP_ERROR;
+            }
+        }
+
+        // Allocate SCB and copy needed parameters
+        scbp = allocate_scb(&scb_index);
+        if (!scbp) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"ran out of SCBs\n", fname);
+            subs_result_data.u.subs_result_data.status_code =
+                SUBSCRIBE_FAILED_NORESOURCE;
+            sip_send_error_message(&subs_result_data,
+                                   sub_datap->subsNotCallbackTask,
+                                   sub_datap->subsResCallbackMsgID,
+                                   sub_datap->subsResultCallback,
+                                   fname);
+            show_scbs_inuse();
+            return SIP_ERROR;
+        }
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Allocated SCB for Sending Subscribe,"
+                         " event=%d scb=%d sub_id=%x\n",
+                         DEB_F_PREFIX_ARGS(SIP_SUB, fname), sub_datap->eventPackage,
+                         scb_index, scbp->sub_id);
+        if (sub_datap->dn_line == 0 || sub_datap->dn_line > MAX_REG_LINES) {
+            // By not giving a DN line, the app is asking us to use
+            // device addressing and not line addressing
+            scbp->hb.dn_line = 1;
+            scbp->useDeviceAddressing = TRUE;
+        } else {
+            scbp->hb.dn_line = sub_datap->dn_line;
+        }
+
+        scbp->gsm_id = sub_datap->call_id;
+        if (scbp->gsm_id != 0) {
+            ccbp = sip_sm_get_ccb_by_gsm_id(scbp->gsm_id);
+        } else {
+            ccbp = NULL;
+        }
+        scbp->ccbp = ccbp;
+
+        scbp->hb.event_type = sub_datap->eventPackage;
+        scbp->hb.accept_type = sub_datap->acceptPackage;
+        if (sub_datap->eventPackage - CC_SUBSCRIPTIONS_DIALOG > -1 &&
+            sub_datap->eventPackage - CC_SUBSCRIPTIONS_DIALOG < 5) {
+            sstrncpy(scbp->event_name, eventNames[sub_datap->eventPackage - CC_SUBSCRIPTIONS_DIALOG], MAX_EVENT_NAME_LEN);
+        }
+        sstrncpy(scbp->SubURIOriginal, sub_datap->subscribe_uri,
+                 sizeof(scbp->SubURIOriginal));
+        sstrncpy(scbp->SubscriberURI, sub_datap->subscriber_uri,
+                 sizeof(scbp->SubscriberURI));
+        scbp->subsResultCallback    = sub_datap->subsResultCallback;
+        scbp->notifyIndCallback     = sub_datap->notifyIndCallback;
+        scbp->subsTermCallback      = sub_datap->subsTermCallback;
+        scbp->subsNotCallbackTask   = sub_datap->subsNotCallbackTask;
+        scbp->subsResCallbackMsgID  = sub_datap->subsResCallbackMsgID;
+        scbp->notIndCallbackMsgID   = sub_datap->subsNotIndCallbackMsgID;
+        scbp->subsTermCallbackMsgID = sub_datap->subsTermCallbackMsgID;
+
+        scbp->hb.dest_sip_addr = sub_datap->dest_sip_addr;
+        scbp->hb.dest_sip_port = sub_datap->dest_sip_port;
+
+        scbp->auto_resubscribe = sub_datap->auto_resubscribe;
+        scbp->norefersub = sub_datap->norefersub;
+        scbp->request_id = sub_datap->request_id;
+
+        // Set default value of subscribe duration, if not specified
+        if (sub_datap->duration < 0) {
+            config_get_value(CFGID_TIMER_SUBSCRIBE_EXPIRES, &subscription_expires,
+                             sizeof(subscription_expires));
+            sub_datap->duration = subscription_expires;
+        }
+        scbp->internal = TRUE;
+    } else {
+        /* SCB exists, it is a re-subscribe */
+        reSubscribe = TRUE;
+    }
+
+    scbp->hb.expires = sub_datap->duration;
+    scbp->hb.orig_expiration = sub_datap->duration;
+
+    if (scbp->hb.event_data_p) {
+        free_event_data(scbp->hb.event_data_p);
+        scbp->hb.event_data_p = NULL;
+    }
+
+    // Copy any body received - it will be framed later
+    if (sub_datap->eventData) {
+        scbp->hb.event_data_p = sub_datap->eventData;
+        sub_datap->eventData = NULL;
+    }
+
+    //re-initialize cred_type
+    scbp->hb.authen.cred_type = 0;
+
+    if (sipSPISendSubscribe(scbp, reSubscribe, FALSE /* auth */)) {
+        if (scbp->smState == SUBS_STATE_RCVD_NOTIFY) {
+            scbp->smState = SUBS_STATE_SENT_SUBSCRIBE_RCVD_NOTIFY;
+        } else {
+            scbp->smState = SUBS_STATE_SENT_SUBSCRIBE;
+        }
+        outgoingSubscribes++;
+        if (!reSubscribe) {
+            outgoingSubscriptions++;
+        }
+        return (0);
+    }
+    // If unable to send subscribe, return error immediately
+    // and return scb to the pool if not resubscribing
+    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send SUBSCRIBE message\n", fname);
+    sip_send_error_message(&subs_result_data, scbp->subsNotCallbackTask,
+                           scbp->subsResCallbackMsgID,
+                           scbp->subsResultCallback, fname);
+
+    if (!reSubscribe) {
+        free_scb(scb_index, fname);
+    }
+    return SIP_ERROR;
+}
+
+/***********************************************************
+ * Handle Application Response to a Remote Subscribe Request
+ ***********************************************************/
+int
+subsmanager_handle_ev_app_subscribe_response (cprBuffer_t buf)
+{
+    const char     *fname = "subsmanager_handle_ev_app_subscribe_response";
+    sipspi_subscribe_resp_t *subres_datap;
+    sipSCB_t       *scbp;
+    sipspi_msg_t   *pSIPSPIMsg = NULL;
+
+
+    pSIPSPIMsg = (sipspi_msg_t *) buf;
+
+    subres_datap = &(pSIPSPIMsg->msg.subscribe_resp);
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing an app subscribe response for"
+                     " sub_id=%x\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname), subres_datap->sub_id);
+    /*
+     * Find SCB from the sub_id.
+     */
+    scbp = find_scb_by_sub_id(subres_datap->sub_id, NULL);
+    if (scbp == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"no SCB for sub_id=%x found\n",
+                          fname, subres_datap->sub_id);
+        return SIP_ERROR;
+    }
+
+    scbp->hb.expires = subres_datap->duration;
+    if (sipSPISendSubscribeNotifyResponse
+        (scbp, subres_datap->response_code, scbp->last_recv_request_cseq)) {
+        if (scbp->smState == SUBS_STATE_RCVD_SUBSCRIBE_SENT_NOTIFY) {
+            scbp->smState = SUBS_STATE_SENT_NOTIFY;
+        } else {
+            scbp->smState = SUBS_STATE_ACTIVE;
+        }
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send SUBSCRIBE Response\n", fname);
+        return SIP_ERROR;
+    }
+    return (0);
+}
+
+/********************************************************
+ * Handle Application Generated NOTIFY
+ ********************************************************/
+int
+subsmanager_handle_ev_app_notify (cprBuffer_t buf)
+{
+    const char     *fname = "subsmanager_handle_ev_app_notify";
+    sipspi_notify_t *not_datap;
+    sipSCB_t       *scbp;
+    ccsip_sub_not_data_t notify_result_data;
+    sipspi_msg_t   *pSIPSPIMsg = NULL;
+    sipspi_msg_t   *temp_SIPSPIMsg = NULL;
+
+    pSIPSPIMsg = (sipspi_msg_t *) buf;
+
+    not_datap = &(pSIPSPIMsg->msg.notify);
+
+    // Fill in the return data structure in case we encounter any problems
+    notify_result_data.u.notify_result_data.status_code = NOTIFY_REQUEST_FAILED;
+    notify_result_data.msg_id = not_datap->subsNotResCallbackMsgID;
+    notify_result_data.sub_id = not_datap->sub_id;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing an app notify request for"
+                     " sub_id=%x\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname),
+                     not_datap->sub_id);
+    /*
+     * Find SCB from the sub_id.
+     */
+    scbp = find_scb_by_sub_id(not_datap->sub_id, NULL);
+    if (scbp == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"no SCB for sub_id=%x found\n", fname,
+                          not_datap->sub_id);
+        free_event_data(not_datap->eventData);
+        sip_send_error_message(&notify_result_data,
+                               not_datap->subsNotCallbackTask,
+                               not_datap->subsNotResCallbackMsgID,
+                               not_datap->notifyResultCallback,
+                               fname);
+        return SIP_ERROR;
+    }
+
+    notify_result_data.line_id = scbp->hb.dn_line;
+
+    // Check state to see if we need to queue this request
+    if ((scbp->smState == SUBS_STATE_SENT_NOTIFY) ||
+        (scbp->smState == SUBS_STATE_RCVD_SUBSCRIBE_SENT_NOTIFY)) {
+        // This means we have sent a NOTIFY but have not received a response
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Queueing request for later transmission\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+        temp_SIPSPIMsg = (sipspi_msg_t *) cpr_malloc(sizeof(sipspi_msg_t));
+        if (temp_SIPSPIMsg) {
+            /* Copy the content so that we do not touch the pSIPSPImsg */
+            (*temp_SIPSPIMsg) = (*pSIPSPIMsg);
+
+            /* Append the request */
+            if (append_pending_requests(scbp, temp_SIPSPIMsg,
+                SIPSPI_EV_CC_NOTIFY)) {
+                return SIP_DEFER;
+            }
+            cpr_free(temp_SIPSPIMsg);
+        }
+        /* We either do not have buffer or failed to append msg. */
+        free_event_data(not_datap->eventData);
+        sip_send_error_message(&notify_result_data,
+                               not_datap->subsNotCallbackTask,
+                               not_datap->subsNotResCallbackMsgID,
+                               not_datap->notifyResultCallback,
+                               fname);
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to queue request\n", fname);
+        return SIP_ERROR;
+    }
+
+    // Check state to see if we are in a position to send this NOTIFY
+    if (scbp->smState == SUBS_STATE_IDLE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad SCB State: %d\n", fname, scbp->smState);
+        free_event_data(not_datap->eventData);
+        sip_send_error_message(&notify_result_data,
+                               not_datap->subsNotCallbackTask,
+                               not_datap->subsNotResCallbackMsgID,
+                               not_datap->notifyResultCallback, fname);
+        return SIP_ERROR;
+    }
+
+    // Copy the callback function, if not already copied
+    if (not_datap->notifyResultCallback == NULL &&
+        not_datap->subsNotResCallbackMsgID == 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No callback event or function\n", fname);
+        // Can't really send back any error ...
+        free_event_data(not_datap->eventData);
+        return SIP_ERROR;
+    } else {
+        scbp->notifyResultCallback = not_datap->notifyResultCallback;
+        scbp->notResCallbackMsgID = not_datap->subsNotResCallbackMsgID;
+    }
+
+    if (scbp->hb.event_data_p) {
+        free_event_data(scbp->hb.event_data_p);
+        scbp->hb.event_data_p = NULL;
+    }
+
+    // Copy any body received - it will be framed later
+    if (not_datap->eventData) {
+        scbp->hb.event_data_p = not_datap->eventData;
+        not_datap->eventData = NULL;
+    }
+
+    // Find out if app wants to terminate this subscription
+    if (not_datap->subState == SUBSCRIPTION_TERMINATE) {
+        scbp->hb.expires = 0;
+    }
+
+    //re-initialize cred_type
+    scbp->hb.authen.cred_type = 0;
+
+    if (sipSPISendSubNotify((ccsip_common_cb_t *)scbp, FALSE) != TRUE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send Notify Message\n", fname);
+        sip_send_error_message(&notify_result_data, scbp->subsNotCallbackTask,
+                               scbp->notResCallbackMsgID,
+                               scbp->notifyResultCallback, fname);
+        return SIP_ERROR;
+    }
+
+    if (scbp->smState == SUBS_STATE_RCVD_SUBSCRIBE) {
+        scbp->smState = SUBS_STATE_RCVD_SUBSCRIBE_SENT_NOTIFY;
+    } else {
+        scbp->smState = SUBS_STATE_SENT_NOTIFY;
+    }
+    outgoingNotifies++;
+
+    return (0);
+}
+
+/**
+  * This function will handle Application Generated unsolicited NOTIFY
+  *
+  * @param[in] buf - pointer to sipspi_msg_t
+  * @param[in] line - line id.
+  *
+  * @return none
+  */
+void subsmanager_handle_ev_app_unsolicited_notify (cprBuffer_t buf, line_t line)
+{
+    const char     *fname = "subsmanager_handle_ev_app_unsolicited_notify";
+    sipspi_msg_t   *pSIPSPIMsg = NULL;
+    sipspi_notify_t *not_datap;
+    sipTCB_t       *tcbp;
+    int nat_enable = 0;
+    static uint32_t trxn_id = 1;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing an outgoing unsolicited notify request\n",
+                     DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+
+    pSIPSPIMsg = (sipspi_msg_t *) buf;
+    not_datap = &(pSIPSPIMsg->msg.notify);
+
+    /*
+     * If TCB list is not created yet, create the list.
+     */
+    if (s_TCB_list == NULL) {
+        s_TCB_list = sll_create(NULL);
+        if (s_TCB_list == NULL) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc of TCBList failed\n", fname);
+            free_event_data(not_datap->eventData);
+            return;
+        }
+    }
+
+    /*
+     *  create a TCB (transaction control block)
+     */
+    tcbp = cpr_malloc(sizeof(sipTCB_t));
+    if (tcbp == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc of TCB failed\n", fname);
+        free_event_data(not_datap->eventData);
+        return;
+    }
+    memset(tcbp, 0, sizeof(sipTCB_t));
+    tcbp->trxn_id = trxn_id;
+    trxn_id++;
+    if (trxn_id == 0) {
+        trxn_id = 1;
+    }
+    tcbp->timer = cprCreateTimer("Unsolicited transaction timer",
+                                 SIP_UNSOLICITED_TRANSACTION_TIMER,
+                                 TIMER_EXPIRATION,
+                                 sip_msgq);
+    if (tcbp->timer == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to create a timer\n", fname);
+        free_event_data(not_datap->eventData);
+        cpr_free(tcbp);
+        return;
+    }
+    tcbp->hb.cb_type = UNSOLICIT_NOTIFY_CB;
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 0) {
+        sip_config_get_net_device_ipaddr(&(tcbp->hb.src_addr));
+    } else {
+        sip_config_get_nat_ipaddr(&(tcbp->hb.src_addr));
+    }
+    tcbp->hb.dn_line = line;
+    tcbp->hb.local_port = sipTransportGetListenPort(tcbp->hb.dn_line, NULL);
+
+    tcbp->hb.event_type = not_datap->eventPackage;
+
+    // Copy any body received - it will be framed later
+    if (not_datap->eventData) {
+        tcbp->hb.event_data_p = not_datap->eventData;
+        not_datap->eventData = NULL;
+    }
+    (void) sll_append(s_TCB_list, tcbp);
+
+    if (sipSPISendSubNotify((ccsip_common_cb_t *)tcbp, FALSE) != TRUE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send Notify Message\n", fname);
+        free_tcb(tcbp);
+        return;
+    }
+
+    outgoingUnsolicitedNotifies++;
+
+    return;
+}
+
+/**
+  * This function will handle network generated response to unsolicited NOTIFY
+  *
+  * @param[in] pSipMessage - pointer to sipMessage_t
+  * @param[in] tcbp -  pointer to associated trnsaction control block.
+  *
+  * @returns SIP_OK/SIP_ERROR
+  */
+int subsmanager_handle_ev_sip_unsolicited_notify_response (sipMessage_t *pSipMessage, sipTCB_t *tcbp)
+{
+    int             response_code = 0;
+    const char     *fname = "subsmanager_handle_ev_sip_unsolicited_notify_response";
+
+    // Parse the return code
+    (void) sipGetResponseCode(pSipMessage, &response_code);
+
+    /*
+     * if the response is < 200, do nothing.
+     */
+    if (response_code < 200) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"received %d response\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname), response_code);
+        return SIP_OK;
+    }
+
+    if ((response_code == SIP_CLI_ERR_UNAUTH) ||
+        (response_code == SIP_CLI_ERR_PROXY_REQD)) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Authentication Required\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+        if (ccsip_common_util_generate_auth(pSipMessage, &tcbp->hb, SIP_METHOD_NOTIFY,
+                                            response_code, tcbp->full_ruri) == TRUE) {
+            if (sipSPISendSubNotify((ccsip_common_cb_t *)tcbp, TRUE) == TRUE) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"sent request with Auth header\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+                return SIP_OK;
+             }
+        }
+        free_tcb (tcbp);
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to respond to auth challenge\n", fname);
+        return SIP_ERROR;
+    }
+
+    free_tcb(tcbp);
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"received %d response\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname), response_code);
+    return SIP_OK;
+}
+
+/**
+  * This function will handle outgoing unsolicited NOTIFY transaction timeout.
+  *
+  * @param[in] data - pointer to an id that identifies the TCB.
+  *
+  * @returns none.
+  */
+void subsmanager_unsolicited_notify_timeout (void *data)
+{
+    const char     *fname = "subsmanager_unsolicited_notify_timeout";
+    uint32_t trxn_id = (long)data;
+    sipTCB_t *temp_tcbp = NULL;
+
+    /*
+     * make sure that the TCB still exists.
+     */
+    temp_tcbp = (sipTCB_t *)sll_next(s_TCB_list, NULL);
+    while (temp_tcbp != NULL) {
+        if (temp_tcbp->trxn_id == trxn_id) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"unsolicited notify transaction timedout\n", fname);
+            free_tcb(temp_tcbp);
+            return;
+        }
+        temp_tcbp = (sipTCB_t *)sll_next(s_TCB_list, temp_tcbp);
+    }
+}
+
+/**********************************************************
+ * Handle Application Response to a received Notify request
+ **********************************************************/
+int
+subsmanager_handle_ev_app_notify_response (cprBuffer_t buf)
+{
+    sipspi_notify_resp_t *notify_resp;
+    sipSCB_t       *scbp = NULL;
+    sipspi_msg_t   *pSIPSPIMsg = NULL;
+    uint32_t        cseq;
+    const char     *fname = "subsmanager_handle_ev_app_notify_response";
+
+    pSIPSPIMsg = (sipspi_msg_t *) buf;
+
+    notify_resp = &(pSIPSPIMsg->msg.notify_resp);
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing an app notify response for"
+                     " sub_id=%x\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname),
+                     notify_resp->sub_id);
+
+    // Retrieve response parameters
+    /*
+     * Find SCB from the sub_id.
+     */
+    scbp = find_scb_by_sub_id(notify_resp->sub_id, NULL);
+    if (scbp == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"no SCB for sub_id=%x found\n",
+                          fname, notify_resp->sub_id);
+        return SIP_ERROR;
+    }
+
+    if (notify_resp->cseq == 0) {
+        cseq = scbp->last_recv_request_cseq;
+    } else {
+        cseq = notify_resp->cseq;
+    }
+    // Call function to make and send the response
+    if (sipSPISendSubscribeNotifyResponse(scbp,
+                (uint16_t)(notify_resp->response_code), cseq)) {
+        /*
+         * if the outstanding NOTIFY transaction is only one,
+         * then update the scbp->smState.
+         */
+        if (scbp->outstandingIncomingNotifyTrxns == 1) {
+            if (scbp->smState == SUBS_STATE_SENT_SUBSCRIBE_RCVD_NOTIFY) {
+                scbp->smState = SUBS_STATE_SENT_SUBSCRIBE;
+            } else {
+                scbp->smState = SUBS_STATE_ACTIVE;
+            }
+        }
+        scbp->outstandingIncomingNotifyTrxns -= 1;
+        return (0);
+    }
+    return SIP_ERROR;
+}
+
+/********************************************************
+ * Handle Application Request to Terminate Subscription
+ ********************************************************/
+int
+subsmanager_handle_ev_app_subscription_terminated (cprBuffer_t buf)
+{
+    /*
+     * This function is used by the application to clean up a subscription
+     * whether internally or externally initiated. The application should have
+     * taken care of all the protocol related messaging before calling this
+     * function.
+     */
+    const char     *fname = "subsmanager_handle_ev_app_subscription_terminated";
+    sipspi_subscribe_term_t *subs_term;
+    int             scb_index;
+    sipSCB_t       *scbp;
+    sipspi_msg_t   *pSIPSPIMsg = NULL;
+
+
+    pSIPSPIMsg = (sipspi_msg_t *) buf;
+
+    subs_term = &(pSIPSPIMsg->msg.subs_term);
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing terminate request for sub_id=%x\n",
+                     DEB_F_PREFIX_ARGS(SIP_SUB, fname), subs_term->sub_id);
+    /*
+     * Find SCB from the sub_id and allow matching with request ID and
+     * eventPackage if sub id is not known.
+     */
+    if (subs_term->sub_id == CCSIP_SUBS_INVALID_SUB_ID) {
+        /*
+         * find scb based on request_id and event package.
+         * This scenario is possible if application decides to terminate a
+         * subscription before subsmanager provides app with sub_id.
+         */
+        for (scb_index = 0; scb_index < MAX_SCBS; scb_index++) {
+            if ((subsManagerSCBS[scb_index].request_id == subs_term->request_id) &&
+                (subsManagerSCBS[scb_index].hb.event_type == subs_term->eventPackage) &&
+                (!subsManagerSCBS[scb_index].pendingClean)) {
+                break;
+            }
+        }
+        if (scb_index >= MAX_SCBS) {
+            scbp = NULL;
+        } else {
+            scbp = &(subsManagerSCBS[scb_index]);
+        }
+    } else {
+        /* Find SCB by sub_id */
+        scbp = find_scb_by_sub_id(subs_term->sub_id, &scb_index);
+    }
+    if (scbp == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"no SCB for sub_id=%x or request id %d"
+                          " and eventPackage %d found\n", fname,
+                          subs_term->sub_id, subs_term->request_id,
+                          subs_term->eventPackage);
+        return SIP_ERROR;
+    }
+    if (scbp->smState == SUBS_STATE_IDLE || scbp->pendingClean) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SCB: scb=%d sub_id=%x has already been"
+                          " cleaned up\n", fname,
+                          scb_index, subs_term->sub_id);
+        return 0;
+    }
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Cleaning out subscription for SCB: scb=%d sub_id=%x\n",
+                     DEB_F_PREFIX_ARGS(SIP_SUB, fname), scb_index, scbp->sub_id);
+
+    if (scbp->internal) {
+        outgoingSubscriptions--;
+    } else {
+        incomingSubscriptions--;
+    }
+
+    // There could still be messages left to receive and flush, so if not
+    // immediate, we mark this SCB as pending deletion and we will clean it up
+    // later
+    if (subs_term->immediate) {
+        free_scb(scb_index, fname);
+    } else {
+        scbp->pendingClean = TRUE;
+        if (scbp->pendingRequests)
+            scbp->pendingCount = 2 * TMR_PERIODIC_SUBNOT_INTERVAL;
+        else
+            scbp->pendingCount = 1 * TMR_PERIODIC_SUBNOT_INTERVAL;
+    }
+
+    return (0);
+}
+
+
+/********************************************************
+ * Handle network response to a Sub/Not request
+ ********************************************************/
+int
+subsmanager_handle_ev_sip_response (sipMessage_t *pSipMessage)
+{
+    const char     *fname = "subsmanager_handle_ev_sip_response";
+    sipSCB_t       *scbp;
+    int             scb_index;
+    ccsip_sub_not_data_t sub_not_result_data;
+
+    // currently not used  SysHdr *pSm = NULL;
+    int             response_code = 0;
+    const char     *pCallID = NULL;
+    const char     *rsp_method = NULL;
+    sipMethod_t     method = sipMethodInvalid;
+    sipStatusCodeClass_t code_class = codeClassInvalid;
+    const char     *expires = NULL;
+    long            expiry_time;
+    const char     *to = NULL, *from = NULL;
+    const char     *record_route = NULL;
+    sipLocation_t  *to_loc = NULL;
+    sipCseq_t      *resp_cseq_structure = NULL;
+    uint32_t        cseq;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing a response\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+
+    if (sipGetResponseMethod(pSipMessage, &method) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sipGetResponseMethod");
+        return SIP_ERROR;
+    }
+    if (method == sipMethodSubscribe) {
+        rsp_method = SIP_METHOD_SUBSCRIBE;
+    } else if (method == sipMethodRefer) {
+        rsp_method = SIP_METHOD_REFER;
+    } else {
+        rsp_method = SIP_METHOD_NOTIFY;
+    }
+
+    pCallID = sippmh_get_cached_header_val(pSipMessage, CALLID);
+    if (!pCallID) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Cannot obtain SIP Call ID.\n", fname);
+        return SIP_ERROR;
+    }
+
+    if (!getCSeqInfo(pSipMessage, &resp_cseq_structure)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Cannot obtain SIP CSEQ.\n", fname);
+        return SIP_ERROR;
+    }
+    cseq = resp_cseq_structure->number;
+    cpr_free(resp_cseq_structure);
+
+    // Locate request scbp from that match the response
+    scbp = find_req_scb(pCallID, method, cseq, &scb_index);
+    if (!scbp) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No matching request found\n", fname);
+        return SIP_ERROR;
+    }
+    // Cancel any outstanding retry timer
+    if (scbp->hb.retx_flag == TRUE) {
+        sip_platform_msg_timer_subnot_stop(&sipPlatformUISMSubNotTimers[scb_index]);
+        scbp->hb.retx_flag = FALSE;
+    }
+    // Parse the return code
+    (void) sipGetResponseCode(pSipMessage, &response_code);
+    code_class = sippmh_get_code_class((uint16_t) response_code);
+
+    /*
+     * If it is a response (18x/2xx) to a dialog initiating SUBSCRIBE,
+     * parse the Record-Route header and set up Route set for this dialog.
+     * If the sip_to_tag is not yet populated and if there is no associated
+     * CCB, then this can be considered as a response to dialog
+     * initating SUBSCRIBE.
+     */
+    if ((strcmp(rsp_method, SIP_METHOD_SUBSCRIBE) == 0) &&
+        (scbp->ccbp == NULL) && (scbp->sip_to_tag[0] == '\0')) {
+        if (((response_code >= 180) && (response_code <= 189)) ||
+            ((response_code >= 200) && (response_code <= 299))) {
+            record_route = sippmh_get_cached_header_val(pSipMessage,
+                                                        RECORD_ROUTE);
+            if (record_route) {
+                if (scbp->record_route_info) {
+                    sippmh_free_record_route(scbp->record_route_info);
+                }
+                scbp->record_route_info = sippmh_parse_record_route(record_route);
+                if (scbp->record_route_info == NULL) {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sippmh_parse_record_route() failed",
+                                      fname);
+                    return SIP_ERROR;
+                }
+            }
+        }
+    }
+
+    if ((response_code == SIP_CLI_ERR_UNAUTH) ||
+        (response_code == SIP_CLI_ERR_PROXY_REQD)) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Authentication Required\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+        if (ccsip_common_util_generate_auth(pSipMessage, (ccsip_common_cb_t *)scbp, rsp_method,
+                                            response_code, scbp->SubURI) == TRUE) {
+            // Send Sub/Not message again - this time with authorization
+            if ((method == sipMethodSubscribe) || (method == sipMethodRefer)) {
+                (void) sipSPISendSubscribe(scbp, TRUE, TRUE);
+            } else {
+                (void) sipSPISendSubNotify((ccsip_common_cb_t *)scbp, TRUE);
+            }
+        } else {
+            // Return error to caller
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to generate Auth header", fname);
+            return SIP_ERROR;
+        }
+        return (0);
+    }
+
+    // If this subscription has been cleaned by the application, then
+    // return SCB to IDLE state and discard this response
+    if (scbp->pendingClean) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recd msg for terminated sub\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+        if (scbp->pendingRequests) {
+            scbp->pendingCount += TMR_PERIODIC_SUBNOT_INTERVAL;
+            scbp->smState = SUBS_STATE_ACTIVE;
+            handle_pending_requests(scbp);
+        } else {
+            free_scb(scb_index, fname);
+        }
+        return (0);
+    }
+
+    /*
+     * if response code is 423, grab Min-Expires and let app know so
+     * that it can be used for subsequent subscriptions.
+     */
+    if ((response_code == SIP_CLI_ERR_INTERVAL_TOO_SMALL) &&
+        (strcmp(rsp_method, SIP_METHOD_SUBSCRIBE) == 0)) {
+        expires = sippmh_get_header_val(pSipMessage,
+                                        (const char *)SIP_HEADER_MIN_EXPIRES,
+                                        NULL);
+        if (expires) {
+            expiry_time = strtoul(expires, NULL, 10);
+            //ensure new Min-Expires is > what we set before in Expires
+            if ((long) expiry_time > scbp->hb.expires) {
+                scbp->hb.expires = expiry_time;
+            }
+        }
+    } else {
+        // Get the expires header value and add to scb
+        expires = sippmh_get_header_val(pSipMessage, SIP_HEADER_EXPIRES, NULL);
+        if (expires) {
+            expiry_time = strtoul(expires, NULL, 10);
+            scbp->hb.expires = expiry_time;
+        }
+    }
+
+    // update to and from headers to capture the tag
+    if (strcmp(rsp_method, SIP_METHOD_SUBSCRIBE) == 0) {
+        to = sippmh_get_cached_header_val(pSipMessage, TO);
+        from = sippmh_get_cached_header_val(pSipMessage, FROM);
+        scbp->sip_to = strlib_update(scbp->sip_to, to);
+        // grab the to-tag if present, and if this is a response to a SUBSCRIBE
+        to_loc = sippmh_parse_from_or_to((char *) to, TRUE);
+        if (to_loc != NULL) {
+            if (to_loc->tag != NULL) {
+                scbp->sip_to_tag = strlib_update(scbp->sip_to_tag,
+                                                 sip_sm_purify_tag(to_loc->tag));
+            }
+            sippmh_free_location(to_loc);
+        }
+        scbp->sip_from = strlib_update(scbp->sip_from, from);
+    }
+
+    // Delete body, if any, since it is no longer needed
+    if (code_class > codeClass1xx) {
+        if (scbp->hb.event_data_p) {
+            free_event_data(scbp->hb.event_data_p);
+            scbp->hb.event_data_p = NULL;
+        }
+    }
+
+    if ((scbp->smState == SUBS_STATE_SENT_SUBSCRIBE_RCVD_NOTIFY) ||
+        (scbp->smState == SUBS_STATE_SENT_SUBSCRIBE)) {
+        sub_not_result_data.u.subs_result_data.expires = scbp->hb.expires;
+        sub_not_result_data.u.subs_result_data.status_code = response_code;
+        sub_not_result_data.request_id = scbp->request_id;
+        sub_not_result_data.sub_id = scbp->sub_id;
+        sub_not_result_data.msg_id = scbp->subsResCallbackMsgID;
+        sub_not_result_data.gsm_id = scbp->gsm_id;
+        sub_not_result_data.line_id = scbp->hb.dn_line;
+
+        if (code_class > codeClass1xx) {
+            if (scbp->smState == SUBS_STATE_SENT_SUBSCRIBE_RCVD_NOTIFY) {
+                scbp->smState = SUBS_STATE_RCVD_NOTIFY;
+            } else {
+                scbp->smState = SUBS_STATE_ACTIVE;
+            }
+        }
+
+        if (scbp->subsResultCallback) {
+            (scbp->subsResultCallback) (&sub_not_result_data);
+        } else if (scbp->subsNotCallbackTask != CC_SRC_MIN) {
+            (void) sip_send_message(&sub_not_result_data,
+                                    scbp->subsNotCallbackTask,
+                                    scbp->subsResCallbackMsgID);
+        }
+    } else if ((scbp->smState == SUBS_STATE_SENT_NOTIFY) ||
+               (scbp->smState == SUBS_STATE_RCVD_SUBSCRIBE_SENT_NOTIFY)) {
+        sub_not_result_data.u.notify_result_data.status_code = response_code;
+        sub_not_result_data.request_id = scbp->request_id;
+        sub_not_result_data.sub_id = scbp->sub_id;
+        sub_not_result_data.msg_id = scbp->notResCallbackMsgID;
+        sub_not_result_data.gsm_id = scbp->gsm_id;
+        sub_not_result_data.line_id = scbp->hb.dn_line;
+
+        if (code_class > codeClass1xx) {
+            if (scbp->smState == SUBS_STATE_RCVD_SUBSCRIBE_SENT_NOTIFY) {
+                scbp->smState = SUBS_STATE_RCVD_SUBSCRIBE;
+            } else {
+                scbp->smState = SUBS_STATE_ACTIVE;
+            }
+        }
+
+        if (scbp->notifyResultCallback) {
+            (scbp->notifyResultCallback) (&sub_not_result_data);
+        } else if (scbp->subsNotCallbackTask != CC_SRC_MIN) {
+            (void) sip_send_message(&sub_not_result_data,
+                                    scbp->subsNotCallbackTask,
+                                    scbp->notResCallbackMsgID);
+        }
+        // If there are pending requests - handle them now
+        if (scbp->pendingRequests) {
+            handle_pending_requests(scbp);
+        }
+    } else {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Incorrect SCB State\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+        return SIP_ERROR;
+    }
+
+    return (0);
+}
+
+/********************************************************
+ * Handle Network Received Subscribe Request
+ ********************************************************/
+int
+subsmanager_handle_ev_sip_subscribe (sipMessage_t *pSipMessage,
+                                     sipMethod_t sipMethod,
+                                     boolean in_dialog)
+{
+    const char     *fname = "subsmanager_handle_ev_sip_subscribe";
+    const char     *event = NULL;
+    cc_subscriptions_t eventPackage = CC_SUBSCRIPTIONS_NONE;
+    sipSCB_t       *scbpReg = NULL, *scbp = NULL;
+    const char     *callID = NULL, *via = NULL;
+    const char     *contact = NULL;
+    const char     *record_route = NULL;
+    const char     *expires = NULL;
+    const char     *require = NULL, *supported = NULL;
+    sipCseq_t      *request_cseq_structure = NULL;
+    unsigned long   request_cseq_number = 0, expiry_time = 0;
+
+    // currently not used  unsigned long diff_time = 0;
+    sipMethod_t     request_cseq_method = sipMethodInvalid;
+    unsigned int    content_length = 0;
+    line_t          dn_line = 0;
+    boolean         request_uri_error = FALSE;
+    sipReqLine_t   *requestURI = NULL;
+
+    // currently not used  sipLocation_t *uri_loc = NULL;
+    genUrl_t       *genUrl = NULL;
+    const char     *sip_from = NULL;
+    const char     *sip_to = NULL;
+    sipLocation_t  *to_loc = NULL;
+    sipLocation_t  *from_loc = NULL;
+    sipUrl_t       *sipUriUrl = NULL, *sipFromUrl = NULL;
+    char           *pUser = NULL;
+    char           *sip_to_tag_temp, *sip_to_temp;
+    char           *kpml_call_id = NULL, *from_tag, *to_tag;
+    ccsip_event_data_t *subDatap = NULL;
+    int             scb_index;
+    boolean         reSubscribe = FALSE;
+    ccsip_sub_not_data_t subs_ind_data;
+    ccsipCCB_t     *ccb = NULL;
+    char           *referToString = NULL;
+    uint32_t        tags = 0;
+    int             result;
+    int             requestStatus = SIP_MESSAGING_ERROR;
+    uint8_t         i;
+    char            line_contact[MAX_LINE_CONTACT_SIZE];
+    char            line_name[MAX_LINE_NAME_SIZE];
+    int             noOfReferTo = 0;
+    sipServiceControl_t *scp=NULL;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing a new SIP subscription request\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+
+    if (!subsManagerRunning) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Subscription Manager Not Initialized!\n", fname);
+        return SIP_ERROR;
+    }
+
+    /* Steps:
+     * * Check request for support by a registered app
+     * * If not, reject the request as unsupported
+     * * Decode the body, if any, and if understood
+     * * Allocate and fill in SCB
+     * * Call the callback function by registered application
+     */
+    if (!in_dialog) {
+        requestStatus = sipSPICheckRequest(NULL, pSipMessage);
+        if (requestStatus != SIP_MESSAGING_OK) {
+            if (requestStatus == SIP_MESSAGING_DUPLICATE) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Recieved duplicate request\n", fname);
+            } else {
+                if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                            SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                            SIP_WARN_MISC, NULL, NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_BAD_REQ);
+                }
+            }
+            return SIP_ERROR;
+        }
+    }
+
+    memset(&subs_ind_data, 0, sizeof(ccsip_sub_not_data_t));
+
+    // Get relevant parameters from the SIP message
+    // These are: Event, Subscription-State, Accept, Content-Length besides
+    // To, From, Via, Call-ID, CSeq, and Contact
+
+    event = sippmh_get_header_val(pSipMessage, SIP_HEADER_EVENT,
+                                  SIP_C_HEADER_EVENT);
+
+    if (event) {
+        if (cpr_strcasecmp(event, "dialog") == 0) {
+            eventPackage = CC_SUBSCRIPTIONS_DIALOG;
+        } else if (cpr_strncasecmp(event, "kpml", 4) == 0) {
+            eventPackage = CC_SUBSCRIPTIONS_KPML;
+        } else if (cpr_strcasecmp(event, "presence") == 0) {
+            eventPackage = CC_SUBSCRIPTIONS_PRESENCE;
+        } else {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unsupported event=%s\n", fname, event);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_EVENT,
+                                        SIP_CLI_ERR_BAD_EVENT_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_EVENT);
+            }
+            return SIP_ERROR;
+        }
+    } else if (sipMethod == sipMethodRefer) {
+
+       } else if (sipMethod == sipMethodBye) {
+
+            return SIP_ERROR;
+
+    } else {
+        // Reached here in error
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No event header\n", fname);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_EVENT,
+                                    SIP_CLI_ERR_BAD_EVENT_PHRASE,
+                                    SIP_WARN_MISC,
+                                    NULL,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_EVENT);
+        }
+        return SIP_ERROR;
+    }
+
+
+    // Parse Call-ID
+    callID = sippmh_get_cached_header_val(pSipMessage, CALLID);
+    if (!callID) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to obtain request's "
+                          "Call-ID header.\n", fname);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_CALLID_ABSENT,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return SIP_ERROR;
+    }
+    // Check content
+    content_length = sippmh_get_content_length(pSipMessage);
+
+    if (pSipMessage->raw_body) {
+        if (content_length != strlen(pSipMessage->raw_body)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"\n Mismatched Content length and \
+                             Actual message body length:content length=%d \
+                             \n and message as %s \
+                             \n and strlenof messagebody = %d\n", fname,
+                             content_length, pSipMessage->raw_body,
+                             strlen(pSipMessage->raw_body));
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        SIP_WARN_MISC,
+                                        SIP_CLI_ERR_BAD_REQ_CONTENT_LENGTH_ERROR,
+                                        NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+            return SIP_ERROR;
+        }
+    } else if (content_length != 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"\n Mismatched Content length and \
+                             Actual message body length:content length=%d \
+                             \n and message is EMPTY \
+                             \n and strlenof messagebody = 0\n", fname,
+                             content_length);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_CONTENT_LENGTH_ERROR,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return SIP_ERROR;
+    }
+    // Parse CSEQ
+    if (getCSeqInfo(pSipMessage, &request_cseq_structure) == FALSE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to obtain or parse request's CSeq "
+                          "header.\n", fname);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_CSEQ_FIELD,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return SIP_ERROR;
+    }
+
+    request_cseq_number = request_cseq_structure->number;
+    request_cseq_method = request_cseq_structure->method;
+    cpr_free(request_cseq_structure);
+
+    // Parse From
+    sip_from = sippmh_get_cached_header_val(pSipMessage, FROM);
+    from_loc = sippmh_parse_from_or_to((char *) sip_from, TRUE);
+    if (!from_loc) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_FROM));
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_FROM_OR_TO_FIELD,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return SIP_ERROR;
+    }
+    // Parse To
+    sip_to = sippmh_get_cached_header_val(pSipMessage, TO);
+    to_loc = sippmh_parse_from_or_to((char *) sip_to, TRUE);
+    if (!to_loc) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname,
+                          get_debug_string(DEBUG_FUNCTIONNAME_SIPPMH_PARSE_TO));
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_FROM_OR_TO_FIELD,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        sippmh_free_location(from_loc);
+        return SIP_ERROR;
+    }
+    // Parse Req-URI
+    requestURI = sippmh_get_request_line(pSipMessage);
+    if (requestURI) {
+        if (requestURI->url) {
+            genUrl = sippmh_parse_url(requestURI->url, TRUE);
+            if (genUrl) {
+                if (genUrl->schema != URL_TYPE_SIP) {
+                    request_uri_error = TRUE;
+                }
+            } else {
+                request_uri_error = TRUE;
+            }
+        } else {
+            request_uri_error = TRUE;
+        }
+
+    } else {
+        request_uri_error = TRUE;
+    }
+    if (request_uri_error) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Invalid Request URI" "failed.\n", fname);
+        if (to_loc)
+            sippmh_free_location(to_loc);
+        if (from_loc)
+            sippmh_free_location(from_loc);
+        if (genUrl)
+            sippmh_genurl_free(genUrl);
+        if (requestURI)
+            SIPPMH_FREE_REQUEST_LINE(requestURI);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_URL_ERROR,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return SIP_ERROR;
+    }
+    // Parse Expires header
+    expires = sippmh_get_header_val(pSipMessage, SIP_HEADER_EXPIRES, NULL);
+    if (expires) {
+        expiry_time = strtoul(expires, NULL, 10);
+    } else {
+        // No expires header, use default
+        expiry_time = 3600;
+    }
+
+    scbp = find_scb_by_subscription(eventPackage, &scb_index, callID);
+    if (scbp && eventPackage == scbp->hb.event_type) {
+        // SCB is already there - must be a re-subscribe
+        reSubscribe = TRUE;
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received a reSubscribe Message\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+        if (scbp->pendingClean) {
+            // Oops, the application has already terminated this subscription
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Subscribe received for subscription "
+                              "already terminated by application.\n", fname);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        SIP_WARN_MISC,
+                                        SIP_CLI_ERR_BAD_REQ_SUBSCRIPTION_DELETED,
+                                        NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+            sippmh_free_location(to_loc);
+            sippmh_free_location(from_loc);
+            sippmh_genurl_free(genUrl);
+            SIPPMH_FREE_REQUEST_LINE(requestURI);
+            return SIP_ERROR;
+        }
+        // Check continuity of CSeq numbers
+        if (request_cseq_number <= scbp->last_recv_request_cseq) {
+            // Return 500 Internal Server Error
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Out of order CSeq number received\n",
+                              fname);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_SERV_ERR_INTERNAL,
+                                        SIP_SERV_ERR_INTERNAL_PHRASE,
+                                        SIP_WARN_MISC,
+                                        SIP_CLI_ERR_BAD_REQ_CSEQ_FIELD,
+                                        NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_SERV_ERR_INTERNAL);
+            }
+            sippmh_free_location(to_loc);
+            sippmh_free_location(from_loc);
+            sippmh_genurl_free(genUrl);
+            SIPPMH_FREE_REQUEST_LINE(requestURI);
+            return SIP_ERROR;
+        }
+
+        sippmh_free_location(to_loc);
+        sippmh_free_location(from_loc);
+        sippmh_genurl_free(genUrl);
+        SIPPMH_FREE_REQUEST_LINE(requestURI);
+    } else {
+        // Check if this is a SUBSCRIBE request for an already terminated
+        // subscription.  If this subscription is associated with an
+        // existing dialog, let it go
+        ccb = sip_sm_get_ccb_by_callid(callID);
+        if ((ccb == NULL) &&
+            is_previous_sub(callID, from_loc->tag, eventPackage)) {
+            // Return 481 Internal Server Error
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SUBSCRIBE received for terminated "
+                              "subscription\n", fname);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_CALLEG,
+                                        SIP_CLI_ERR_CALLEG_PHRASE,
+                                        SIP_WARN_MISC,
+                                        SIP_CLI_ERR_BAD_REQ_SUBSCRIPTION_DELETED,
+                                        NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_CALLEG);
+            }
+            sippmh_free_location(to_loc);
+            sippmh_free_location(from_loc);
+            sippmh_genurl_free(genUrl);
+            SIPPMH_FREE_REQUEST_LINE(requestURI);
+            return SIP_ERROR;
+        }
+        // This is a valid new subscription - get all the params
+        scbpReg = find_scb_by_registration(eventPackage, &scb_index);
+        if (!scbpReg) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No application registered "
+                              "to accept this event.\n", fname);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_EVENT,
+                                        SIP_CLI_ERR_BAD_EVENT_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_EVENT);
+            }
+            sippmh_free_location(to_loc);
+            sippmh_free_location(from_loc);
+            sippmh_genurl_free(genUrl);
+            SIPPMH_FREE_REQUEST_LINE(requestURI);
+            return SIP_ERROR;
+        }
+
+        scbp = allocate_scb(&scb_index);
+        if (!scbp) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No SCB Available "
+                              "to accept this event.\n", fname);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_SERV_ERR_INTERNAL,
+                                        SIP_SERV_ERR_INTERNAL_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+            sippmh_free_location(to_loc);
+            sippmh_free_location(from_loc);
+            sippmh_genurl_free(genUrl);
+            SIPPMH_FREE_REQUEST_LINE(requestURI);
+            show_scbs_inuse();
+            return SIP_ERROR;
+        }
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Allocated SCB for Received Subscribe, event=%d,"
+                         " scb=%d sub_id=%x\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname), scbpReg->hb.event_type,
+                         scb_index, scbp->sub_id);
+        scbp->hb.event_type = scbpReg->hb.event_type;
+        scbp->subsIndCallback = scbpReg->subsIndCallback;
+        scbp->subsIndCallbackTask = scbpReg->subsIndCallbackTask;
+        scbp->subsNotCallbackTask = scbpReg->subsNotCallbackTask;
+        scbp->subsIndCallbackMsgID = scbpReg->subsIndCallbackMsgID;
+        scbp->subsTermCallback = scbpReg->subsTermCallback;
+        scbp->subsTermCallbackMsgID = scbpReg->subsTermCallbackMsgID;
+        sstrncpy(scbp->event_name, scbpReg->event_name, MAX_EVENT_NAME_LEN);
+
+        scbp->internal = FALSE;
+
+        /* If the event package is KPML then check if the ID values
+         * are associated with it
+         */
+        if (eventPackage == CC_SUBSCRIPTIONS_KPML) {
+            /*
+             * At this point from_tag and to_tag is never used.  Since
+             * kpml_call_id, from_tag and to_tag just point to the
+             * appropriate location and does not allocate memory by itself.
+             */
+            (void) sippmh_parse_kpml_event_id_params((char *)event,
+                                                     &kpml_call_id,
+                                                     &from_tag,
+                                                     &to_tag);
+        }
+
+        if (kpml_call_id) {
+            ccb = sip_sm_get_ccb_by_callid(kpml_call_id);
+        } else {
+            ccb = sip_sm_get_ccb_by_callid(callID);
+            scbp->ccbp = ccb;
+        }
+        if (ccb) {
+            scbp->gsm_id = ccb->gsm_id;
+            scbp->hb.dn_line = ccb->dn_line;
+        } else {
+            scbp->gsm_id = 0;
+            scbp->hb.dn_line = 0;
+        }
+        // Parse Supported and Required headers to see if there is the
+        // norefersub option tag specified
+        require = sippmh_get_cached_header_val(pSipMessage, REQUIRE);
+        if (require) {
+            tags = sippmh_parse_supported_require(require, NULL);
+            if (tags & norefersub_tag) {
+                scbp->norefersub = TRUE;
+            }
+         }
+        if ((eventPackage == CC_SUBSCRIPTIONS_CONFIGAPP) &&
+             (scbp->norefersub == FALSE)) {
+             //noReferSub tag is required for OOD Refer for config change
+             CCSIP_DEBUG_ERROR(SIP_F_PREFIX"noReferSub missing from Require header.\n",
+                               fname);
+             if (sipSPISendErrorResponse(pSipMessage,
+                                   SIP_CLI_ERR_BAD_REQ,
+                                   SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                   SIP_WARN_MISC,
+                                   SIP_CLI_ERR_BAD_REQ_REQUIRE_HDR,
+                                 NULL) != TRUE) {
+                 CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                       fname, SIP_CLI_ERR_BAD_REQ);
+             }
+             free_scb(scb_index, fname);
+             sippmh_free_location(to_loc);
+             sippmh_free_location(from_loc);
+             sippmh_genurl_free(genUrl);
+             SIPPMH_FREE_REQUEST_LINE(requestURI);
+             return SIP_ERROR;
+         }
+        supported = sippmh_get_cached_header_val(pSipMessage, SUPPORTED);
+        if (supported) {
+            tags = sippmh_parse_supported_require(supported, NULL);
+            if (tags & norefersub_tag) {
+                scbp->norefersub = TRUE;
+            }
+        }
+        // Check to see if the expires value is within the acceptable range,
+        // if any has been given to us
+        if (scbp->min_expires != 0) {
+            if (expiry_time < scbp->min_expires && expiry_time < 3600) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Too small expiry time: %d; "
+                                  "Min acceptable: %d.\n", fname,
+                                  expiry_time, scbp->min_expires);
+
+                if (sipSPISendErrorResponse(pSipMessage,
+                                            SIP_CLI_ERR_INTERVAL_TOO_SMALL,
+                                            SIP_CLI_ERR_INTERVAL_TOO_SMALL_PHRASE,
+                                            0, NULL, NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_INTERVAL_TOO_SMALL);
+                }
+                free_scb(scb_index, fname);
+                sippmh_free_location(to_loc);
+                sippmh_free_location(from_loc);
+                sippmh_genurl_free(genUrl);
+                SIPPMH_FREE_REQUEST_LINE(requestURI);
+                return SIP_ERROR;
+            }
+        }
+        if (scbp->max_expires != 0) {
+            if (expiry_time > scbp->max_expires) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Too large expiry time: %d; "
+                                  "Max acceptable: %d.\n", fname,
+                                  expiry_time, scbp->max_expires);
+                // There doesn't seem to be any particular error code for
+                // maximum expiry time so just return the generic error
+                if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                            SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                            SIP_WARN_MISC,
+                                            SIP_CLI_ERR_INTERVAL_TOO_LARGE_PHRASE,
+                                            NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_BAD_REQ);
+                }
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Freeing SCB: scb=%d sub_id=%x\n",
+                                 DEB_F_PREFIX_ARGS(SIP_SUB, fname), scb_index, scbp->sub_id);
+                free_scb(scb_index, fname);
+                sippmh_free_location(to_loc);
+                sippmh_free_location(from_loc);
+                sippmh_genurl_free(genUrl);
+                SIPPMH_FREE_REQUEST_LINE(requestURI);
+                return SIP_ERROR;
+            }
+        }
+        sipUriUrl = genUrl->u.sipUrl;
+        if (sipUriUrl) {
+            pUser = sippmh_parse_user(sipUriUrl->user);
+            if (pUser) {
+                sstrncpy(scbp->SubURI, pUser, sizeof(scbp->SubURI));
+                cpr_free(pUser);
+            } else {
+                /* An error occurred, copy the whole thing.. */
+                sstrncpy(scbp->SubURI, sipUriUrl->user, sizeof(scbp->SubURI));
+            }
+        }
+        sippmh_genurl_free(genUrl);
+        SIPPMH_FREE_REQUEST_LINE(requestURI);
+
+        scbp->sip_from = strlib_update(scbp->sip_from, sip_from);
+        if (from_loc->tag) {
+            scbp->sip_from_tag = strlib_update(scbp->sip_from_tag,
+                                               sip_sm_purify_tag(from_loc->tag));
+        }
+
+        if (from_loc->genUrl->schema == URL_TYPE_SIP) {
+            sipFromUrl = from_loc->genUrl->u.sipUrl;
+        }
+        if (sipFromUrl) {
+            if (sipFromUrl->user) {
+                char    *pUserTemp, *target = NULL, addr_error;
+                uint32_t sip_address = 0;
+
+                pUserTemp = sippmh_parse_user(sipFromUrl->user);
+                if (pUserTemp) {
+                    scbp->callingNumber = strlib_update(scbp->callingNumber,
+                                                        pUserTemp);
+                    cpr_free(pUserTemp);
+                } else {
+                    scbp->callingNumber = strlib_update(scbp->callingNumber,
+                                                        sipFromUrl->user);
+                }
+
+                target = cpr_strdup(sipFromUrl->host);
+                if (!target) {
+                    sip_address = 0;
+                } else {
+//CPR TODO: need reference for
+//Should replace with a boolean util_check_ip_addr(char *addr) call
+                    sip_address = IPNameCk(target, &addr_error);
+                    cpr_free(target);
+                }
+
+                if ((from_loc->genUrl->schema == URL_TYPE_SIP) &&
+                    (!sip_address)) {
+                    if (scbp->callingNumber) {
+                        scbp->callingNumber = strlib_append(scbp->callingNumber, "@");
+                        scbp->callingNumber = strlib_append(scbp->callingNumber, sipFromUrl->host);
+                    }
+                }
+            } else {
+                scbp->callingNumber = strlib_update(scbp->callingNumber,
+                                                    "Unknown Number");
+            }
+        }
+
+        scbp->sip_to = strlib_update(scbp->sip_to, sip_to);
+        if (to_loc->tag == NULL) {
+            // Create To tag
+            sip_to_tag_temp = strlib_open(scbp->sip_to_tag, MAX_SIP_TAG_LENGTH);
+            if (sip_to_tag_temp) {
+                sip_util_make_tag(sip_to_tag_temp);
+            }
+            scbp->sip_to_tag = strlib_close(sip_to_tag_temp);
+            sip_to_temp = strlib_open(scbp->sip_to, MAX_SIP_URL_LENGTH);
+            if (sip_to_temp) {
+                sstrncat(sip_to_temp, ";tag=",
+                        MAX_SIP_URL_LENGTH - strlen(sip_to_temp));
+                if (scbp->sip_to_tag) {
+                    sstrncat(sip_to_temp, scbp->sip_to_tag,
+                            MAX_SIP_URL_LENGTH - strlen(sip_to_temp));
+                }
+            }
+            scbp->sip_to = strlib_close(sip_to_temp);
+        }
+
+        sippmh_free_location(to_loc);
+        sippmh_free_location(from_loc);
+
+        sstrncpy(scbp->hb.sipCallID, callID, MAX_SIP_CALL_ID);
+
+        // Parse Contact info
+        contact = sippmh_get_cached_header_val(pSipMessage, CONTACT);
+        if (contact) {
+            if (scbp->contact_info) {
+                sippmh_free_contact(scbp->contact_info);
+            }
+            scbp->contact_info = sippmh_parse_contact(contact);
+
+            if ((scbp->contact_info == NULL) || // contact in msg, parse error
+                (sipSPICheckContact(contact) < 0)) { // If contact is invalid
+                if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                            SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                            SIP_WARN_MISC,
+                                            SIP_CLI_ERR_BAD_REQ_CONTACT_FIELD,
+                                            NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_BAD_REQ);
+                }
+                free_scb(scb_index, fname);
+                return SIP_ERROR;
+            }
+        }
+
+        if ((sipMethod == sipMethodSubscribe) && (scbp->ccbp == NULL)) {
+            record_route = sippmh_get_cached_header_val(pSipMessage,
+                                                        RECORD_ROUTE);
+            if (record_route) {
+                if (scbp->record_route_info) {
+                    sippmh_free_record_route(scbp->record_route_info);
+                }
+                scbp->record_route_info = sippmh_parse_record_route(record_route);
+                if (scbp->record_route_info == NULL) {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sippmh_parse_record_route() failed",
+                                      fname);
+                    free_scb(scb_index, fname);
+                    return SIP_ERROR;
+                }
+                /*
+                 * Store the Record-Route header value to be used
+                 * in 18x or 2xx response
+                 */
+                scbp->cached_record_route = strlib_update(scbp->cached_record_route,
+                                                          record_route);
+            }
+        }
+
+    } // End of populating a new SCB
+
+    scbp->hb.expires = expiry_time;
+    scbp->hb.orig_expiration = expiry_time;
+    scbp->last_recv_request_cseq = request_cseq_number;
+    scbp->last_recv_request_cseq_method = request_cseq_method;
+
+    // Parse Refer-To
+    if (sipMethod == sipMethodRefer) {
+        sipReferTo_t   *referto = NULL;
+
+        noOfReferTo = sippmh_get_num_particular_headers(pSipMessage,
+                                                        SIP_HEADER_REFER_TO,
+                                                        SIP_C_HEADER_REFER_TO,
+                                                        &referToString,
+                                                        MAX_REFER_TO_HEADERS);
+
+        if ((noOfReferTo == 0) || (noOfReferTo > 1)) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              scb_index, scbp->hb.dn_line, fname,
+                              "Incorrect number of Refer-To headers\n");
+            (void) sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                           SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                           SIP_WARN_MISC,
+                                           SIP_CLI_ERR_BAD_REQ_PHRASE_REFER_TO,
+                                           NULL);
+            return SIP_ERROR;
+        }
+        // Note that the refer-to header is not parsed for CCM mode since it was
+        // malformed in older versions of CCM
+        if (sip_regmgr_get_cc_mode(1) != REG_MODE_CCM) {
+            referto = sippmh_parse_refer_to(referToString);
+            if (referto == NULL) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                  scb_index, scbp->hb.dn_line, fname,
+                                  "Refer-To header could not be parsed\n");
+                (void) sipSPISendErrorResponse(pSipMessage,
+                                               SIP_CLI_ERR_BAD_REQ,
+                                               SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                               SIP_WARN_MISC,
+                                               SIP_CLI_ERR_BAD_REQ_PHRASE_REFER_TO,
+                                               NULL);
+                return SIP_ERROR;
+            } else {
+                sippmh_free_refer_to(referto);
+            }
+        }
+    }
+
+    // Parse and store Via Header
+    via = sippmh_get_cached_header_val(pSipMessage, VIA);
+    if (via) {
+        /* store the via header */
+        if (store_incoming_trxn(via, request_cseq_number,  scbp) == FALSE) {
+            (void) sipSPISendErrorResponse(pSipMessage,
+                                               SIP_SERV_ERR_INTERNAL,
+                                               SIP_SERV_ERR_INTERNAL_PHRASE,
+                                               0,
+                                               NULL,
+                                               NULL);
+            return SIP_ERROR;
+        }
+    }
+
+    // See if can determine dn_line from any of the parameters
+    // 1st try the REQ-URI
+    // CCM should send us
+    // SUBSCRIBE sip:<subscribed-line-pkid>@<phone-ip> SIP/2.0
+    // and we should be able to get the line from its pkid
+    if (scbp->hb.dn_line == 0) {
+        for (dn_line = 1; dn_line <= MAX_REG_LINES; dn_line++) {
+            if (sip_config_check_line(dn_line) == FALSE) {
+                continue;
+            }
+            config_get_line_string(CFGID_LINE_CONTACT, line_contact,
+                                   dn_line, sizeof(line_contact));
+            if (cpr_strcasecmp(line_contact, UNPROVISIONED) != 0) {
+                if (cpr_strcasecmp(scbp->SubURI, line_contact) == 0) {
+                    scbp->hb.dn_line = dn_line;
+                    break;
+                }
+            }
+        }
+    }
+    if (scbp->hb.dn_line == 0) {
+        // If no match there, check against the DN number
+        for (dn_line = 1; dn_line <= MAX_REG_LINES; dn_line++) {
+            config_get_line_string(CFGID_LINE_NAME, line_name,
+                                   dn_line, sizeof(line_name));
+            if (!cpr_strcasecmp(scbp->SubURI, line_name)) {
+                scbp->hb.dn_line = dn_line;
+                break;
+            }
+        }
+    }
+    // If unable to determine dn_line, the SUBSCRIBE is directed to device
+    // Could make sure and check against the MAC address here
+    if (scbp->hb.dn_line == 0) {
+        scbp->useDeviceAddressing = TRUE;
+    }
+    // Parse Body
+    subs_ind_data.u.subs_ind_data.eventData = NULL;
+    i = 0;
+    while (i < HTTPISH_MAX_BODY_PARTS && pSipMessage->mesg_body[i].msgBody
+            != NULL) {
+        if (pSipMessage->mesg_body[i].msgContentTypeValue != SIP_CONTENT_TYPE_DIALOG_VALUE &&
+            pSipMessage->mesg_body[i].msgContentTypeValue != SIP_CONTENT_TYPE_KPML_REQUEST_VALUE &&
+            pSipMessage->mesg_body[i].msgContentTypeValue != SIP_CONTENT_TYPE_REMOTECC_REQUEST_VALUE &&
+            pSipMessage->mesg_body[i].msgContentTypeValue != SIP_CONTENT_TYPE_REMOTECC_RESPONSE_VALUE &&
+            pSipMessage->mesg_body[i].msgContentTypeValue != SIP_CONTENT_TYPE_CONFIGAPP_VALUE &&
+            pSipMessage->mesg_body[i].msgContentTypeValue != SIP_CONTENT_TYPE_PRESENCE_VALUE) {
+
+            if (pSipMessage->mesg_body[i].msgContentTypeValue == SIP_CONTENT_TYPE_CMXML_VALUE) {
+                // Body can not be parsed - send it up as it is
+                subDatap = (ccsip_event_data_t *) cpr_malloc(sizeof(ccsip_event_data_t));
+                if (subDatap == NULL) {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc of subDatap failed.\n",
+                                      fname);
+                    return (0);
+                }
+
+                subDatap->u.raw_data.data = pSipMessage->mesg_body[i].msgBody;
+                subDatap->u.raw_data.length = pSipMessage->mesg_body[i].msgLength;
+                pSipMessage->mesg_body[i].msgBody = NULL;
+                pSipMessage->mesg_body[i].msgLength = 0;
+                subDatap->type = EVENT_DATA_RAW;
+                subDatap->next = NULL;
+            } else {
+
+                /* There are other applications that has been handled like syncCheck
+                 */
+
+                scp = sippmh_parse_service_control_body(pSipMessage->mesg_body[i].msgBody,
+                                       pSipMessage->mesg_body[i].msgLength);
+
+                if (scp != NULL) {
+                    // Hand over the event to platform
+                    sip_platform_handle_service_control_notify(scp);
+
+                    sippmh_free_service_control_info(scp);
+
+                }
+
+                /* If this is the only body then have to send the response out as
+                 * no other application will be requesting to send response
+                 */
+                if (i== 0 && pSipMessage->mesg_body[i+1].msgBody == NULL) {
+
+                    if (sipSPISendErrorResponse(pSipMessage, 200, SIP_SUCCESS_SETUP_PHRASE,
+                                                    0, NULL, NULL) != TRUE) {
+                        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                          fname, SIP_SUCCESS_SETUP);
+                    }
+                    if (!reSubscribe) {
+                        free_scb(scb_index, fname);
+                    }
+                    return(0);
+                }
+            }
+
+        } else {
+            result = parse_body(scbp->hb.event_type, pSipMessage->mesg_body[i].msgBody,
+                                pSipMessage->mesg_body[i].msgLength, &subDatap, fname);
+            if (result == SIP_ERROR) {
+                if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                            SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                            SIP_WARN_MISC,
+                                            SIP_CLI_ERR_BAD_REQ_BAD_BODY_ENCODING, NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_BAD_REQ);
+                }
+                if (!reSubscribe) {
+                    free_scb(scb_index, fname);
+                }
+                free_event_data(subDatap);
+                free_event_data(subs_ind_data.u.subs_ind_data.eventData);
+                return SIP_ERROR;
+            }
+        }
+        if (subs_ind_data.u.subs_ind_data.eventData == NULL &&
+            subDatap != NULL) {
+            subs_ind_data.u.subs_ind_data.eventData = subDatap;
+            subDatap->next = NULL;
+        } else if (subs_ind_data.u.subs_ind_data.eventData != NULL &&
+            subDatap != NULL){
+            append_event_data(subs_ind_data.u.subs_ind_data.eventData,
+                              subDatap);
+        }
+        i++;
+    }
+
+    // Prepare subscription indication data
+    subs_ind_data.event = eventPackage;
+    subs_ind_data.u.subs_ind_data.expires = scbp->hb.expires;
+    subs_ind_data.u.subs_ind_data.from = scbp->sip_from;
+    subs_ind_data.u.subs_ind_data.to = scbp->sip_to;
+    subs_ind_data.u.subs_ind_data.line = scbp->hb.dn_line;
+    subs_ind_data.sub_duration = scbp->hb.expires;
+    subs_ind_data.sub_id = scbp->sub_id;
+    subs_ind_data.msg_id = scbp->subsIndCallbackMsgID;
+    subs_ind_data.gsm_id = scbp->gsm_id;
+    subs_ind_data.line_id = scbp->hb.dn_line;
+    subs_ind_data.norefersub = scbp->norefersub;
+    subs_ind_data.request_id = -1;
+
+    // Eventually let the subscribing application know
+    if (scbp->subsIndCallback) {
+        (scbp->subsIndCallback) (&subs_ind_data);
+    } else if ((scbp->subsIndCallbackTask != CC_SRC_MIN) && (scbp->subsIndCallbackMsgID != 0)) {
+        (void) sip_send_message(&subs_ind_data, scbp->subsIndCallbackTask,
+                                scbp->subsIndCallbackMsgID);
+    }
+
+    if (scbp->smState == SUBS_STATE_SENT_NOTIFY) {
+        scbp->smState = SUBS_STATE_RCVD_SUBSCRIBE_SENT_NOTIFY;
+    } else {
+        scbp->smState = SUBS_STATE_RCVD_SUBSCRIBE;
+    }
+    incomingSubscribes++;
+    if (!reSubscribe) {
+        incomingSubscriptions++;
+    }
+    return (0);
+}
+
+/**
+  * This function will decode the xml bodies.
+  *
+  * @param[in] event_type - event type.
+  * @param[in] pSipMessage - pointer to sipMessage_t
+  * @param[out] dataPP -  pointer to pointer to decoded data.
+  *
+  * @returns TRUE/FALSE
+  *
+  * @pre (pSipMessage != NULL) && (dataPP != NULL)
+  */
+static boolean
+decode_message_body (cc_subscriptions_t event_type, sipMessage_t *pSipMessage, ccsip_event_data_t **dataPP)
+{
+    const char     *fname = "decode_message_body";
+    uint8_t         i = 0;
+    ccsip_event_data_t *notDatap = NULL;
+    int             result;
+
+    // Decode the body, if any
+    while (i < HTTPISH_MAX_BODY_PARTS && pSipMessage->mesg_body[i].msgBody
+            != NULL) {
+        if (pSipMessage->mesg_body[0].msgContentTypeValue != SIP_CONTENT_TYPE_DIALOG_VALUE &&
+            pSipMessage->mesg_body[0].msgContentTypeValue != SIP_CONTENT_TYPE_KPML_REQUEST_VALUE &&
+            pSipMessage->mesg_body[0].msgContentTypeValue != SIP_CONTENT_TYPE_REMOTECC_REQUEST_VALUE &&
+            pSipMessage->mesg_body[0].msgContentTypeValue != SIP_CONTENT_TYPE_REMOTECC_RESPONSE_VALUE &&
+            pSipMessage->mesg_body[0].msgContentTypeValue != SIP_CONTENT_TYPE_PRESENCE_VALUE) {
+
+            // Body can not be parsed - send it up as it is
+            notDatap = (ccsip_event_data_t *) cpr_malloc(sizeof(ccsip_event_data_t));
+            if (notDatap == NULL) {
+                CCSIP_DEBUG_ERROR("%s: Error - malloc of notDatap failed.\n",
+                                  fname);
+                return FALSE;
+            }
+
+            notDatap->u.raw_data.data = pSipMessage->mesg_body[0].msgBody;
+            notDatap->u.raw_data.length = pSipMessage->mesg_body[0].msgLength;
+            pSipMessage->mesg_body[0].msgBody = NULL;
+            pSipMessage->mesg_body[0].msgLength = 0;
+            notDatap->type = EVENT_DATA_RAW;
+            notDatap->next = NULL;
+
+        } else {
+
+            result = parse_body(event_type, pSipMessage->mesg_body[i].msgBody,
+                                pSipMessage->mesg_body[i].msgLength, &notDatap, fname);
+            if (result == SIP_ERROR) {
+                free_event_data(notDatap);
+                free_event_data(*dataPP);
+                return FALSE;
+            }
+        }
+        if ((*dataPP) == NULL) {
+            (*dataPP) = notDatap;
+            notDatap->next = NULL;
+        } else {
+            append_event_data((*dataPP), notDatap);
+        }
+        i++;
+    }
+    return TRUE;
+}
+
+/********************************************************
+ * Handle Network Received Notify Request
+ ********************************************************/
+int
+subsmanager_handle_ev_sip_subscribe_notify (sipMessage_t *pSipMessage)
+{
+    const char     *fname = "subsmanager_handle_ev_sip_subscribe_notify";
+    cc_subscriptions_t eventPackage;
+    sipSCB_t       *scbp = NULL;
+    const char     *callID = NULL;
+    const char     *event = NULL;
+    int             scb_index;
+    sipCseq_t      *request_cseq_structure = NULL;
+    unsigned long   request_cseq_number = 0;
+    sipMethod_t     request_cseq_method = sipMethodInvalid;
+    ccsip_sub_not_data_t notify_ind_data;
+    int16_t         requestStatus = SIP_MESSAGING_ERROR;
+    const char     *from = NULL, *to = NULL;
+    const char     *subs_state = NULL;
+    boolean         subs_header_found = FALSE;
+    sipLocation_t  *to_loc = NULL;
+    const char     *via = NULL;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Processing a network generated NOTIFY\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+
+    memset(&notify_ind_data, 0, sizeof(notify_ind_data));
+    requestStatus = (uint16_t) sipSPICheckRequest(NULL, pSipMessage);
+    if (requestStatus != SIP_MESSAGING_OK) {
+        if (requestStatus == SIP_MESSAGING_DUPLICATE) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received duplicate request\n", fname);
+        } else {
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        SIP_WARN_MISC, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+        }
+        return SIP_ERROR;
+    }
+    // Get fields from NOTIFY
+    event = sippmh_get_header_val(pSipMessage, SIP_HEADER_EVENT,
+                                  SIP_C_HEADER_EVENT);
+    if (event) {
+        if (cpr_strcasecmp(event, SIP_EVENT_DIALOG) == 0) {
+            eventPackage = CC_SUBSCRIPTIONS_DIALOG;
+        } else if (cpr_strcasecmp(event, SIP_EVENT_KPML) == 0) {
+            eventPackage = CC_SUBSCRIPTIONS_KPML;
+        } else if (cpr_strcasecmp(event, SIP_EVENT_CONFIG) == 0) {
+            eventPackage = CC_SUBSCRIPTIONS_CONFIG;
+        } else if (cpr_strcasecmp(event, SIP_EVENT_PRESENCE) == 0) {
+            eventPackage = CC_SUBSCRIPTIONS_PRESENCE;
+        } else if ((cpr_strcasecmp(event, SIP_EVENT_REFER) == 0) &&
+            (pSipMessage->mesg_body[0].msgContentTypeValue == SIP_CONTENT_TYPE_DIALOG_VALUE)) {
+            eventPackage = CC_SUBSCRIPTIONS_DIALOG;
+        } else {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unsupported event=%s\n", fname, event);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_EVENT,
+                                        SIP_CLI_ERR_BAD_EVENT_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_EVENT);
+            }
+            return SIP_ERROR;
+        }
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Missing Event header\n", fname);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_EVENT,
+                                    SIP_CLI_ERR_BAD_EVENT_PHRASE,
+                                    0, NULL, NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_EVENT);
+        }
+        return SIP_ERROR;
+    }
+
+    callID = sippmh_get_cached_header_val(pSipMessage, CALLID);
+    if (!callID) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to obtain request's "
+                          "Call-ID header.\n", fname);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_CALLID_ABSENT,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        return SIP_ERROR;
+    }
+
+        // Find the proper SCB
+        scbp = find_scb_by_subscription(eventPackage, &scb_index, callID);
+        if (!scbp) {
+            /*
+             * check if it is an unsolicited dialog/presence event NOTIFY
+             * check if it has a to-tag. if so, then it is not considered unsolicited NOTIFY.
+             * Presence of to-tag could be because it is a final NOTIFY of a subscription which
+             * we have just terminated.
+             */
+            to = sippmh_get_cached_header_val(pSipMessage, TO);
+            to_loc = sippmh_parse_from_or_to((char *) to, TRUE);
+            if ((to_loc == NULL) || (to_loc->tag == NULL)) {
+                if (eventPackage == CC_SUBSCRIPTIONS_DIALOG) {
+                    notify_ind_data.line_id = 1;
+                    /* decode the body */
+                    if (decode_message_body(CC_SUBSCRIPTIONS_DIALOG, pSipMessage,
+                                            &(notify_ind_data.u.notify_ind_data.eventData)) == FALSE) {
+                        if (sipSPISendErrorResponse(pSipMessage, SIP_SERV_ERR_INTERNAL,
+                                                    SIP_SERV_ERR_INTERNAL_PHRASE,
+                                                    0, NULL, NULL) != TRUE) {
+                            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                              fname, SIP_CLI_ERR_BAD_REQ);
+                            return SIP_ERROR;
+                        }
+                    }
+                   if (sipSPISendErrorResponse(pSipMessage, SIP_STATUS_SUCCESS,
+                                                SIP_SUCCESS_SETUP_PHRASE,
+                                                0, NULL, NULL) != TRUE) {
+                        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                          fname, SIP_STATUS_SUCCESS);
+                    }
+
+                    incomingUnsolicitedNotifies++;
+                    sippmh_free_location(to_loc);
+                    return (0);
+                } else if (eventPackage == CC_SUBSCRIPTIONS_PRESENCE) {
+                    /* decode the body */
+                    if (decode_message_body(CC_SUBSCRIPTIONS_PRESENCE, pSipMessage,
+                                            &(notify_ind_data.u.notify_ind_data.eventData)) == FALSE) {
+                        if (sipSPISendErrorResponse(pSipMessage, SIP_SERV_ERR_INTERNAL,
+                                                    SIP_SERV_ERR_INTERNAL_PHRASE,
+                                                    0, NULL, NULL) != TRUE) {
+                            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                              fname, SIP_CLI_ERR_BAD_REQ);
+                            return SIP_ERROR;
+                        }
+                    }
+                    pres_unsolicited_notify_ind(&notify_ind_data);
+                    if (sipSPISendErrorResponse(pSipMessage, SIP_STATUS_SUCCESS,
+                                                SIP_SUCCESS_SETUP_PHRASE,
+                                                0, NULL, NULL) != TRUE) {
+                        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                          fname, SIP_STATUS_SUCCESS);
+                    }
+
+                    incomingUnsolicitedNotifies++;
+                    sippmh_free_location(to_loc);
+                    return (0);
+                }
+            }
+            sippmh_free_location(to_loc);
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No prior subscription", fname);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_CALLEG,
+                                        SIP_CLI_ERR_SUBS_DOES_NOT_EXIST_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_CALLEG);
+            }
+            return SIP_ERROR;
+        }
+
+
+    if (scbp->pendingClean) {
+        // Oops, the application has already terminated this subscription
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Notify received for subscription "
+                         "already terminated by application.\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+        if (sipSPISendErrorResponse(pSipMessage, SIP_SUCCESS_SETUP,
+                                    SIP_SUCCESS_SETUP_PHRASE,
+                                    0, NULL, NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_SUCCESS_SETUP);
+        }
+        return (0);
+    }
+    // Check SCB state
+    if (scbp->smState == SUBS_STATE_SENT_SUBSCRIBE) {
+        // Have just sent a SUBSCRIBE but have not received a response
+        // This could happen if the NOTIFY from the remote side comes in
+        // before its 2xx response to SUBSCRIBE does
+        // Should still process this as if received a 2xx response
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Bad subscription state: "
+                         "But still processing out-of-turn NOTIFY.\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+    }
+    // Copy stuff from the message into the SCB - the parts that we
+    // need to keep to respond to this request. This includes: Via&Branch
+    // (From and To & Tags, CallID are simply inverse of the ones in
+    // SUBSCRIBE), and CSeq
+
+    // Parse subscription state header
+    subs_state = sippmh_get_header_val(pSipMessage,
+                                       SIP_HEADER_SUBSCRIPTION_STATE,
+                                       SIP_HEADER_SUBSCRIPTION_STATE);
+    if (subs_state) {
+        sipSubscriptionStateInfo_t subsStateInfo;
+
+        memset(&subsStateInfo, 0, sizeof(sipSubscriptionStateInfo_t));
+        // Get the state, expires, retry-after and reason
+        if (sippmh_parse_subscription_state(&subsStateInfo, subs_state) == 0) {
+            // Store values in SCB
+            scbp->hb.expires = subsStateInfo.expires;
+            scbp->subscription_state = subsStateInfo.state;
+            scbp->retry_after = subsStateInfo.retry_after;
+            scbp->subscription_state_reason = subsStateInfo.reason;
+            subs_header_found = TRUE;
+        }
+    }
+    if (!subs_header_found) {
+        // Subscription-State header not present - reject
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to obtain or parse request's "
+                          "Subs-state header.\n", fname);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_NO_SUBSCRIPTION_HEADER,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        // Send message to the app and let it decide what it wants
+        // to do with a bad NOTIFY
+        sip_subsManager_send_protocol_error(scbp, scb_index, FALSE);
+        return SIP_ERROR;
+    }
+    // Parse CSEQ
+    if (getCSeqInfo(pSipMessage, &request_cseq_structure) == FALSE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to obtain or parse request's CSeq "
+                          "header.\n", fname);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_CSEQ_FIELD,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        // Send message to the app and let it decide what it wants
+        // to do with a bad NOTIFY
+        sip_subsManager_send_protocol_error(scbp, scb_index, FALSE);
+        return SIP_ERROR;
+    }
+
+    request_cseq_number = request_cseq_structure->number;
+    request_cseq_method = request_cseq_structure->method;
+    cpr_free(request_cseq_structure);
+
+    // Check continuity of CSeq numbers
+    if (request_cseq_number <= scbp->last_recv_request_cseq) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Out of order CSeq number received\n",
+                          fname);
+    } else {
+        scbp->last_recv_request_cseq = request_cseq_number;
+        scbp->last_recv_request_cseq_method = request_cseq_method;
+    }
+
+    // Parse and store Via Header
+    via = sippmh_get_cached_header_val(pSipMessage, VIA);
+    if (via) {
+        /* store the via header */
+        if (store_incoming_trxn(via, request_cseq_number,  scbp) == FALSE) {
+            (void) sipSPISendErrorResponse(pSipMessage,
+                                               SIP_SERV_ERR_INTERNAL,
+                                               SIP_SERV_ERR_INTERNAL_PHRASE,
+                                               0,
+                                               NULL,
+                                               NULL);
+            return SIP_ERROR;
+        }
+    }
+
+    // Update from/to headers - note this is reversed
+    // as we generated the initial SUBSCRIBE
+    to = sippmh_get_cached_header_val(pSipMessage, TO);
+    from = sippmh_get_cached_header_val(pSipMessage, FROM);
+
+    if (to) {
+        scbp->sip_from = strlib_update(scbp->sip_from, to);
+    }
+    if (from) {
+        scbp->sip_to = strlib_update(scbp->sip_to, from);
+    }
+
+    if ((scbp->smState == SUBS_STATE_SENT_SUBSCRIBE) ||
+        (scbp->smState == SUBS_STATE_SENT_SUBSCRIBE_RCVD_NOTIFY)) {
+        scbp->smState = SUBS_STATE_SENT_SUBSCRIBE_RCVD_NOTIFY;
+    } else {
+        scbp->smState = SUBS_STATE_RCVD_NOTIFY;
+    }
+
+    // Decode the body, if any
+    if (decode_message_body(eventPackage, pSipMessage, &(notify_ind_data.u.notify_ind_data.eventData)) == FALSE) {
+        if (sipSPISendErrorResponse(pSipMessage, SIP_SERV_ERR_INTERNAL,
+                                    SIP_SERV_ERR_INTERNAL_PHRASE,
+                                    0, NULL, NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        // Send message to the app and let it decide what it
+        // wants to do with a bad NOTIFY
+        sip_subsManager_send_protocol_error(scbp, scb_index, FALSE);
+        return SIP_ERROR;
+    }
+
+    // Fill out the return structure and call the callback routine
+    notify_ind_data.event = eventPackage;
+    notify_ind_data.sub_id = scbp->sub_id;
+    notify_ind_data.msg_id = scbp->notIndCallbackMsgID;
+    notify_ind_data.gsm_id = scbp->gsm_id;
+    notify_ind_data.line_id = scbp->hb.dn_line;
+    notify_ind_data.request_id = scbp->request_id;
+    notify_ind_data.u.notify_ind_data.subscription_state =
+        scbp->subscription_state;
+    notify_ind_data.u.notify_ind_data.expires = scbp->hb.expires;
+    notify_ind_data.u.notify_ind_data.retry_after = scbp->retry_after;
+    notify_ind_data.u.notify_ind_data.subscription_state_reason =
+        scbp->subscription_state_reason;
+    notify_ind_data.u.notify_ind_data.cseq = request_cseq_number;
+
+    if (scbp->notifyIndCallback) {
+        (scbp->notifyIndCallback) (&notify_ind_data);
+    } else if (scbp->subsNotCallbackTask != CC_SRC_MIN) {
+        (void) sip_send_message(&notify_ind_data, scbp->subsNotCallbackTask,
+                                scbp->notIndCallbackMsgID);
+    }
+
+    scbp->outstandingIncomingNotifyTrxns += 1;
+    incomingNotifies++;
+    return (0);
+}
+
+static sipRet_t
+sm_add_contact (ccsip_common_cb_t *cbp, sipMessage_t *msg)
+{
+
+    char    src_addr_str[MAX_IPADDR_STR_LEN];
+    uint8_t mac_address[MAC_ADDRESS_LENGTH];
+    char    contact[MAX_LINE_CONTACT_SIZE];
+    char    contact_str[MAX_SIP_URL_LENGTH];
+    size_t  escaped_char_str_len;
+    sipSCB_t *scbp = (sipSCB_t *)cbp;
+
+    if (!cbp || !msg) {
+        return HSTATUS_FAILURE;
+    }
+
+    ipaddr2dotted(src_addr_str, &cbp->src_addr);
+
+    if ((cbp->cb_type == SUBNOT_CB) && (scbp->useDeviceAddressing)) {
+        platform_get_wired_mac_address(mac_address);
+        snprintf(contact_str, MAX_SIP_URL_LENGTH, "<sip:%.4x%.4x%.4x@%s:%d>",
+                 mac_address[0] * 256 + mac_address[1],
+                 mac_address[2] * 256 + mac_address[3],
+                 mac_address[4] * 256 + mac_address[5],
+                 src_addr_str, scbp->hb.local_port);
+    } else {
+        snprintf(contact_str, 6, "<sip:");
+        contact[0] = '\0';
+        config_get_line_string(CFGID_LINE_CONTACT, contact,
+                               cbp->dn_line, sizeof(contact));
+        if ((cpr_strcasecmp(contact, UNPROVISIONED) == 0) ||
+            (contact[0] == '\0')) {
+            // pk-id has not been provisioned, use line name instead
+            config_get_line_string(CFGID_LINE_NAME, contact,
+                                   cbp->dn_line, sizeof(contact));
+        }
+        escaped_char_str_len = sippmh_convertURLCharToEscChar(contact,
+                                                              strlen(contact),
+                                                              contact_str + 5,
+                                                              (MAX_SIP_URL_LENGTH - 5),
+                                                              FALSE);
+        snprintf(contact_str + 5 + escaped_char_str_len,
+                 sizeof(contact_str) - 5 - escaped_char_str_len,
+                 "@%s:%d;transport=%s>", src_addr_str, cbp->local_port,
+                 sipTransportGetTransportType(cbp->dn_line, TRUE, NULL));
+    }
+
+    return sippmh_add_text_header(msg, SIP_HEADER_CONTACT, contact_str);
+}
+
+static sipRet_t
+sm_add_cseq (sipSCB_t *scbp, sipMethod_t method, sipMessage_t *msg)
+{
+    uint32_t cseq_number;
+
+    if (!scbp || !msg) {
+        return HSTATUS_FAILURE;
+    }
+    if (scbp->ccbp) {
+        // Use cseq from CCB
+        cseq_number = ++(scbp->ccbp->last_used_cseq);
+        /*
+         * Remember CSEQ for the request aside from ccb so that
+         * CSEQ can be match when response is received (can not.
+         * depend on last CSEQ stored on CCB since it can be
+         * changed).
+         */
+        scbp->last_sent_request_cseq = cseq_number;
+    } else if (scbp->last_sent_request_cseq == 0) {
+        cseq_number = scbp->last_sent_request_cseq = CCSIP_SUBS_START_CSEQ;
+    } else {
+        cseq_number = ++(scbp->last_sent_request_cseq);
+    }
+
+    return (sippmh_add_cseq(msg, sipGetMethodString(method), cseq_number));
+}
+
+static boolean
+sipSPIAddRouteHeadersToSubNot (sipMessage_t *msg, sipSCB_t * scbp,
+                               char *result_route, int result_route_length)
+{
+    const char     *fname = "sipSPIAddRouteHeadersToSubNot";
+    static char     route[MAX_SIP_HEADER_LENGTH * NUM_INITIAL_RECORD_ROUTE_BUFS];
+    static char     Contact[MAX_SIP_HEADER_LENGTH];
+    boolean         lr = FALSE;
+    sipRecordRoute_t *rr_info;
+
+    /* Check args */
+    if (!msg) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "msg");
+        return (FALSE);
+    }
+    if (!scbp) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_BADARGUMENT),
+                          fname, "scbp");
+        return (FALSE);
+    }
+
+    if (scbp->ccbp) {
+        rr_info = scbp->ccbp->record_route_info;
+    } else {
+        rr_info = scbp->record_route_info;
+    }
+    if (!rr_info) {
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Route info not available; will not"
+                            " add Route header.\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+        return (TRUE);
+    }
+
+    memset(route, 0, MAX_SIP_HEADER_LENGTH * NUM_INITIAL_RECORD_ROUTE_BUFS);
+    memset(Contact, 0, MAX_SIP_HEADER_LENGTH);
+
+    if (scbp->internal == FALSE) {  /* incoming subscription */
+        /*
+         * For Incoming dialog (UAS), Copy the RR headers as it is
+         * If Contact is present, append it at the end
+         */
+        if (sipSPIGenerateRouteHeaderUAS(rr_info, route, sizeof(route), &lr)
+                == FALSE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPIGenerateRouteHeaderUAS()");
+            return (FALSE);
+        }
+    } else {
+        /*
+         * For Outgoing dialog (UAC), Copy the RR headers in the reverse
+         * order. If Contact is present, append it at the end
+         */
+        if (sipSPIGenerateRouteHeaderUAC(rr_info, route, sizeof(route), &lr)
+                == FALSE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPIGenerateRouteHeaderUAC()");
+            return (FALSE);
+        }
+    }
+    /*
+     * If loose_routing is TRUE, then the contact header is NOT appended
+     * to the Routeset but is instead used in the Req-URI^M
+     */
+    if (!lr) {
+        Contact[0] = '\0';
+        if (sipSPIGenerateContactHeader(scbp->contact_info, Contact,
+                    sizeof(Contact)) == FALSE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipSPIGenerateContactHeader()");
+            return (FALSE);
+        }
+        /* Append Contact to the Route Header, if Contact is available */
+        if (Contact[0] != '\0') {
+            if (route[0] != '\0') {
+                sstrncat(route, ", ", sizeof(route) - strlen(route));
+            }
+            sstrncat(route, Contact, MIN((sizeof(route) - strlen(route)), sizeof(Contact)));
+        }
+    }
+
+    if (route[0] != '\0') {
+        if (STATUS_SUCCESS == sippmh_add_text_header(msg, SIP_HEADER_ROUTE,
+                    route)) {
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Adding route = %s\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname), route);
+            if (result_route) {
+                sstrncpy(result_route, route, result_route_length);
+            }
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sippmh_add_text_header(ROUTE)");
+            return (FALSE);
+        }
+    } else {
+        /* Having nothing in Route header is a legal case.
+         * This would happen when the Record-Route header has
+         * a single entry and Contact was NULL
+         */
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Not adding route \n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+    }
+
+    return (TRUE);
+}
+
+/**************************************************************
+ * Format and Send an application generated SUBSCRIBE
+ **************************************************************/
+boolean
+sipSPISendSubscribe (sipSCB_t *scbp, boolean renew, boolean authen)
+{
+    const char     *fname = "SIPSPISendSubscribe";
+    sipMessage_t   *request = NULL;
+    sipMethod_t     method = sipMethodInvalid;
+    sipRet_t        flag = STATUS_SUCCESS;
+    char            src_addr_str[MAX_IPADDR_STR_LEN];
+    char            dest_sip_addr_str[MAX_IPADDR_STR_LEN];
+    char            addr[MAX_IPADDR_STR_LEN];
+    char           *sip_from_temp, *sip_from_tag, *sip_to_temp;
+    char            via[SIP_MAX_VIA_LENGTH];
+    char            display_name[MAX_LINE_NAME_SIZE];
+    char            line_name[MAX_LINE_NAME_SIZE];
+    uint8_t         mac_address[MAC_ADDRESS_LENGTH];
+    static uint16_t count = 1;
+    int             timeout = 0, max_forwards_value = 70;
+#define CID_LENGTH 9
+    char            cid[CID_LENGTH];
+    char            tmp_header[MAX_SIP_URL_LENGTH + 2];
+    char           *remvtag = NULL;
+    char           *domainloc = NULL;
+    char            allow[MAX_SIP_HEADER_LENGTH];
+
+    /*
+     * This function builds and sends a SUBSCRIBE message.
+     * Specific subscription information including the encoded body, if any,
+     * is taken from the SCB pointer passed. The renew flag indicates that the
+     * SUBSCRIBE message is to be sent to renew a previous subscription
+     */
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST), fname, "SUBSCRIBE");
+
+    if (!scbp) {
+        return FALSE;
+    }
+    // Fill in source and destination addresses
+    // Destination address should be the current CCM that is serving us
+    // In the future, this part of the code will be using the tables provided
+    // by the connection manager
+
+    // First check if the application gave us any forwarding address
+    if (scbp->hb.dest_sip_addr.type == CPR_IP_ADDR_INVALID) {
+        sipTransportGetPrimServerAddress(scbp->hb.dn_line, addr);
+        dns_error_code = sipTransportGetServerAddrPort(addr,
+                                                       &scbp->hb.dest_sip_addr,
+                                                       (uint16_t *)&scbp->hb.dest_sip_port,
+                                                       &scbp->hb.SRVhandle,
+                                                       FALSE);
+        if (dns_error_code == 0) {
+            util_ntohl(&(scbp->hb.dest_sip_addr), &(scbp->hb.dest_sip_addr));
+        } else {
+            sipTransportGetServerIPAddr(&(scbp->hb.dest_sip_addr), scbp->hb.dn_line);
+        }
+        scbp->hb.dest_sip_port = ((dns_error_code == 0) &&
+                               (scbp->hb.dest_sip_port)) ?
+            ntohs((uint16_t) scbp->hb.dest_sip_port) : (sipTransportGetPrimServerPort(scbp->hb.dn_line));
+    }
+    ipaddr2dotted(src_addr_str, &scbp->hb.src_addr);
+    ipaddr2dotted(dest_sip_addr_str, &scbp->hb.dest_sip_addr);
+
+    // Get the MAC address once - since need it in several places
+    platform_get_wired_mac_address(mac_address);
+
+    // If there is a related dialog, use the From header + tag,
+    // To header + tag, and the call-id from that dialog
+    if (scbp->ccbp) {
+        if (scbp->ccbp->flags & INCOMING) {
+            // If the call was incoming - reverse the headers for an
+            // outgoing request
+            scbp->sip_to = strlib_copy(scbp->ccbp->sip_from);
+            scbp->sip_to_tag = strlib_copy(scbp->ccbp->sip_from_tag);
+            scbp->sip_from = strlib_copy(scbp->ccbp->sip_to);
+            scbp->sip_from_tag = strlib_copy(scbp->ccbp->sip_to_tag);
+        } else {
+            // Otherwise copy then in order
+            scbp->sip_from = strlib_copy(scbp->ccbp->sip_from);
+            scbp->sip_from_tag = strlib_copy(scbp->ccbp->sip_from_tag);
+            scbp->sip_to = strlib_copy(scbp->ccbp->sip_to);
+            scbp->sip_to_tag = strlib_copy(scbp->ccbp->sip_to_tag);
+        }
+
+        if (scbp->ccbp &&
+            (scbp->ccbp->state >= SIP_STATE_SENT_INVITE_CONNECTED)) {
+            sstrncpy(scbp->SubURI, scbp->ccbp->ReqURI, MAX_SIP_URL_LENGTH);
+        } else {
+            sstrncpy(scbp->SubURI, "sip:", MAX_SIP_URL_LENGTH);
+            domainloc = scbp->SubURI + strlen(scbp->SubURI);
+            sstrncpy(domainloc, dest_sip_addr_str,
+                     MAX_SIP_URL_LENGTH - (domainloc - (scbp->SubURI)));
+        }
+        sstrncpy(scbp->hb.sipCallID, scbp->ccbp->sipCallID, MAX_SIP_CALL_ID);
+    } else if (!renew) {
+        // Create the From header
+        sip_from_temp = strlib_open(scbp->sip_from, MAX_SIP_URL_LENGTH);
+        if (sip_from_temp) {
+            // Use the subscriberURI, if present
+            if (scbp->SubscriberURI[0] != '\0') {
+                if (scbp->hb.src_addr.type == CPR_IP_ADDR_IPV6) {
+                    snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, "<sip:%s@[%s]>",
+                             scbp->SubscriberURI, src_addr_str);
+                } else {
+                    snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, "<sip:%s@%s>",
+                             scbp->SubscriberURI, src_addr_str);
+                }
+            } else if (scbp->useDeviceAddressing) {
+                if (scbp->hb.src_addr.type == CPR_IP_ADDR_IPV6) {
+
+                    snprintf(sip_from_temp, MAX_SIP_URL_LENGTH,
+                             "<sip:%.4x%.4x%.4x@[%s]>",
+                             mac_address[0] * 256 + mac_address[1],
+                            mac_address[2] * 256 + mac_address[3],
+                            mac_address[4] * 256 + mac_address[5],
+                            src_addr_str);
+                } else {
+                    snprintf(sip_from_temp, MAX_SIP_URL_LENGTH,
+                             "<sip:%.4x%.4x%.4x@%s>",
+                             mac_address[0] * 256 + mac_address[1],
+                            mac_address[2] * 256 + mac_address[3],
+                            mac_address[4] * 256 + mac_address[5],
+                            src_addr_str);
+                }
+            } else {
+                sip_config_get_display_name(scbp->hb.dn_line, display_name,
+                                            sizeof(display_name));
+                config_get_line_string(CFGID_LINE_NAME, line_name,
+                                       scbp->hb.dn_line, sizeof(line_name));
+                if (scbp->hb.src_addr.type == CPR_IP_ADDR_IPV6) {
+
+                    snprintf(sip_from_temp, MAX_SIP_URL_LENGTH,
+                             "\"%s\" <sip:%s@[%s]>", display_name,
+                             line_name, src_addr_str);
+                } else {
+                    snprintf(sip_from_temp, MAX_SIP_URL_LENGTH,
+                             "\"%s\" <sip:%s@%s>", display_name,
+                             line_name, src_addr_str);
+                }
+            }
+
+            // Add tag to the From header (need to allocate and fill in scbp->sip_from_tag
+            sip_from_tag = strlib_open(scbp->sip_from_tag, MAX_SIP_URL_LENGTH);
+            if (sip_from_tag) {
+                sip_util_make_tag(sip_from_tag);
+                sstrncat(sip_from_temp, ";tag=",
+                        MAX_SIP_URL_LENGTH - strlen(sip_from_temp));
+                sstrncat(sip_from_temp, sip_from_tag,
+                        MAX_SIP_URL_LENGTH - strlen(sip_from_temp));
+            }
+            scbp->sip_from_tag = strlib_close(sip_from_tag);
+        }
+        scbp->sip_from = strlib_close(sip_from_temp);
+
+        // Create the Call-ID header - For now create own call-id
+        count++;
+
+        snprintf(scbp->hb.sipCallID, MAX_SIP_CALL_ID,
+                 "%.4x%.4x-%.4x%.4x-%.8x-%.8x@%s",
+                 mac_address[0] * 256 + mac_address[1],
+                 mac_address[2] * 256 + mac_address[3],
+                 mac_address[4] * 256 + mac_address[5], count,
+                 (unsigned int) cpr_rand(),
+                 (unsigned int) cpr_rand(),
+                 src_addr_str);
+
+        // Create the ReqURI
+        sstrncpy(scbp->SubURI, "sip:", MAX_SIP_URL_LENGTH);
+
+        /* Indialog requests should contain suburi only
+         */
+        sstrncat(scbp->SubURI, scbp->SubURIOriginal, MAX_SIP_URL_LENGTH - sizeof("sip:"));
+        domainloc = strchr(scbp->SubURI, '@');
+        if (domainloc == NULL) {
+            domainloc = scbp->SubURI + strlen(scbp->SubURI);
+            if ((domainloc - scbp->SubURI) < (MAX_SIP_URL_LENGTH - 1)) {
+                /* Do not include @ when there is no user part */
+                if (scbp->SubURIOriginal[0] != '\0') {
+                    *domainloc++ = '@';
+                }
+                sstrncpy(domainloc, dest_sip_addr_str,
+                         MAX_SIP_URL_LENGTH - (domainloc - (scbp->SubURI)));
+            }
+        }
+        // Create the To header
+        sip_to_temp = strlib_open(scbp->sip_to, MAX_SIP_URL_LENGTH);
+        if (sip_to_temp) {
+            snprintf(sip_to_temp, MAX_SIP_URL_LENGTH, "<%s>", scbp->SubURI);
+            scbp->sip_to = strlib_close(sip_to_temp);
+        }
+    }
+
+    method = sipMethodSubscribe;
+
+
+    scbp->last_sent_request_cseq_method = method;
+
+    // Get a blank SIP message template and fill it in
+    request = GET_SIP_MESSAGE();
+    if (!request) {
+        return FALSE;
+    }
+    // Can't use CreateRequest to fill in the message since not using CCB. So
+    // do the following:
+    // 1. Add request line (sipSPIAddRequestLine).
+    if (HSTATUS_SUCCESS != sippmh_add_request_line(request,
+                                                   sipGetMethodString(method),
+                                                   scbp->SubURI, SIP_VERSION)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Request line\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+    // 2. Add local Via (sipSPIAddLocalVia)
+    snprintf(via, sizeof(via), "SIP/2.0/%s %s:%d;%s=%s%.8x",
+             sipTransportGetTransportType(scbp->hb.dn_line, TRUE, NULL),
+             src_addr_str, scbp->hb.local_port, VIA_BRANCH,
+             VIA_BRANCH_START, (unsigned int) cpr_rand());
+    if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_VIA, via)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding VIA header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // 3. Add common headers (sipSPIAddCommonHeaders)
+    if ((HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_FROM, scbp->sip_from)) ||
+        (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_TO, scbp->sip_to)) ||
+        (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_CALLID, scbp->hb.sipCallID)) ||
+        (HSTATUS_SUCCESS != sipAddDateHeader(request)) ||
+        (HSTATUS_SUCCESS != sm_add_cseq(scbp, method, request))) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding either From, To, CallID, "
+                          "Date or Cseq header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+    // 4. Add text headers (sippmh_add_text_header)
+    (void) sippmh_add_text_header(request, SIP_HEADER_USER_AGENT,
+                                  sipHeaderUserAgent);
+
+    // 5. Add general headers (AddGeneralHeaders)
+    // Event header is not needed for Refer msg
+    if (method != sipMethodRefer) {
+        if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_EVENT,
+                                scbp->event_name)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Event header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    if (scbp->hb.accept_type == CC_SUBSCRIPTIONS_DIALOG) {
+        flag = sippmh_add_text_header(request, SIP_HEADER_ACCEPT,
+                                      dialogAcceptHeader);
+    } else if (scbp->hb.event_type == CC_SUBSCRIPTIONS_KPML) {
+        flag = sippmh_add_text_header(request, SIP_HEADER_ACCEPT,
+                                      kpmlResponseAcceptHeader);
+    } else if (scbp->hb.event_type == CC_SUBSCRIPTIONS_DIALOG) {
+        flag = sippmh_add_text_header(request, SIP_HEADER_ACCEPT,
+                                      dialogAcceptHeader);
+    } else if (scbp->hb.event_type == CC_SUBSCRIPTIONS_PRESENCE) {
+        flag = sippmh_add_text_header(request, SIP_HEADER_ACCEPT,
+                                      presenceAcceptHeader);
+    }
+    if (HSTATUS_SUCCESS != flag) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Accept header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    if (HSTATUS_SUCCESS != sippmh_add_int_header(request, SIP_HEADER_EXPIRES,
+                scbp->hb.expires)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Expires header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+    // Add max-forwards header
+    config_get_value(CFGID_SIP_MAX_FORWARDS, &max_forwards_value,
+                     sizeof(max_forwards_value));
+    if (HSTATUS_SUCCESS !=
+        sippmh_add_int_header(request, SIP_HEADER_MAX_FORWARDS,
+            max_forwards_value)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Max-Forwards header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+    // Add Contact header
+    if (HSTATUS_SUCCESS != sm_add_contact(&(scbp->hb), request)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Contact header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    if (authen) {
+        if (HSTATUS_SUCCESS != sippmh_add_text_header(request, AUTHOR_HDR(scbp->hb.authen.status_code),
+                                                      scbp->hb.authen.authorization)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Authorization header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    if (scbp->norefersub) {
+        if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_REQUIRE, "norefersub")) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Require header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    if (method == sipMethodRefer) {
+        // Add Refer-To, Referred-By, and Content-ID headers
+        sstrncpy(tmp_header, (const char *) (scbp->sip_from), MAX_SIP_URL_LENGTH + 1);
+        remvtag = strstr(tmp_header, ";tag=");
+        if (remvtag) {
+            *remvtag = '\0';
+        }
+        if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_REFERRED_BY, tmp_header)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Referred-By header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+
+        snprintf(cid, sizeof(cid), "%.8x", (unsigned int)cpr_rand());
+        snprintf(tmp_header, sizeof(tmp_header), "cid:%s@%s", cid, src_addr_str);
+        if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_REFER_TO, tmp_header)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Refer-To header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+
+        snprintf(tmp_header, sizeof(tmp_header), "<%s@%s>", cid, src_addr_str);
+        if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_CONTENT_ID, tmp_header)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Content-ID header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    // Add Allow
+    snprintf(allow, MAX_SIP_HEADER_LENGTH, "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
+             SIP_METHOD_ACK, SIP_METHOD_BYE, SIP_METHOD_CANCEL,
+             SIP_METHOD_INVITE, SIP_METHOD_NOTIFY, SIP_METHOD_OPTIONS,
+             SIP_METHOD_REFER, SIP_METHOD_REGISTER, SIP_METHOD_UPDATE,
+             SIP_METHOD_SUBSCRIBE);
+    if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_ALLOW, allow)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Allow header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    /*
+     * Add route header if needed.
+     */
+    if (FALSE == sipSPIAddRouteHeadersToSubNot(request, scbp, NULL, 0)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Route header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+    // Add content, if any
+    if (scbp->hb.event_data_p) {
+        if (add_content(scbp->hb.event_data_p, request, fname) == FALSE) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Content\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    } else {
+        if (HSTATUS_SUCCESS != sippmh_add_int_header(request, SIP_HEADER_CONTENT_LENGTH, 0)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Content-Len\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    ccsip_common_util_set_retry_settings((ccsip_common_cb_t *)scbp, &timeout);
+    if (sipTransportCreateSendMessage(NULL, request, method,
+                                      &(scbp->hb.dest_sip_addr),
+                                      (int16_t) scbp->hb.dest_sip_port,
+                                      FALSE, TRUE, timeout, scbp,
+                                      RELDEV_NO_STORED_MSG) < 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send message\n", fname);
+        return (FALSE);
+    }
+
+    return (TRUE);
+}
+
+/**************************************************************
+ * Format and send a response to a network received SUBSCRIBE
+ **************************************************************/
+boolean
+sipSPISendSubscribeNotifyResponse (sipSCB_t *scbp,
+                                   uint16_t response_code,
+                                   uint32_t cseq)
+{
+    const char     *fname = "sipSPISendSubscribeNotifyResponse";
+    sipMessage_t   *response = NULL;
+    sipVia_t       *via = NULL;
+    sub_not_trxn_t *trxn_p;
+    int            status_code = 0;
+
+    // currently not used  const char *request_callid = NULL;
+    cpr_ip_addr_t   cc_remote_ipaddr;
+    uint16_t        cc_remote_port = 0;
+
+    // currently not used  int timeout = 0;
+    char           *pViaHeaderStr = NULL;
+    char           *dest_ip_addr_str = 0;
+    char            src_addr_str[MAX_IPADDR_STR_LEN];
+    char            response_text[MAX_SIP_URL_LENGTH];
+    boolean         port_present = FALSE;
+    int             reldev_stored_msg;
+
+    sipRet_t        tflag = HSTATUS_SUCCESS;
+
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_RESPONSE), fname, response_code);
+
+    memset(&cc_remote_ipaddr, 0, sizeof(cpr_ip_addr_t));
+    cc_remote_ipaddr = ip_addr_invalid;
+    if (!scbp) {
+        return FALSE;
+    }
+
+    /*
+     * In the response to Subscribe:
+     * The Via header gets a ";received" addition (...;received: 192.168.0.7)
+     * The From header is the same as in the subscribe request
+     * The To header is the same as in the subscribe request but has a tag
+     * The call-id is the same as in the request
+     * Content Length is set to 0
+     */
+    response = GET_SIP_MESSAGE();
+    if (!response) {
+        return FALSE;
+    }
+
+    get_sip_error_string(response_text, response_code);
+
+    tflag = sippmh_add_response_line(response, SIP_VERSION, response_code,
+                                     response_text);
+    if (tflag != HSTATUS_SUCCESS) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding response line", fname);
+        free_sip_message(response);
+        return FALSE;
+    }
+
+    /*
+     * find the transaction and fetch the via header
+     */
+    trxn_p = sll_find(scbp->incoming_trxns, &cseq);
+    if (trxn_p) {
+        pViaHeaderStr = trxn_p->via;
+        /* remove the transaction  because we are done with it */
+        (void)sll_remove(scbp->incoming_trxns, trxn_p);
+        cpr_free(trxn_p);
+    }
+    if (pViaHeaderStr) {
+        via = sippmh_parse_via(pViaHeaderStr);
+    }
+    if (!via || !pViaHeaderStr) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "No or Bad Via Header present in Message!");
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in parsing Via header", fname);
+        if (via)
+            sippmh_free_via(via);
+        free_sip_message(response);
+        cpr_free(pViaHeaderStr);
+        return (FALSE);
+    }
+    if (STATUS_SUCCESS != sippmh_add_text_header(response, SIP_HEADER_VIA, pViaHeaderStr)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Via header", fname);
+        sippmh_free_via(via);
+        free_sip_message(response);
+        cpr_free(pViaHeaderStr);
+        return (FALSE);
+    }
+    /* free the via header because we will not need it anymore */
+    cpr_free(pViaHeaderStr);
+
+    // Add common headers: From, To, CallID, Date, CSeq, Contact
+    // Note: If this is a response to a NOTIFY, then from and to headers
+    // must be reversed
+    if (scbp->last_recv_request_cseq_method == sipMethodNotify) {
+        if (STATUS_SUCCESS != sippmh_add_text_header(response, SIP_HEADER_FROM, scbp->sip_to)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding From header", fname);
+            sippmh_free_via(via);
+            free_sip_message(response);
+            return (FALSE);
+        }
+
+        if (STATUS_SUCCESS != sippmh_add_text_header(response, SIP_HEADER_TO, scbp->sip_from)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding To header", fname);
+            sippmh_free_via(via);
+            free_sip_message(response);
+            return (FALSE);
+        }
+    } else {
+        if (STATUS_SUCCESS != sippmh_add_text_header(response, SIP_HEADER_FROM, scbp->sip_from)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding From header", fname);
+            sippmh_free_via(via);
+            free_sip_message(response);
+            return (FALSE);
+        }
+
+        if (STATUS_SUCCESS != sippmh_add_text_header(response, SIP_HEADER_TO, scbp->sip_to)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding To header", fname);
+            sippmh_free_via(via);
+            free_sip_message(response);
+            return (FALSE);
+        }
+    }
+
+    if (STATUS_SUCCESS != sippmh_add_text_header(response, SIP_HEADER_CALLID, scbp->hb.sipCallID)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding CallID header", fname);
+        sippmh_free_via(via);
+        free_sip_message(response);
+        return (FALSE);
+    }
+
+    if (HSTATUS_SUCCESS != sipAddDateHeader(response)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Date header", fname);
+        sippmh_free_via(via);
+        free_sip_message(response);
+        return (FALSE);
+    }
+
+        if (HSTATUS_SUCCESS != sippmh_add_cseq(response,
+                                               sipGetMethodString(scbp->
+                                               last_recv_request_cseq_method),
+                                               cseq)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding CSeq header", fname);
+            sippmh_free_via(via);
+            free_sip_message(response);
+            return (FALSE);
+        }
+
+    tflag = sippmh_add_text_header(response, SIP_HEADER_SERVER, sipHeaderServer);
+    if (tflag != HSTATUS_SUCCESS) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Server header", fname);
+        sippmh_free_via(via);
+        free_sip_message(response);
+        return (FALSE);
+    }
+    ipaddr2dotted(src_addr_str, &scbp->hb.src_addr);
+
+    tflag = sm_add_contact(&(scbp->hb), response);
+
+    if (tflag != HSTATUS_SUCCESS) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Contact header", fname);
+        sippmh_free_via(via);
+        free_sip_message(response);
+        return (FALSE);
+    }
+    // Add expires header - only for a SUBSCRIBE response
+    if (scbp->last_recv_request_cseq_method == sipMethodSubscribe) {
+        if (sippmh_add_int_header(response, SIP_HEADER_EXPIRES, scbp->hb.expires) != 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Expires header", fname);
+            sippmh_free_via(via);
+            free_sip_message(response);
+            return (FALSE);
+        }
+        /*
+         * add Record-Route Header, if it is a response (18x/2xx) to
+         * dialog initiating SUBSCRIBE.
+         */
+        if ((scbp->cached_record_route[0] != '\0') &&
+            (((response_code >= 180) && (response_code <= 189)) ||
+             ((response_code >= 200) && (response_code <= 299)))) {
+            tflag = sippmh_add_text_header(response, SIP_HEADER_RECORD_ROUTE,
+                                           scbp->cached_record_route);
+            if (tflag != HSTATUS_SUCCESS) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Record-Route header",
+                                  fname);
+                sippmh_free_via(via);
+                free_sip_message(response);
+                return (FALSE);
+            }
+        }
+        /* free the cached record_route, once the final resp is sent */
+        if (response_code >= 200) {
+            strlib_free(scbp->cached_record_route);
+            scbp->cached_record_route = strlib_empty();
+        }
+    }
+    // Add other headers such as content-length, content-type
+    if (HSTATUS_SUCCESS != sippmh_add_int_header(response, SIP_HEADER_CONTENT_LENGTH, 0)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Content-Length header", fname);
+        sippmh_free_via(via);
+        free_sip_message(response);
+        return (FALSE);
+    }
+    // Send Response
+    // Get information on where to send the response from via headers
+    if (via->remote_port) {
+        cc_remote_port = via->remote_port;
+        port_present = TRUE;
+    } else {
+        /* Use default 5060 if via does not have port */
+        cc_remote_port = SIP_WELL_KNOWN_PORT;
+    }
+
+    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Via host: %s, port: %d, rcvd host: %s\n",
+                      fname,  via->host, via->remote_port, via->recd_host);
+
+    if (via->maddr) {
+        if (!port_present) {
+            dns_error_code = sipTransportGetServerAddrPort(via->maddr,
+                                                           &cc_remote_ipaddr,
+                                                           &cc_remote_port,
+                                                           NULL, FALSE);
+        } else {
+            dns_error_code = dnsGetHostByName(via->maddr, &cc_remote_ipaddr,
+                                               100, 1);
+        }
+
+        if (dns_error_code != 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname,
+                              "sipTransportGetServerAddrPort or dnsGetHostByName()");
+        } else {
+            util_ntohl(&cc_remote_ipaddr, &cc_remote_ipaddr);
+        }
+    }
+    // If all else fails send the response to where we got the request from
+    if (cc_remote_ipaddr.type == CPR_IP_ADDR_INVALID) {
+        if (via->recd_host) {
+            dest_ip_addr_str = via->recd_host;
+        } else {
+            dest_ip_addr_str = via->host;
+        }
+
+        if (!port_present) {
+            dns_error_code = sipTransportGetServerAddrPort(dest_ip_addr_str,
+                                                           &cc_remote_ipaddr,
+                                                           &cc_remote_port,
+                                                           NULL, FALSE);
+        } else {
+            dns_error_code = dnsGetHostByName(dest_ip_addr_str,
+                                               &cc_remote_ipaddr, 100, 1);
+        }
+
+        if (dns_error_code != 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname,
+                              "sipTransportGetServerAddrPort or dnsGetHostByName()");
+            // cpr_free(request_cseq_structure);
+            sippmh_free_via(via);
+            free_sip_message(response);
+            return (FALSE);
+        } else {
+            util_ntohl(&cc_remote_ipaddr, &cc_remote_ipaddr);
+        }
+    }
+    // Store cc_remote_ipaddr and cc_remote_port values in SCB for later use
+    // in the corresponding NOTIFY, if any
+    scbp->hb.dest_sip_addr = cc_remote_ipaddr;
+    scbp->hb.dest_sip_port = cc_remote_port;
+
+    reldev_stored_msg = sipRelDevCoupledMessageStore(response, scbp->hb.sipCallID,
+                                                     cseq,
+                                                     scbp->last_recv_request_cseq_method,
+                                                     FALSE,
+                                                     status_code,
+                                                     &cc_remote_ipaddr,
+                                                     cc_remote_port, TRUE);
+
+    sippmh_free_via(via);
+
+    scbp->hb.retx_flag = FALSE;
+    if (sipTransportCreateSendMessage(NULL, response, sipMethodSubscribe,
+                                      &cc_remote_ipaddr, cc_remote_port,
+                                      FALSE, TRUE, 0, scbp, reldev_stored_msg) != 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send message", fname);
+        return (FALSE);
+    }
+
+    return (TRUE);
+}
+
+/**************************************************************
+ * Format and send a locally generated NOTIFY
+ **************************************************************/
+boolean
+sipSPISendSubNotify (ccsip_common_cb_t *cbp, boolean authen)
+{
+    const char     *fname = "SIPSPISendSubNotify";
+    sipMessage_t   *request = NULL;
+    static uint32_t   cseq = 0;
+
+    // currently not used  char *message_body = NULL;
+    // currently not used  sipRet_t flag = STATUS_SUCCESS;
+    char            src_addr_str[MAX_IPADDR_STR_LEN];
+    char            dest_sip_addr_str[MAX_IPADDR_STR_LEN];
+    char            addr[MAX_IPADDR_STR_LEN];
+
+    // currently not used  uint32_t nbytes = SIP_UDP_MESSAGE_SIZE;
+    char            via[SIP_MAX_VIA_LENGTH];
+
+    // currently not used  short send_to_proxy_handle = INVALID_SOCKET;
+    static char     ReqURI[MAX_SIP_URL_LENGTH];
+    static char     sip_temp_str[MAX_SIP_URL_LENGTH];
+    static char     sip_temp_tag[MAX_SIP_URL_LENGTH];
+    sipSubscriptionStateInfo_t subs_state;
+    int             timeout = 0, max_forwards_value = 70;
+    char            allow[MAX_SIP_HEADER_LENGTH];
+    sipSCB_t *scbp = (sipSCB_t *)cbp;
+    sipTCB_t *tcbp = (sipTCB_t *)cbp;
+
+    /*
+     * This function builds and sends a NOTIFY message as a follow up to
+     * a previously received SUBSCRIBE
+     */
+    CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_MSG_SENDING_REQUEST), fname, "NOTIFY");
+
+    if (!cbp) {
+        return FALSE;
+    }
+
+    if (util_check_if_ip_valid(&cbp->dest_sip_addr)== FALSE) {
+        sipTransportGetPrimServerAddress(scbp->hb.dn_line, addr);
+
+        dns_error_code = sipTransportGetServerAddrPort(addr,
+                                                       &cbp->dest_sip_addr,
+                                                       (uint16_t *)&cbp->dest_sip_port,
+                                                       &cbp->SRVhandle,
+                                                       FALSE);
+
+        if (dns_error_code == 0) {
+            util_ntohl(&(cbp->dest_sip_addr), &(cbp->dest_sip_addr));
+        } else {
+            sipTransportGetServerIPAddr(&(cbp->dest_sip_addr), cbp->dn_line);
+        }
+
+        cbp->dest_sip_port = ((dns_error_code == 0) &&
+                               (cbp->dest_sip_port)) ?
+            ntohs(cbp->dest_sip_port) : (sipTransportGetPrimServerPort(cbp->dn_line));
+
+    }
+
+    ipaddr2dotted(src_addr_str, &cbp->src_addr);
+    ipaddr2dotted(dest_sip_addr_str, &cbp->dest_sip_addr);
+
+    // Create the request
+    request = GET_SIP_MESSAGE();
+
+    /*
+     * Determine the Request-URI
+     */
+    memset(ReqURI, 0, sizeof(ReqURI));
+
+    if (cbp->cb_type == SUBNOT_CB) {
+        if (scbp->contact_info) {
+            // Build Req-URI from contact info
+            sipContact_t *response_contact_info;
+            sipUrl_t *sipUrl = NULL;
+            cpr_ip_addr_t request_uri_addr;
+            uint16_t request_uri_port = 0;
+
+            request_uri_addr = ip_addr_invalid;
+
+            response_contact_info = scbp->contact_info;
+            if (response_contact_info->locations[0]->genUrl->schema == URL_TYPE_SIP) {
+                sipUrl = response_contact_info->locations[0]->genUrl->u.sipUrl;
+            } else {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"URL is not SIP\n", fname);
+                free_sip_message(request);
+                return (FALSE);
+            }
+
+            request_uri_port = sipUrl->port;
+            dns_error_code = sipTransportGetServerAddrPort(sipSPIUrlDestination(sipUrl),
+                                                           &request_uri_addr,
+                                                           &request_uri_port, NULL, FALSE);
+            if (dns_error_code == 0) {
+                util_ntohl(&request_uri_addr, &request_uri_addr);
+            } else {
+                request_uri_addr = ip_addr_invalid;
+            }
+
+            if (sipUrl->user != NULL) {
+                if (sipUrl->password) {
+                    snprintf(ReqURI, sizeof(ReqURI),
+                             sipUrl->is_phone ?
+                             "sip:%s:%s@%s:%d;user=phone" :
+                             "sip:%s:%s@%s:%d",
+                             sipUrl->user, sipUrl->password,
+                             sipUrl->host, sipUrl->port);
+                } else {
+                    snprintf(ReqURI, sizeof(ReqURI),
+                             sipUrl->is_phone ?
+                             "sip:%s@%s:%d;user=phone" :
+                             "sip:%s@%s:%d",
+                             sipUrl->user, sipUrl->host,
+                             sipUrl->port);
+                }
+            } else {
+                snprintf(ReqURI, sizeof(ReqURI),
+                         sipUrl->is_phone ?
+                         "sip:%s:%d;user=phone" :
+                         "sip:%s:%d",
+                         sipUrl->host, sipUrl->port);
+            }
+        } else {
+            // Build Req-URI from FROM field
+            sipLocation_t  *request_uri_loc = NULL;
+            char            sip_from[MAX_SIP_URL_LENGTH];
+            sipUrl_t       *request_uri_url = NULL;
+
+            sstrncpy(sip_from, scbp->sip_from, MAX_SIP_URL_LENGTH);
+            request_uri_loc = sippmh_parse_from_or_to(sip_from, TRUE);
+            if (!request_uri_loc) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "sippmh_parse_from_or_to(TO)");
+                free_sip_message(request);
+                return (FALSE);
+            }
+
+            if (!sippmh_valid_url(request_uri_loc->genUrl)) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "sippmh_valid_url()");
+                sippmh_free_location(request_uri_loc);
+                free_sip_message(request);
+                return (FALSE);
+            }
+
+            CCSIP_DEBUG_STATE(DEB_F_PREFIX"Forming Req-URI (Caller): using original "
+                              "Req-URI\n", DEB_F_PREFIX_ARGS(SIP_SUB, fname));
+            /*
+             * if (request_uri_loc->name) {
+             * if (request_uri_loc->name[0]) {
+             * sstrncat(ReqURI, "\"", sizeof(ReqURI)-strlen(ReqURI));
+             * sstrncat(ReqURI, request_uri_loc->name,
+             * sizeof(ReqURI)-strlen(ReqURI));
+             * sstrncat(ReqURI, "\" ", sizeof(ReqURI)-strlen(ReqURI));
+             * }
+             * }
+             */
+            sstrncat(ReqURI, "sip:", sizeof(ReqURI) - strlen(ReqURI));
+            if (request_uri_loc->genUrl->schema == URL_TYPE_SIP) {
+                request_uri_url = request_uri_loc->genUrl->u.sipUrl;
+            } else {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"URL is not SIP\n", fname);
+                sippmh_free_location(request_uri_loc);
+                free_sip_message(request);
+                return (FALSE);
+            }
+
+            if (request_uri_url->user) {
+                sstrncat(ReqURI, request_uri_url->user,
+                        sizeof(ReqURI) - strlen(ReqURI));
+                sstrncat(ReqURI, "@", sizeof(ReqURI) - strlen(ReqURI));
+            }
+            if (request_uri_url->is_phone) {
+                sstrncat(ReqURI, ";user=phone",
+                        sizeof(ReqURI) - strlen(ReqURI));
+            }
+            sstrncat(ReqURI, request_uri_url->host,
+                    sizeof(ReqURI) - strlen(ReqURI));
+            // sstrncat(ReqURI, ">", sizeof(ReqURI)-strlen(ReqURI));
+            sippmh_free_location(request_uri_loc);
+        }
+    } else { //Unsolicited NOTIFY
+        char               line_name[MAX_LINE_NAME_SIZE];
+        config_get_line_string(CFGID_LINE_NAME, line_name, cbp->dn_line, sizeof(line_name));
+        snprintf(ReqURI, MAX_SIP_URL_LENGTH, "sip:%s@%s", line_name, dest_sip_addr_str);
+        sstrncpy(tcbp->full_ruri, ReqURI, MAX_SIP_URL_LENGTH);
+    }
+    (void) sippmh_add_request_line(request,
+                                   sipGetMethodString(sipMethodNotify),
+                                   ReqURI, SIP_VERSION);
+
+    // The Via header has the local URI and a new branch
+    snprintf(via, sizeof(via), "SIP/2.0/%s %s:%d;%s=%s%.8x",
+             sipTransportGetTransportType(cbp->dn_line, TRUE, NULL),
+             src_addr_str, cbp->local_port, VIA_BRANCH,
+             VIA_BRANCH_START, (unsigned int) cpr_rand());
+
+    if (STATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_VIA, via)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding VIA header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // The To field including the tag is the same as the From field in the
+    // response to SUBSCRIBE
+    if (cbp->cb_type != SUBNOT_CB) {
+        snprintf(sip_temp_str, MAX_SIP_URL_LENGTH, "<%s>", ReqURI);
+    }
+    if (STATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_TO,
+                                                 ((cbp->cb_type == SUBNOT_CB) ? scbp->sip_from : sip_temp_str))) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding To header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    // The From field including the tag is the same as the To field in the
+    // response to SUBSCRIBE
+    if (cbp->cb_type != SUBNOT_CB) {
+        sstrncat(sip_temp_str, ";tag=", MAX_SIP_URL_LENGTH - strlen(sip_temp_str));
+        sip_util_make_tag(sip_temp_tag);
+        sstrncat(sip_temp_str, sip_temp_tag, MAX_SIP_URL_LENGTH - strlen(sip_temp_str));
+    }
+    if (STATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_FROM,
+                                                 ((cbp->cb_type == SUBNOT_CB) ?  scbp->sip_to : sip_temp_str))) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding From header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+    // The call-id used is the same as in the SCB
+    if (cbp->sipCallID[0] == 0) {
+        snprintf(tcbp->hb.sipCallID, sizeof(tcbp->hb.sipCallID), "%.8x-%.8x@%s", // was MAX_SIP_URL_LENGTH
+                 (unsigned int) cpr_rand(),
+                 (unsigned int) cpr_rand(),
+                 src_addr_str);
+    }
+    if (STATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_CALLID, cbp->sipCallID)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding CallID header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    if (HSTATUS_SUCCESS != sipAddDateHeader(request)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Date header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    if (cbp->cb_type == SUBNOT_CB) {
+        if (sm_add_cseq(scbp, sipMethodNotify, request) != HSTATUS_SUCCESS) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding CSeq header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    } else {
+        cseq++;
+        if (cseq == 0) {
+            cseq = 1;
+        }
+        if (HSTATUS_SUCCESS != sippmh_add_cseq(request, sipGetMethodString(sipMethodNotify), cseq)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding CSEQ header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    // Event is the event name
+    // The subscription-state header is active
+    if (cbp->event_type - CC_SUBSCRIPTIONS_DIALOG > -1 &&
+        cbp->event_type - CC_SUBSCRIPTIONS_DIALOG < 5) {
+        if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_EVENT,
+                                                      eventNames[cbp->event_type - CC_SUBSCRIPTIONS_DIALOG])) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Event header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+    }
+
+    subs_state.expires = cbp->expires;
+    if (cbp->cb_type == SUBNOT_CB) {
+        if (subs_state.expires > 0) {
+            subs_state.state = SUBSCRIPTION_STATE_ACTIVE;
+        } else {
+            subs_state.state = SUBSCRIPTION_STATE_TERMINATED;
+        }
+    }  else {
+        subs_state.state = SUBSCRIPTION_STATE_ACTIVE;
+    }
+    if (sippmh_add_subscription_state(request, &subs_state) != 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Subscription-State header", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+    // Add max-forwards header
+    config_get_value(CFGID_SIP_MAX_FORWARDS, &max_forwards_value,
+                     sizeof(max_forwards_value));
+    (void) sippmh_add_int_header(request, SIP_HEADER_MAX_FORWARDS,
+                                 max_forwards_value);
+
+    // Add contact header
+    if (sm_add_contact(cbp, request) != HSTATUS_SUCCESS) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Contact header\n", fname);
+        free_sip_message(request);
+        return (FALSE);
+    }
+
+    if (authen) {
+        (void) sippmh_add_text_header(request,
+                                      AUTHOR_HDR(scbp->hb.authen.status_code),
+                                      scbp->hb.authen.authorization);
+    }
+    // Add Allow
+    snprintf(allow, MAX_SIP_HEADER_LENGTH, "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
+             SIP_METHOD_ACK, SIP_METHOD_BYE, SIP_METHOD_CANCEL,
+             SIP_METHOD_INVITE, SIP_METHOD_NOTIFY, SIP_METHOD_OPTIONS,
+             SIP_METHOD_REFER, SIP_METHOD_REGISTER, SIP_METHOD_UPDATE,
+             SIP_METHOD_SUBSCRIBE);
+    (void) sippmh_add_text_header(request, SIP_HEADER_ALLOW, allow);
+
+    if (cbp->cb_type == SUBNOT_CB) {
+        /* keep the request method to be sent */
+        scbp->last_sent_request_cseq_method = sipMethodNotify;
+        /*
+         * Add route header if needed.
+         */
+        if (FALSE == sipSPIAddRouteHeadersToSubNot(request, scbp, NULL, 0)) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Route header\n", fname);
+            free_sip_message(request);
+            return (FALSE);
+        }
+        ccsip_common_util_set_retry_settings((ccsip_common_cb_t *)scbp, &timeout);
+    }
+
+    // Add content, if any
+    if (cbp->event_data_p) {
+        if (add_content(cbp->event_data_p, request, fname) == FALSE) {
+            free_sip_message(request);
+            return (FALSE);
+        }
+    } else {
+        (void) sippmh_add_int_header(request, SIP_HEADER_CONTENT_LENGTH, 0);
+    }
+
+    if (sipTransportCreateSendMessage(NULL, request, sipMethodNotify,
+                                      &(scbp->hb.dest_sip_addr),
+                                      (int16_t) scbp->hb.dest_sip_port,
+                                      FALSE, TRUE, timeout, cbp,
+                                      RELDEV_NO_STORED_MSG) != 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in sending message\n", fname);
+        return (FALSE);
+    }
+
+    return (TRUE);
+}
+
+/**************************************************************
+ * Handle message retries
+ *************************************************************/
+int
+subsmanager_handle_retry_timer_expire (int scb_index)
+{
+    const char     *fname = "subsmanager_handle_retry_timer_expire";
+    sipSCB_t       *scbp = NULL;
+    uint32_t        max_retx = 0;
+    ccsip_sub_not_data_t sub_not_result_data;
+    uint32_t        time_t1 = 0;
+    uint32_t        time_t2 = 0;
+    uint32_t        timeout = 0;
+
+    CCSIP_DEBUG_TASK("Entering %s. scb_index: %d\n", fname, scb_index);
+
+    if (scb_index < 0 || scb_index >= MAX_SCBS) {
+        return (-1);
+    }
+    scbp = &(subsManagerSCBS[scb_index]);
+
+    if (scbp->hb.retx_flag == TRUE) {
+        config_get_value(CFGID_SIP_RETX, &max_retx, sizeof(max_retx));
+        if (max_retx > MAX_NON_INVITE_RETRY_ATTEMPTS) {
+            max_retx = MAX_NON_INVITE_RETRY_ATTEMPTS;
+        }
+        if (scbp->hb.retx_counter < max_retx) {
+            config_get_value(CFGID_TIMER_T1, &time_t1, sizeof(time_t1));
+            scbp->hb.retx_counter++;
+            timeout = time_t1 * (1 << scbp->hb.retx_counter);
+            config_get_value(CFGID_TIMER_T2, &time_t2, sizeof(time_t2));
+            if (timeout > time_t2) {
+                timeout = time_t2;
+            }
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Resending message #%d\n",
+                             DEB_F_PREFIX_ARGS(SIP_SUB, fname), scbp->hb.retx_counter);
+            if (sipTransportSendMessage(NULL,
+                                        sipPlatformUISMSubNotTimers[scb_index].message_buffer,
+                                        sipPlatformUISMSubNotTimers[scb_index].message_buffer_len,
+                                        sipPlatformUISMSubNotTimers[scb_index].message_type,
+                                        &(sipPlatformUISMSubNotTimers[scb_index].ipaddr),
+                                        sipPlatformUISMSubNotTimers[scb_index].port,
+                                        FALSE, TRUE, timeout, scbp) < 0) {
+                return (-1);
+            }
+        } else {
+            // Should we terminate this dialog? At the very least - stop
+            // the timer block, display error, and send a message to the app
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Either exceeded max retries for UDP"
+                              " or Timer F fired for TCP\n", fname);
+            sip_platform_msg_timer_subnot_stop(&sipPlatformUISMSubNotTimers[scb_index]);
+            scbp->hb.retx_flag = FALSE;
+            scbp->hb.retx_counter = 0;
+
+            memset(&sub_not_result_data, 0, sizeof(sub_not_result_data));
+            sub_not_result_data.request_id = scbp->request_id;
+            sub_not_result_data.sub_id = scbp->sub_id;
+            sub_not_result_data.gsm_id = scbp->gsm_id;
+            sub_not_result_data.line_id = scbp->hb.dn_line;
+
+            if ((scbp->last_sent_request_cseq_method == sipMethodSubscribe) ||
+                (scbp->last_sent_request_cseq_method == sipMethodRefer)) {
+                sub_not_result_data.u.subs_result_data.status_code = REQUEST_TIMEOUT;
+                sip_send_error_message(&sub_not_result_data,
+                                       scbp->subsNotCallbackTask,
+                                       scbp->subsResCallbackMsgID,
+                                       scbp->subsResultCallback, fname);
+            } else {
+                sub_not_result_data.u.notify_result_data.status_code = REQUEST_TIMEOUT;
+                // Set the state to ACTIVE, so we can go on appropriately. Note that
+                // this assignment below should be placed before sip_send_error_message() so
+                // that in case of callback running on same thread freed the scb, we will not
+                // set it back to active and try to use it later.
+                scbp->smState = SUBS_STATE_ACTIVE;
+                sip_send_error_message(&sub_not_result_data,
+                                       scbp->subsNotCallbackTask,
+                                       scbp->notResCallbackMsgID,
+                                       scbp->notifyResultCallback, fname);
+            }
+            // If there are any pending requests, execute them now
+            if (scbp->pendingRequests) {
+                handle_pending_requests(scbp);
+            }
+        }
+    }
+
+    return (0);
+}
+
+/**************************************************************
+ * Handle periodic timer
+ *************************************************************/
+void
+subsmanager_handle_periodic_timer_expire (void)
+{
+    const char     *fname = "subsmanager_handle_periodic_timer_expire";
+    sipSCB_t       *scbp = NULL;
+    int             scb_index;
+    ccsip_sub_not_data_t subs_term_data;
+    sipspi_msg_t    subscribe;
+    int             subscription_delta = 0;
+
+    // static char          count = 0;
+    /*
+     * Go through the list of SCBs and pick out the ones for
+     * which some action needs to be taken. This action may include:
+     * 1. If an incoming SUBSCRIBE has expired, inform the application
+     * 2. If auto-subscribe is set on an outgoing subscribe, send
+     *    a re-SUBSCRIBE message
+     */
+    config_get_value(CFGID_TIMER_SUBSCRIBE_DELTA, &subscription_delta,
+                     sizeof(subscription_delta));
+    for (scb_index = 0; scb_index < MAX_SCBS; scb_index++) {
+        scbp = &(subsManagerSCBS[scb_index]);
+        if (scbp->pendingClean) {
+            if (scbp->pendingCount > 0) {
+                scbp->pendingCount -= TMR_PERIODIC_SUBNOT_INTERVAL;
+            } else {
+                free_scb(scb_index, fname);
+            }
+            continue;
+        }
+        if (scbp->smState == SUBS_STATE_REGISTERED) {
+            continue;
+        }
+        if (scbp->smState != SUBS_STATE_IDLE) {
+            if (scbp->hb.expires > 0) {
+                scbp->hb.expires -= TMR_PERIODIC_SUBNOT_INTERVAL;
+            }
+            if (scbp->hb.expires > (subscription_delta + TMR_PERIODIC_SUBNOT_INTERVAL)) {
+                continue;
+            }
+
+            if (scbp->internal) {
+                // Subscription was internally generated
+                if (scbp->auto_resubscribe) {
+                    if (scbp->smState != SUBS_STATE_SENT_SUBSCRIBE) {
+                        // Send re-SUBSCRIBE message - but not if we just sent one
+                        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Auto reSubscribing:"
+                                         " scb=%d sub_id=%x\n",
+                                         DEB_F_PREFIX_ARGS(SIP_SUB, fname), scb_index, scbp->sub_id);
+                        memset(&subscribe, 0, sizeof(sipspi_msg_t));
+                        subscribe.msg.subscribe.sub_id = scbp->sub_id;
+                        subscribe.msg.subscribe.duration = scbp->hb.orig_expiration;
+                        (void) subsmanager_handle_ev_app_subscribe(&subscribe);
+                    }
+                } else {
+                    // Let app know its own SUBSCRIBE is about to expire
+                    // Application SHOULD renew its own subscription
+                    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Notifying App of internal"
+                                     " expiry: scb=%d sub_id=%x\n",
+                                     DEB_F_PREFIX_ARGS(SIP_SUB, fname), scb_index, scbp->sub_id);
+                    subs_term_data.sub_id = scbp->sub_id;
+                    subs_term_data.event = scbp->hb.event_type;
+                    subs_term_data.msg_id = scbp->subsTermCallbackMsgID;
+                    subs_term_data.request_id = scbp->request_id;
+                    subs_term_data.reason_code = SM_REASON_CODE_NORMAL;
+                    subs_term_data.u.subs_term_data.status_code =
+                        APPLICATION_SUBSCRIPTION_EXPIRED;
+                    if (scbp->subsTermCallback) {
+                        scbp->subsTermCallback(&subs_term_data);
+                    } else if (scbp->subsIndCallbackTask != CC_SRC_MIN) {
+                        (void) sip_send_message(&subs_term_data, scbp->subsIndCallbackTask,
+                                                scbp->subsTermCallbackMsgID);
+                    }
+                }
+
+            } else {
+                // Subscription was received from network
+                if (scbp->hb.expires <= 0) {
+                    // Let app know this SUBSCRIBE has expired
+                    // App SHOULD send final NOTIFY and terminate the session
+                    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Notifying App of external"
+                                     " expiry: scb=%d sub_id=%x\n",
+                                     DEB_F_PREFIX_ARGS(SIP_SUB, fname), scb_index, scbp->sub_id);
+                    subs_term_data.sub_id = scbp->sub_id;
+                    subs_term_data.event = scbp->hb.event_type;
+                    subs_term_data.msg_id = scbp->subsTermCallbackMsgID;
+                    subs_term_data.line_id = scbp->hb.dn_line;
+                    subs_term_data.gsm_id = scbp->gsm_id;
+                    subs_term_data.reason_code = SM_REASON_CODE_NORMAL;
+                    subs_term_data.u.subs_term_data.status_code =
+                        NETWORK_SUBSCRIPTION_EXPIRED;
+                    if (scbp->subsTermCallback) {
+                        scbp->subsTermCallback(&subs_term_data);
+                    } else if (scbp->subsIndCallbackTask != CC_SRC_MIN) {
+                        (void) sip_send_message(&subs_term_data, scbp->subsIndCallbackTask,
+                                                scbp->subsTermCallbackMsgID);
+                    }
+                }
+            }
+        }
+    }
+    // Re-start periodic timer
+    (void) sip_platform_subnot_periodic_timer_start(TMR_PERIODIC_SUBNOT_INTERVAL * 1000);
+
+    /*
+     * Comment out the periodic show since now we have a show-subscription-statistics CLI
+     * Do not want to completely remove this code since the CLI is not available in
+     * IP-Communicator where this could be enabled for debugging
+     */
+    /*
+     * if (count == 50) {
+     * // Show the stats for the Subsmanager
+     * CCSIP_DEBUG_TASK("Reg - RecdSubs RecdNots ActiveExtSubs - SentSubs SentNots ActiveIntSubs - CurSCBs MaxSCBs\n");
+     * CCSIP_DEBUG_TASK("-----------------------------------------------------------------------------------\n");
+     * CCSIP_DEBUG_TASK("%d - %d  %d  %d - %d  %d  %d - %d  %d\n",
+     * internalRegistrations, incomingSubscribes,
+     * incomingNotifies, incomingSubscriptions,
+     * outgoingSubscribes, outgoingNotifies,
+     * outgoingSubscriptions, currentScbsAllocated,
+     * maxScbsAllocated);
+     * count = 0;
+     * } else {
+     * count ++;
+     * }
+     */
+}
+
+/**************************************************************
+ * Format and send a response to a network received NOTIFY
+ * Currently this action is handled together with the response
+ * to SUBSCRIBE
+ **************************************************************/
+boolean
+sipSPISendSubNotifyResponse (sipSCB_t *scbp, int response_code)
+{
+    return TRUE;
+}
+
+static void
+show_scbs_inuse ()
+{
+    int i;
+    sipSCB_t *scbp = NULL;
+
+    if (subsManagerRunning == 0) {
+        return;
+    }
+
+    debugif_printf("---------SCB DUMP----------\n");
+    for (i = 0; i < MAX_SCBS; i++) {
+        scbp = &(subsManagerSCBS[i]);
+        if (scbp->smState == SUBS_STATE_IDLE) {
+            debugif_printf("SCB# %d, State = %d (IDLE)\n", i, scbp->smState);
+            continue;
+        }
+        if (scbp->smState == SUBS_STATE_REGISTERED) {
+            debugif_printf("SCB# %d, State = %d (REGISTERED) sub_id=%x\n",
+                    i, scbp->smState, scbp->sub_id);
+            debugif_printf("SCB# %d, eventPackage=%d\n",
+                    i, scbp->hb.event_type);
+            continue;
+        }
+        debugif_printf("SCB# %d, State = %d sub_id=%x\n", i, scbp->smState,
+                       scbp->sub_id);
+        debugif_printf("SCB# %d, pendingClean=%d, internal=%d, eventPackage=%d, "
+                       "norefersub=%d, subscriptionState=%d, expires=%d\n", i,
+                       scbp->pendingClean, scbp->internal, scbp->hb.event_type,
+                       scbp->norefersub, scbp->subscription_state, scbp->hb.expires);
+        debugif_printf("-----------------------------\n");
+    }
+}
+
+cc_int32_t
+show_subsmanager_stats (cc_int32_t argc, const char *argv[])
+{
+    debugif_printf("------ Current Subsmanager Statistics ------\n");
+    debugif_printf("Internal Registrations: %d\n", internalRegistrations);
+    debugif_printf("Total Incoming Subscribes: %d\n", incomingSubscribes);
+    debugif_printf("Total Incoming Notifies: %d\n", incomingNotifies);
+    debugif_printf("Total Incoming Unsolicited Notifies: %d\n", incomingUnsolicitedNotifies);
+    debugif_printf("Active Incoming Subscriptions: %d\n", incomingSubscriptions);
+    debugif_printf("Total Outgoing Subscribes: %d\n", outgoingSubscribes);
+    debugif_printf("Total Outgoing Notifies: %d\n", outgoingNotifies);
+    debugif_printf("Total Outgoing Unsolicited Notifies: %d\n", outgoingUnsolicitedNotifies);
+    debugif_printf("Active Outgoing Subscriptions: %d\n", outgoingSubscriptions);
+    debugif_printf("Current SCBs Allocated: %d\n", currentScbsAllocated);
+    debugif_printf("Total Maximum SCBs Ever Allocated: %d\n", maxScbsAllocated);
+    debugif_printf("------ End of Subsmanager Statistics ------\n");
+
+    // Now print out the status of all SCBs
+    show_scbs_inuse();
+    return 0;
+}
+
diff --git a/libs/sipcc/core/sipstack/ccsip_task.c b/libs/sipcc/core/sipstack/ccsip_task.c
new file mode 100644 (file)
index 0000000..318f2af
--- /dev/null
@@ -0,0 +1,3032 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_socket.h"
+#include "cpr_timers.h"
+#include "cpr_memory.h"
+#include "cpr_in.h"
+#include "cpr_ipc.h"
+#include "phntask.h"
+#include "util_string.h"
+#include "task.h"
+#include "phone.h"
+#include "text_strings.h"
+#include "ccsip_task.h"
+#include "ccsip_core.h"
+#include "ccsip_macros.h"
+#include "ccsip_messaging.h"
+#include "ccsip_sim.h"
+#include "ccsip_platform_udp.h"
+#include "ccsip_platform.h"
+#include "phone_debug.h"
+#include "ccsip_register.h"
+#include "debug.h"
+#include "ccsip_reldev.h"
+#include "ccsip_cc.h"
+#include "gsm.h"
+#include "fim.h"
+#include "ccapi.h"
+#include "lsm.h"
+#include "config.h"
+#include "check_sync.h"
+#include "sip_common_transport.h"
+#include "uiapi.h"
+#include "sip_csps_transport.h"
+#include "sip_common_regmgr.h"
+#include "sip_platform_task.h"
+#include "platform_api.h"
+#include "sip_interface_regmgr.h"
+#include "ccsip_publish.h"
+#include "platform_api.h"
+
+#ifdef SAPP_SAPP_GSM
+#define SAPP_APP_GSM 3
+#define SAPP_APP SAPP_APP_GSM
+#include "sapp.h"
+#endif
+
+#if defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_defines.h"
+#endif
+
+
+
+extern sipSCB_t *find_scb_by_callid(const char *callID, int *scb_index);
+extern int platThreadInit(char *);
+void sip_platform_handle_service_control_notify(sipServiceControl_t *scp);
+short SIPTaskProcessTimerExpiration(void *msg, uint32_t *cmd);
+extern cprMsgQueue_t sip_msgq;
+extern cprMsgQueue_t gsm_msgq;
+extern void ccsip_dump_recv_msg_info(sipMessage_t *pSIPMessage,
+                               cpr_ip_addr_t *cc_remote_ipaddr,
+                               uint16_t cc_remote_port);
+void destroy_sip_thread(void);
+
+// Global variables
+
+
+/*---------------------------------------------------------
+ *
+ * Definitions
+ *
+ */
+
+#define MESSAGE_WAITING_STR_SIZE 5
+
+#define MAC_ADDR_STR_LENGTH         16
+#define SIP_HEADER_SERVER_LEN       80
+#define SIP_HEADER_USER_AGENT_LEN   80
+#define SIP_PHONE_MODEL_NUMBER_LEN  32
+
+/* The maximun number of gsm_msgq depth to allow new incoming call,
+ */
+/*
+ * for RT, the threshold of 60 is not based on testing.
+ * As discussed in CSCsz33584 Code Review, it is better to drop the heavy load as it enters the system than let it go deep into the system.
+ * So we add the same logic into RT, but RT still cannot pass the test in CSCsz33584.
+ * Bottleneck of RT might be ccapp_msgq instead of gsm_msgq.
+ * A more complex solution should be studied to prevent RT phone from overload.
+ */
+#define MAX_DEPTH_OF_GSM_MSGQ_TO_ALLOW_NEW_INCOMING_CALL       60
+
+/* Internal request structure for restart/re-init request from platform */
+typedef enum {
+    SIP_RESTART_REQ_NONE,
+    SIP_RESTART_REQ_RESTART,   /* to restart */
+    SIP_RESTART_REQ_REINIT     /* to re-init */
+} ccsip_restart_cmd;
+
+typedef enum {
+    SIP_SHUTDOWN_REQ_NONE,
+    SIP_SHUTDOWN_REQ_SHUT
+} ccsip_shutdown_cmd;
+
+typedef struct ccsip_restart_req_t_ {
+    ccsip_restart_cmd cmd;     /* restart command */
+} ccsip_restart_req;
+
+typedef struct ccsip_shutdown_req_t_ {
+    ccsip_shutdown_cmd cmd;    /* shutdown command */
+    int action;
+    int reason;
+} ccsip_shutdown_req_t;
+
+
+extern cprThread_t sip_thread;
+
+
+/*---------------------------------------------------------
+ *
+ * Local Variables
+ *
+ */
+// static uint16_t    nfds = 0; No reference. Should this be removed?
+extern fd_set read_fds;
+extern fd_set write_fds;
+// static cpr_socket_t listenSocket = INVALID_SOCKET;  No reference. Should this be removed?
+// static sip_connection_t sipConn; Set in ccsip_task.c but never used. Should this be removed?
+// static cprMsgQueue_t sip_msg_queue;  No reference. Should this be removed?
+// static cprRegion_t   sip_region;   No reference. Should this be removed?
+// static cprPool_t     sip_pool;    No reference. Should this be removed?
+
+
+
+/*---------------------------------------------------------
+ *
+ * Global Variables
+ *
+ */
+sipGlobal_t sip;
+boolean sip_mode_quiet = FALSE;
+char sipHeaderServer[SIP_HEADER_SERVER_LEN];
+char sipHeaderUserAgent[SIP_HEADER_SERVER_LEN];
+char sipPhoneModelNumber[SIP_PHONE_MODEL_NUMBER_LEN];
+char sipUnregisterReason[MAX_SIP_REASON_LENGTH];
+
+boolean Is794x = FALSE;
+
+/*---------------------------------------------------------
+ *
+ * External Variables
+ * TODO reference through proper header files
+ */
+extern sipCallHistory_t gCallHistory[];
+
+
+/*---------------------------------------------------------
+ *
+ * Function declarations
+ *
+ */
+static void SIPTaskProcessSIPMessage(sipMessage_t *message);
+static int  SIPTaskProcessSIPNotify(sipMessage_t *pSipMessage);
+static int  SIPTaskProcessSIPNotifyMWI(sipMessage_t *pSipMessage,
+                                       line_t dn_line);
+static void SIPTaskProcessSIPNotifyCheckSync(sipMessage_t *pSipMessage);
+static void SIPTaskProcessSIPPreviousCallByeResponse(sipMessage_t *pSipMessage,
+                                             int response_code,
+                                             line_t previous_call_index);
+static void SIPTaskProcessSIPPreviousCallInviteResponse(sipMessage_t *pResponse,
+                                             int response_code,
+                                             line_t previous_call_index);
+static void SIPTaskProcessSIPNotifyRefer(sipMessage_t *pSipMessage);
+static int  SIPTaskProcessSIPNotifyServiceControl(sipMessage_t *pSipMessage);
+static void SIPTaskProcessRestart(ccsip_restart_cmd cmd);
+static void SIPTaskProcessShutdown(int action, int reason);
+
+/*---------------------------------------------------------
+ *
+ * Functions
+ *
+ */
+void
+get_ua_model_and_device (char sipHdrUserAgent[])
+{
+    const char fname[] = "get_ua_model_and_device";
+    char *model = NULL;
+
+    model = (char *)platGetModel();
+
+    if (model) {
+        if (strncmp(model, CSF_MODEL, 3) == 0) {
+            sstrncat(sipHdrUserAgent, CCSIP_SIP_CSF_USER_AGENT,
+                    SIP_HEADER_SERVER_LEN - strlen(sipHdrUserAgent));
+            sstrncpy(sipPhoneModelNumber, PHONE_MODEL_NUMBER_CSF,
+                    SIP_PHONE_MODEL_NUMBER_LEN);
+        } else if (strcmp(model, PHONE_MODEL) == 0) {
+            //if phone model is any of vendor defined, set as is.
+            sstrncat(sipHdrUserAgent, CCSIP_SIP_USER_AGENT,
+                    SIP_HEADER_SERVER_LEN - strlen(sipHdrUserAgent));
+            sstrncpy(sipPhoneModelNumber, PHONE_MODEL_NUMBER,
+                    SIP_PHONE_MODEL_NUMBER_LEN);
+        } else {
+            // Default to 7970
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"unknown model,defaulting to model 7970: %s\n", fname, model);
+            sstrncat(sipHdrUserAgent, CCSIP_SIP_7970_USER_AGENT,
+                    SIP_HEADER_SERVER_LEN - strlen(sipHdrUserAgent));
+            sstrncpy(sipPhoneModelNumber, PHONE_MODEL_NUMBER_7970,
+                    SIP_PHONE_MODEL_NUMBER_LEN);
+        }
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"could not obtain model information\n", fname);
+        sstrncat(sipHdrUserAgent, CCSIP_SIP_7970_USER_AGENT,
+                SIP_HEADER_SERVER_LEN - strlen(sipHdrUserAgent));
+        sstrncpy(sipPhoneModelNumber, PHONE_MODEL_NUMBER_7970,
+                SIP_PHONE_MODEL_NUMBER_LEN);
+    }
+}
+
+extern void ccsip_debug_init(void);
+
+/**
+ *
+ * SIPTaskInit
+ *
+ * Initialize the SIP Task
+ *
+ * Parameters:   None
+ *
+ * Return Value: SIP_OK
+ *
+ */
+void
+SIPTaskInit (void)
+{
+    /*
+     * Initialize platform specific parameters
+     */
+
+    // sipConn is set but never used. Should this be removed?
+    // for (i = 0; i < MAX_SIP_CONNECTIONS; i++) {
+    //     sipConn.read[i] = INVALID_SOCKET;
+    //     sipConn.write[i] = INVALID_SOCKET;
+    //}
+
+    /*
+     * Initialize cprSelect call parameters
+     */
+    // XXX already did in sip_platform_task_init(), like, two instructions ago
+    FD_ZERO(&read_fds);
+    FD_ZERO(&write_fds);
+    /*
+     * Do the debug init right here so that we can enable
+     * sip debugs during startup and not wait for sip stack
+     * to initialize.
+     */
+    ccsip_debug_init();
+
+    /************************
+    // Move all initialization to sip_sm_init called after config
+    if (ccsip_register_init() == SIP_ERROR) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"ccsip_register_init() failed.\n", fname);
+        return SIP_ERROR;
+    }
+
+     *
+     * Allocate timers for CCBs
+     *
+    if (sip_platform_timers_init() == SIP_ERROR) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_platform_timers_init() failed\n", fname);
+        return SIP_ERROR;
+    }
+    *************************/
+    // Initialize the value of the UA and Server headers
+    sipHeaderUserAgent[0] = '\0';
+    sipPhoneModelNumber[0] = '\0';
+    sipHeaderServer[0] = '\0';
+
+#if defined _COMMUNICATOR_
+    sstrncat(sipHeaderUserAgent, CCSIP_SIP_COMMUNICATOR_USER_AGENT,
+            sizeof(sipHeaderUserAgent) - strlen(sipHeaderUserAgent));
+    sstrncpy(sipPhoneModelNumber, PHONE_MODEL_NUMBER_COMMUNICATOR,
+            SIP_PHONE_MODEL_NUMBER_LEN);
+#else
+    get_ua_model_and_device(sipHeaderUserAgent);
+#endif
+
+    // Now add the firmware version
+    sstrncat(sipHeaderUserAgent, "/",
+            sizeof(sipHeaderUserAgent) - strlen(sipHeaderUserAgent));
+    sstrncat(sipHeaderUserAgent, gVersion,
+            sizeof(sipHeaderUserAgent) - strlen(sipHeaderUserAgent));
+    sstrncpy(sipHeaderServer, sipHeaderUserAgent,
+            SIP_HEADER_SERVER_LEN);
+}
+
+
+/**
+ *
+ * SIPTaskSendMsg (API)
+ *
+ * The API to send a message to the SIP task
+ *
+ * Parameters:   cmd - the message type
+ *               msg - the message buffer to send
+ *               len - length of the message buffer
+ *               usr - user pointer TEMPORARY VALUE TO MOVE INTO msg
+ *
+ * Return Value: SIP_OK or SIP_ERROR
+ *
+ */
+cpr_status_e
+SIPTaskSendMsg (uint32_t cmd, void *msg, uint16_t len, void *usr)
+{
+    phn_syshdr_t *syshdr;
+
+    syshdr = (phn_syshdr_t *) cprGetSysHeader(msg);
+    if (!syshdr) {
+        return CPR_FAILURE;
+    }
+    syshdr->Cmd = cmd;
+    syshdr->Len = len;
+    syshdr->Usr.UsrPtr = usr;
+
+    /*
+     * If we send a message to the task too soon the sip variable is not set yet.
+     * so just use the global for now. This happens if we create sip thread
+     * and immediately send a CFG_DONE message to sip thread from the main thread.
+     * This can be solved by waiting for an echo roundtrip from Thread before sending
+     * any other message. Will do someday.
+     */
+    if (cprSendMessage(sip_msgq /*sip.msgQueue */ , (cprBuffer_t)msg, (void **)&syshdr)
+        == CPR_FAILURE) {
+        cprReleaseSysHeader(syshdr);
+        return CPR_FAILURE;
+    }
+    return CPR_SUCCESS;
+}
+
+/**
+ *
+ * SIPTaskGetBuffer (API)
+ *
+ * The API to grab a buffer from the SIP buffer pool
+ *
+ * Parameters:   size - size of the buffer requested
+ *
+ * Return Value: requested buffer or NULL
+ *
+ */
+cprBuffer_t
+SIPTaskGetBuffer (uint16_t size)
+{
+    return cpr_malloc(size);
+}
+
+/**
+ *
+ * SIPTaskProcessListEvent (Internal API)
+ *
+ * Process a SIP event/message
+ *
+ * Parameters:   cmd - the type of event
+ *               msg - the event message
+ *               pUsr- a user pointer (TEMPORARY FIELD to be put into msg)
+ *               len - the length of the message
+ *
+ * Return Value: None
+ *
+ */
+void
+SIPTaskProcessListEvent (uint32_t cmd, void *msg, void *pUsr, uint16_t len)
+{
+    static const char *fname = "SIPTaskProcessListEvent";
+    sipSMEvent_t    sip_sm_event;
+    int             idx;
+       int                 p2psip = 0;
+       int             sdpmode = 0;
+    cprCallBackTimerMsg_t *timerMsg;
+    line_t          last_available_line;
+    CCM_ID ccm_id;
+
+    timerMsg = (cprCallBackTimerMsg_t *) msg;
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"cmd = 0x%x\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname), cmd);
+
+    /*
+     * Loop and wait until we get a TCP_PHN_CFG_TCP_DONE or a RESTART
+     * message from the phone before we finish
+     * initializing the SIPTask and SIP state machine
+     * (note this can happen any time the network stack is re-spun)
+     */
+    if ((sip.taskInited == FALSE) &&
+        ((cmd != TCP_PHN_CFG_TCP_DONE) &&
+         (cmd != SIP_RESTART) &&
+         (cmd != SIP_TMR_SHUTDOWN_PHASE2) &&
+         (cmd != SIP_SHUTDOWN) &&
+         (cmd != TIMER_EXPIRATION) &&
+         (cmd != THREAD_UNLOAD)) ) {
+        cpr_free(msg);
+        DEF_DEBUG(DEB_F_PREFIX" !!!sip.taskInited  is false. So not executing cmd=0x%x\n",
+            DEB_F_PREFIX_ARGS(SIP_EVT, fname), cmd);
+        return;
+    }
+
+    memset(&sip_sm_event, 0, sizeof(sipSMEvent_t));
+
+    /*
+     * Before CPR used to call the timer callback function out of its'
+     * timer tick so quite a few of the SIP timers would just post
+     * a timer expiration event back to its' queue to release the
+     * CPR thread. Now CPR just sends a timer expiration event
+     * to the SIP task so we don't want to double post a timer
+     * expiration event. Therefore check to see if this is a timer
+     * expiration event and if so possibly update the command before
+     * switching on it. If the function returns false it means all
+     * it did was update the command so we need to process the switch
+     * statement, if it returns true the timer event has been handled.
+     */
+    if (cmd == TIMER_EXPIRATION) {
+        if (SIPTaskProcessTimerExpiration(msg, &cmd)) {
+            cpr_free(msg);
+            return;
+        }
+        /*
+         * No need to release the buffer if we overwrote the command
+         * as the code that handles the new command below will release
+         * the buffer.
+         */
+    }
+
+       config_get_value(CFGID_P2PSIP, &p2psip, sizeof(p2psip));
+       config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
+
+    switch (cmd) {
+        /*
+         * See comment above
+         */
+    case TCP_PHN_CFG_TCP_DONE:
+        /*
+         * Ignore any TCP_PHN_CFG_TCP_DONE message since it is only used to
+         * determine when the SIP sm should be initialized
+         */
+        cpr_free(msg);
+
+        // If P2P set transport to UDP
+        if (p2psip == TRUE)
+               CC_Config_setIntValue(CFGID_TRANSPORT_LAYER_PROT, 2);
+
+
+        if (sip_sm_init() < 0) {
+               CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_sm_init() failed ",  fname);
+               return;
+        }
+
+        sip_mode_quiet = FALSE;
+
+        /*
+         * If P2P or SDP only do not register with SIP Server
+         */
+        if (!p2psip && !sdpmode)
+               sip_platform_init();
+        else
+               ui_set_sip_registration_state(CC_ALL_LINES, TRUE);
+
+        sip.taskInited = TRUE;
+        DEF_DEBUG(SIP_F_PREFIX"sip.taskInited is set to true ",  fname);
+#ifdef SAPP_SAPP_GSM
+        /*
+         * Initialize SAPP.
+         */
+        if (sapp_init() != 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sapp_init() failed.\n", fname);
+            break;
+        }
+        /*
+         * Start SAPP. SAPP will register with the CCM.
+         */
+        if (sapp_test() != 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sapp_test() failed.\n", fname);
+            break;
+        }
+#endif
+        break;
+
+    case SIP_GSM:
+        /*
+         * GSM CC Events
+         */
+
+#ifdef SAPP_SAPP_GSM
+        if (sapp_process_cc_event(msg) == 0) {
+            return;
+        }
+#endif
+
+        if (p2psip == TRUE)
+               sipTransportSetSIPServer();
+
+        if (sip_sm_process_cc_event(msg) != SIP_OK) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_sm_process_cc_event() "
+                              "failed.\n", fname);
+        }
+        break;
+
+    case SIP_TMR_INV_EXPIRE:
+        idx = (long) (timerMsg->usrData);
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t)idx);
+        cpr_free(msg);
+        if (!sip_sm_event.ccb) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"usrData does not point to a valid ccb "
+                             "discarding SIP_TMR_INV_EXPIRE event\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        }
+        sip_sm_event.type = E_SIP_INV_EXPIRES_TIMER;
+        if (sip_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    case SIP_TMR_SUPERVISION_DISCONNECT:
+        idx = (long) (timerMsg->usrData);
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t)idx);
+        cpr_free(msg);
+        if (!sip_sm_event.ccb) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"usrData does not point to a valid ccb "
+                             "discarding SIP_TMR_SUPERVISION_DISCONNECT event.\n",
+                             DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        }
+        sip_sm_event.type = E_SIP_SUPERVISION_DISCONNECT_TIMER;
+        if (sip_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    case SIP_TMR_INV_LOCALEXPIRE:
+        idx = (long) (timerMsg->usrData);
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t)idx);
+        cpr_free(msg);
+        if (!sip_sm_event.ccb) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"usrData does not point to valid ccb "
+                             "discarding SIP_TMR_INV_LOCALEXPIRE "
+                             "event.\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        }
+        sip_sm_event.type = E_SIP_INV_LOCALEXPIRES_TIMER;
+        if (sip_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+        /*
+         * UsrInfo is set to the IP address that bounced when an
+         * IMCP Unreachable. -1 means we had a timeout, not a bounce.
+         */
+    case SIP_TMR_MSG_RETRY:
+        idx = (long) (timerMsg->usrData);
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t)idx);
+        sip_sm_event.type = E_SIP_TIMER;
+        sip_sm_event.u.UsrInfo = ip_addr_invalid;
+        cpr_free(msg);
+        if (sip_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    case SIP_ICMP_UNREACHABLE:
+        {
+            sipMethod_t retxMessageType;
+
+            idx = msg ? (line_t) (*((uint32_t *)msg)) : 0;
+            sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t)idx);
+            /* IP address that bounced */
+            sip_sm_event.u.UsrInfo = (*(cpr_ip_addr_t *)pUsr);
+            cpr_free(msg);
+            if (!sip_sm_event.ccb) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"event does not point to valid ccb "
+                                 "SIP_ICMP_UNREACHABLE event.\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+                break;
+            }
+            /*
+             * Retrieve message type and cancel outstanding
+             * timer and remove ICMP Handler
+             */
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                              sip_sm_event.ccb->index,
+                              sip_sm_event.ccb->dn_line,
+                              fname, "ICMP_UNREACHABLE");
+
+            /* Do not need to process the ICMP_UNREACHABLE for fallback ccbs */
+            if (sip_sm_event.ccb->index <= REG_BACKUP_CCB) {
+                retxMessageType =
+                    sip_platform_msg_timer_messageType_get(sip_sm_event.ccb->index);
+                sip_platform_msg_timer_stop(sip_sm_event.ccb->index);
+
+                if (retxMessageType == sipMethodInvite) {
+                    config_get_value(CFGID_SIP_INVITE_RETX,
+                                     &sip_sm_event.ccb->retx_counter,
+                                     sizeof(sip_sm_event.ccb->retx_counter));
+                    if (sip_sm_event.ccb->retx_counter > MAX_INVITE_RETRY_ATTEMPTS) {
+                        sip_sm_event.ccb->retx_counter = MAX_INVITE_RETRY_ATTEMPTS;
+                    }
+                } else {
+                    config_get_value(CFGID_SIP_RETX,
+                                     &sip_sm_event.ccb->retx_counter,
+                                     sizeof(sip_sm_event.ccb->retx_counter));
+                    if (sip_sm_event.ccb->retx_counter > MAX_NON_INVITE_RETRY_ATTEMPTS) {
+                        sip_sm_event.ccb->retx_counter = MAX_NON_INVITE_RETRY_ATTEMPTS;
+                    }
+                }
+
+                /*
+                 * If REG CCB send into registration state machine else send
+                 * into sip call processing machine
+                 */
+                if (retxMessageType == sipMethodRegister) {
+                    /*
+                     * UsrInfo is set to the IP address that bounced when an
+                     * ICMP Unreachable. -1 means we had a timeout, not a bounce.
+                     */
+                    sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_TMR_RETRY;
+                    if (sip_reg_sm_process_event(&sip_sm_event) < 0) {
+                        CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+                    }
+                } else {
+                    sip_sm_event.type = E_SIP_ICMP_UNREACHABLE;
+                    if (sip_sm_process_event(&sip_sm_event) < 0) {
+                        CCSIP_DEBUG_ERROR(get_debug_string(SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+                    }
+                }
+            }
+        }
+        break;
+
+    case SIP_TMR_REG_EXPIRE:
+        idx = (long) (timerMsg->usrData);
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t)idx);
+        sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_TMR_EXPIRE;
+        cpr_free(msg);
+        if (sip_reg_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    case SIP_TMR_REG_ACK:
+        idx = (long) (timerMsg->usrData);
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t)idx);
+        sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_TMR_ACK;
+        cpr_free(msg);
+        if (sip_reg_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    case SIP_TMR_REG_WAIT:
+        idx = msg ? (line_t) (*((uint32_t *)msg)) : CC_NO_LINE;
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t)idx);
+        sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_TMR_WAIT;
+        cpr_free(msg);
+        if (sip_reg_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+        /*
+         * UsrInfo is set to the IP address that bounced when an
+         * ICMP Unreachable. -1 means we had a timeout, not a bounce.
+         */
+        /*
+         * currently, we only look for ccm_id for SIP_TMR_REG_RETRY. for rest
+         * of the REG msg, existing code will work because first field of
+         * ccsip_registration_msg_t is uint32_t.
+         */
+    case SIP_TMR_REG_RETRY:
+        idx = msg ? (line_t) ((ccsip_registration_msg_t *)msg)->ccb_index : CC_NO_LINE;
+        ccm_id = msg ? ((ccsip_registration_msg_t *)msg)->ccm_id : UNUSED_PARAM;
+        sip_sm_event.ccb = sip_sm_get_ccb_by_ccm_id_and_index(ccm_id, (line_t) idx);
+        sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_TMR_RETRY;
+        sip_sm_event.u.UsrInfo = ip_addr_invalid;
+        cpr_free(msg);
+        if (sip_sm_event.ccb == NULL || sip_reg_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    case SIP_REG_REQ:
+        idx = msg ? (line_t) (*((uint32_t *)msg)) : CC_NO_LINE;
+        cpr_free(msg);
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t) idx);
+        if (!sip_sm_event.ccb) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"event data does not point to a valid ccb"
+                             "SIP_REG_REQ event.\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            break;
+        }
+        (void) sip_sm_ccb_init(sip_sm_event.ccb, (line_t)idx, idx, SIP_REG_STATE_IDLE);
+        sip_sm_event.ccb->state = (sipSMStateType_t) SIP_REG_STATE_IDLE;
+        sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_REG_REQ;
+        if (sip_reg_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    case SIP_REG_CANCEL:
+        idx = msg ? (line_t) (*((uint32_t *)msg)) : CC_NO_LINE;
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t) idx);
+        sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_CANCEL;
+        cpr_free(msg);
+        if (sip_reg_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    case SIP_REG_CLEANUP:
+        idx = msg ? (line_t) (*((uint32_t *)msg)) : CC_NO_LINE;
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t) idx);
+        sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_CLEANUP;
+        cpr_free(msg);
+        if (sip_reg_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    case SIP_TMR_STANDBY_KEEPALIVE:
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+        sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_TMR_WAIT;
+        cpr_free(msg);
+        if (sip_reg_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+
+        // Subscribe/Notify Events
+    case SIPSPI_EV_CC_SUBSCRIBE_REGISTER:
+        if (subsmanager_handle_ev_app_subscribe_register(msg) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"subsmanager_handle_ev_app_subscribe_register() "
+                              "returned error.\n", fname);
+        }
+        cpr_free(msg);
+        break;
+    case SIPSPI_EV_CC_SUBSCRIBE:
+        if (subsmanager_handle_ev_app_subscribe(msg) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"subsmanager_handle_ev_app_subscribe() "
+                              "returned error.\n", fname);
+        }
+        cpr_free(msg);
+        break;
+    case SIPSPI_EV_CC_SUBSCRIBE_RESPONSE:
+        if (subsmanager_handle_ev_app_subscribe_response(msg) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"subsmanager_handle_ev_app_subscribe_response() "
+                              "returned error.\n", fname);
+        }
+        cpr_free(msg);
+        break;
+    case SIPSPI_EV_CC_NOTIFY:
+        {
+            int ret;
+
+            ret = subsmanager_handle_ev_app_notify(msg);
+            if (ret == SIP_ERROR) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"subsmanager_handle_ev_app_notify() "
+                                  "returned error.\n", fname);
+            } else if (ret == SIP_DEFER) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"subsmanager_handle_ev_app_notify() "
+                                  "deferred request.\n", fname);
+            }
+        }
+        cpr_free(msg);
+        break;
+    case SIPSPI_EV_CC_NOTIFY_RESPONSE:
+        if (subsmanager_handle_ev_app_notify_response(msg) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"subsmanager_handle_ev_app_notify_response() "
+                              "returned error.\n", fname);
+        }
+        cpr_free(msg);
+        break;
+    case SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED:
+        if (subsmanager_handle_ev_app_subscription_terminated(msg) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"subsmanager_handle_ev_app_subscription_terminated() "
+                              "returned error.\n", fname);
+        }
+        cpr_free(msg);
+        break;
+
+    case SIPSPI_EV_CC_PUBLISH_REQ:
+        if (publish_handle_ev_app_publish(msg) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"publish_handle_ev_app_publish() "
+                              "returned error.\n", fname);
+        }
+        cpr_free(msg);
+        break;
+
+    case SIP_REG_UPDATE:
+        last_available_line = msg ? (line_t) (*((uint32_t *)msg)) : CC_NO_LINE;
+        cpr_free(msg);
+        regmgr_handle_register_update(last_available_line);
+        break;
+    case REG_MGR_STATE_CHANGE:
+
+        sip_regmgr_rsp(((cc_regmgr_t *)msg)->rsp_id,
+                       ((cc_regmgr_t *)msg)->rsp_type,
+                       ((cc_regmgr_t *)msg)->wait_flag);
+        cpr_free(msg);
+        break;
+
+
+        // Retry timer expired for Sub/Not
+    case SIP_TMR_MSG_RETRY_SUBNOT:
+        idx = (long) (timerMsg->usrData);
+        cpr_free(msg);
+        if (subsmanager_handle_retry_timer_expire(idx) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"subsmanager_handle_retry_timer_expire() "
+                              "returned error.\n", fname);
+        }
+        break;
+        // Sub/Not periodic timer
+    case SIP_TMR_PERIODIC_SUBNOT:
+        cpr_free(msg);
+        subsmanager_handle_periodic_timer_expire();
+        publish_handle_periodic_timer_expire();
+        break;
+
+    case SIP_RESTART:
+        {
+            ccsip_restart_req *req = (ccsip_restart_req *) msg;
+            ccsip_restart_cmd restartCmd;
+
+            restartCmd = req->cmd;
+            // Handle restart event from platform
+            cpr_free(msg);
+
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received SIP_RESTART event restartCmd = (%d)\n",
+                DEB_F_PREFIX_ARGS(SIP_EVT, fname), restartCmd);
+            SIPTaskProcessRestart(restartCmd);
+            break;
+        }
+
+    case SIP_TMR_SHUTDOWN_PHASE2:
+        {
+            // Handle shutdown phase 2 event (after unregistration)
+            // Note: boolean is 8 bits, but 32 bits have been allocated for it
+            // so we need to dereference msg with 32 bits
+            int action;
+
+            if (msg) {
+                action = (*((uint32_t *)msg));
+            } else {
+                action = SIP_INTERNAL;
+            }
+
+            cpr_free(msg);
+
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received SIP_TMR_SHUTDOWN_PHASE2 event action= (%d)\n",
+                             DEB_F_PREFIX_ARGS(SIP_EVT, fname),  action);
+            sip_shutdown_phase2(action);
+        }
+        break;
+
+    case SIP_SHUTDOWN:
+        {
+            ccsip_shutdown_req_t *req = (ccsip_shutdown_req_t *) msg;
+            int action;
+            int reason;
+
+            action = req->action;
+            reason = req->reason;
+            cpr_free(msg);
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received SIP_SHUTDOWN message\n",
+                DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+            SIPTaskProcessShutdown(action, reason);
+        }
+        break;
+
+    case THREAD_UNLOAD:
+        {
+            destroy_sip_thread();
+        }
+        break;
+
+    case SIP_TMR_GLARE_AVOIDANCE:
+        // Handle glare retry timer expiry
+        idx = (long) (timerMsg->usrData);
+        sip_sm_event.ccb = sip_sm_get_ccb_by_index((line_t)idx);
+        sip_sm_event.type = (sipSMEventType_t) E_SIP_GLARE_AVOIDANCE_TIMER;
+        cpr_free(msg);
+        if (sip_sm_process_event(&sip_sm_event) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+        }
+        break;
+
+    default:
+        cpr_free(msg);
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unknown message\n, fname");
+        break;
+    }
+    return;
+}
+
+/**
+ * SIPTaskCheckSource
+ *
+ * Ensure that sender is a trusted source
+ *
+ * Parameters: from - the source address for the message
+ *
+ * Return Value: SIP_OK if message can be processed further
+ */
+int
+SIPTaskCheckSource (cpr_sockaddr_storage from)
+{
+    static const char fname[] = "SIPTaskCheckSource";
+    int             regConfigValue;
+    line_t          line_index, line_end;
+    cpr_ip_addr_t   fromIPAddr;
+    int             retval = SIP_ERROR;
+    char            fromIPAddrStr[MAX_IPADDR_STR_LEN];
+    ccsipCCB_t     *ccb = NULL;
+    uint32_t        data, *data_p;
+
+    // If not registered, return ok
+    config_get_value(CFGID_PROXY_REGISTER, &regConfigValue, sizeof(regConfigValue));
+    if (regConfigValue == 0) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"CFGID_PROXY_REGISTER is false\n",
+            DEB_F_PREFIX_ARGS(SIP_IP_MATCH, fname));
+        return SIP_OK;
+    }
+
+    line_end = 1;
+    line_end += TEL_CCB_END;
+
+    util_extract_ip(&fromIPAddr,&from);
+    util_ntohl(&fromIPAddr, &fromIPAddr);
+
+    fromIPAddrStr[0] = '\0';
+    ipaddr2dotted(fromIPAddrStr, &fromIPAddr);
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Attempting to recognize \"%s\"\n",
+        DEB_F_PREFIX_ARGS(SIP_IP_MATCH, fname), fromIPAddrStr);
+    // Get the proxy configured for all lines and check against those
+    for (line_index = REG_CCB_START; line_index <= line_end; line_index++) {
+        // Check the binary from-IP address with the ccb->reg.addr value
+        if (sip_config_check_line((line_t)(line_index - TEL_CCB_END))) {
+            ccb = sip_sm_get_ccb_by_index(line_index);
+            if (ccb && util_compare_ip(&(ccb->reg.addr), &fromIPAddr)) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Found server IP match\n",
+                    DEB_F_PREFIX_ARGS(SIP_IP_MATCH, fname));
+                retval = SIP_OK;
+                break;
+            }
+        }
+    }
+    if (retval == SIP_OK) {
+        return retval;
+    }
+    // Not found - continue to check backup CCBs
+    ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+    if (ccb && util_compare_ip(&(ccb->reg.addr), &fromIPAddr)) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Found backup server IP match\n",
+            DEB_F_PREFIX_ARGS(SIP_IP_MATCH, fname));
+        retval = SIP_OK;
+    }
+    if (retval == SIP_OK) {
+        return retval;
+    }
+    // Not found - continue to check with fallback CCBs
+    data = 0x00;
+    data_p = &data;
+    ccb = sip_regmgr_get_fallback_ccb_list(data_p);
+    while ((ccb != NULL) && (retval != SIP_OK)) {
+        if (util_compare_ip(&(ccb->reg.addr), &fromIPAddr)) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Found fallback server IP match\n",
+                DEB_F_PREFIX_ARGS(SIP_IP_MATCH, fname));
+            retval = SIP_OK;
+        }
+        ccb = sip_regmgr_get_fallback_ccb_list(data_p);
+    }
+    return retval;
+}
+
+/**
+ *
+ * SIPTaskProcessUDPMessage (Internal API)
+ *
+ * Process the received (via UDP) SIP message
+ *
+ * Parameters:   msg  - the message buffer
+ *               len  - length of the message buffer
+ *               from - the source address for the message
+ *
+ * Return Value: SIP_OK if message could be processed (though this itself
+ *               may fail) or SIP_ERROR
+ *
+ */
+int
+SIPTaskProcessUDPMessage (cprBuffer_t msg,
+                          uint16_t len,
+                          cpr_sockaddr_storage from)
+{
+    static const char *fname = "SIPProcessUDPMessage";
+    sipMessage_t   *pSipMessage = NULL;
+    static char     buf[SIP_UDP_MESSAGE_SIZE + 1];
+    char            remoteIPAddrStr[MAX_IPADDR_STR_LEN];
+    uint32_t        bytes_used = 0;
+    int             accept_msg = SIP_OK;
+    cpr_ip_addr_t   ip_addr;
+       int p2psip = 0;
+    /*
+     * Convert IP address to string, for debugs
+     */
+    util_extract_ip(&ip_addr, &from);
+
+    util_ntohl(&ip_addr, &ip_addr);
+
+    if (SipDebugMessage) {
+        ipaddr2dotted(remoteIPAddrStr, &ip_addr);
+    }
+
+    util_extract_ip(&ip_addr, &from);
+
+    if (len > SIP_UDP_MESSAGE_SIZE) {
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Received UDP message from <%s>:<%d>: "
+                            "message too big: msg size = %d, max SIP "
+                            "pkt size = %d\n", DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname), remoteIPAddrStr,
+                             util_get_port(&from), bytes_used, SIP_UDP_MESSAGE_SIZE);
+        cpr_free(msg);
+        return SIP_ERROR;
+    }
+
+    /*
+     * Copy message to a local memory and release the system buffer
+     */
+    memcpy(buf, (char *)msg, len);
+    buf[len] = '\0'; /* NULL terminate for debug printing */
+    cpr_free(msg);
+
+    /*
+     * Print the received UDP packet info
+     */
+    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"recv UDP message from <%s>:<%d>, length=<%d>, "
+                        "message=\n", DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname), remoteIPAddrStr,
+                        util_get_port(&from), len);
+    CCSIP_DEBUG_MESSAGE_PKT(buf);
+
+    /*
+     * Determine whether we want to process this packet
+     * Initially just do this if we are talking to CCM - can be expanded later
+     */
+
+
+
+    config_get_value(CFGID_P2PSIP, &p2psip, sizeof(p2psip));
+
+    if (p2psip == 0) {
+       if (sip_regmgr_get_cc_mode(1) == REG_MODE_CCM) {
+               accept_msg = SIPTaskCheckSource(from);
+               if (accept_msg != SIP_OK) {
+                       CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SIPTaskCheckSource() failed - Sender not "
+                                       "recognized\n", fname);
+            return SIP_ERROR;
+               }
+       }
+    }
+
+    /*
+     * Convert to SIP representation
+     */
+    pSipMessage = sippmh_message_create();
+    if (!pSipMessage) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sippmh_message_create() failed\n", fname);
+        return SIP_ERROR;
+    }
+
+    bytes_used = len;
+
+    if (sippmh_process_network_message(pSipMessage, buf, &bytes_used)
+            == STATUS_FAILURE) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sippmh_process_network_message() "
+                          "failed. discarding the message.\n", fname);
+        free_sip_message(pSipMessage);
+        return SIP_ERROR;
+    }
+    /*
+     * Add processing here to append received=<ip_address> to the first Via
+     * field if the IP address we received this message from is not the same
+     * as the IP address in the Via field or if it contains a domain name
+     */
+    sippmh_process_via_header(pSipMessage, &ip_addr);
+
+    ccsip_dump_recv_msg_info(pSipMessage, &ip_addr, 0);
+
+    /* Process SIP message */
+    SIPTaskProcessSIPMessage(pSipMessage);
+    return SIP_OK;
+}
+
+/**
+ *
+ * SIPTaskProcessTCPMessage (Internal API)
+ *
+ * Process the received (via TCP) SIP message
+ *
+ * Parameters:   msg  - the message buffer
+ *               len  - length of the message buffer
+ *               from - the source address for the message
+ *
+ * Return Value: SIP_OK if message could be processed (though this itself
+ *               may fail) or SIP_ERROR
+ *
+ */
+void
+SIPTaskProcessTCPMessage (sipMessage_t *pSipMessage, cpr_sockaddr_storage from)
+{
+    cpr_ip_addr_t   ip_addr;
+
+    CPR_IP_ADDR_INIT(ip_addr);
+    /*
+     * Convert IP address to string, for debugs
+     */
+    util_extract_ip(&ip_addr, &from);
+
+    /*
+     * Add processing here to append received=<ip_address> to the first Via
+     * field if the IP address we received this message from is not the same
+     * as the IP address in the Via field or if it contains a domain name
+     */
+    sippmh_process_via_header(pSipMessage, &ip_addr);
+
+    /* Process SIP message */
+    SIPTaskProcessSIPMessage(pSipMessage);
+}
+
+int
+SIPTaskRetransmitPreviousResponse (sipMessage_t *pSipMessage,
+                                  const char *fname,
+                                  const char *pCallID,
+                                  sipCseq_t *sipCseq,
+                                  int response_code,
+                                  boolean is_request)
+{
+    sipRelDevMessageRecord_t *pRequestRecord = NULL;
+    int             handle = -1;
+    const char     *reldev_to = NULL;
+    const char     *reldev_from = NULL;
+    sipLocation_t  *reldev_to_loc = NULL;
+    sipLocation_t  *reldev_from_loc = NULL;
+
+    pRequestRecord = (sipRelDevMessageRecord_t *)
+        cpr_calloc(1, sizeof(sipRelDevMessageRecord_t));
+    if (!pRequestRecord) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to allocate "
+                          "mem for pRequestRecord.\n", fname);
+        return SIP_ERROR;
+    }
+
+    // Copy to-tag
+    reldev_to = sippmh_get_cached_header_val(pSipMessage, TO);
+    if (reldev_to) {
+        reldev_to_loc = sippmh_parse_from_or_to((char *)reldev_to, TRUE);
+        if ((reldev_to_loc) && (reldev_to_loc->tag)) {
+            sstrncpy(pRequestRecord->tag,
+                     sip_sm_purify_tag(reldev_to_loc->tag),
+                                       MAX_SIP_TAG_LENGTH);
+        }
+
+        // Copy to-user
+        if (reldev_to_loc) {
+            sstrncpy(pRequestRecord->to_user,
+                     reldev_to_loc->genUrl->u.sipUrl->user,
+                     RELDEV_MAX_USER_NAME_LEN);
+            sippmh_free_location(reldev_to_loc);
+        }
+    }
+
+    // Copy from-user and from-host
+    reldev_from = sippmh_get_cached_header_val(pSipMessage, FROM);
+    if (reldev_from) {
+        reldev_from_loc = sippmh_parse_from_or_to((char *)reldev_from, TRUE);
+        if (reldev_from_loc) {
+            sstrncpy(pRequestRecord->from_user,
+                     reldev_from_loc->genUrl->u.sipUrl->user,
+                     RELDEV_MAX_USER_NAME_LEN);
+            sstrncpy(pRequestRecord->from_host,
+                     reldev_from_loc->genUrl->u.sipUrl->host,
+                     RELDEV_MAX_HOST_NAME_LEN);
+
+            sippmh_free_location(reldev_from_loc);
+        }
+    }
+
+    pRequestRecord->is_request = is_request;
+    pRequestRecord->response_code = response_code;
+
+    // Copy Call-id
+    sstrncpy(pRequestRecord->call_id, (pCallID) ? pCallID : "",
+             MAX_SIP_CALL_ID);
+
+    // Copy CSeq values
+    pRequestRecord->cseq_method = sipCseq->method;
+    pRequestRecord->cseq_number = sipCseq->number;
+
+    if (sipRelDevMessageIsDuplicate(pRequestRecord, &handle)) {
+        cpr_free(pRequestRecord);
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Line filter: Previous "
+                         "Call ID. Resending stored "
+                         "response...\n", DEB_F_PREFIX_ARGS(SIP_MSG, fname));
+        if (sipRelDevCoupledMessageSend(handle) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipRelDevCoupledMessageSend(%d)"
+                              "returned error.\n", fname, handle);
+        }
+        return SIP_OK;
+    } else {
+        cpr_free(pRequestRecord);
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Line filter: Previous Call "
+                         "ID. Message not in reTx list.\n", DEB_F_PREFIX_ARGS(SIP_MSG, fname));
+    }
+    return SIP_ERROR;
+}
+
+/**
+ *
+ * SIPTaskProcessTimerExpiration
+ *
+ * Process a msg indicating a timer has expired
+ *
+ * Parameters:   msg - the timer callback msg
+ *               cmd - allows us to overwrite the cmd if needed
+ *
+ * Return Value: boolean, if true return as timer has been processed.
+ *
+ */
+short
+SIPTaskProcessTimerExpiration (void *msg, uint32_t *cmd)
+{
+    static const char fname[] = "SIPTaskProcessTimerExpiration";
+    cprCallBackTimerMsg_t *timerMsg;
+    boolean returnCode = TRUE;
+    uint32_t handle;
+
+    timerMsg = (cprCallBackTimerMsg_t *) msg;
+    TMR_DEBUG(DEB_F_PREFIX"Timer %s expired. Id is %d\n", DEB_F_PREFIX_ARGS(SIP_TIMER, fname),
+              timerMsg->expiredTimerName, timerMsg->expiredTimerId);
+
+    /* The REGALLFAIL Timer message could come in before the task has
+     * been initialized. Need to handle this timer, in order to restart
+     * the system. */
+    if ((sip.taskInited == FALSE) && (timerMsg->expiredTimerId != SIP_REGALLFAIL_TIMER)) {
+        return returnCode;
+    }
+
+    switch (timerMsg->expiredTimerId) {
+    case SIP_ACK_TIMER:
+        *cmd = SIP_TMR_REG_ACK;
+        returnCode = FALSE;
+        break;
+
+    case SIP_WAIT_TIMER:
+        sip_regmgr_wait_timeout_expire(timerMsg->usrData);
+        break;
+
+    case SIP_RETRY_TIMER:
+        sip_regmgr_retry_timeout_expire(timerMsg->usrData);
+        break;
+
+    case SIP_MSG_TIMER:
+        *cmd = SIP_TMR_MSG_RETRY;
+        returnCode = FALSE;
+        break;
+
+    case SIP_EXPIRES_TIMER:
+        *cmd = SIP_TMR_INV_EXPIRE;
+        returnCode = FALSE;
+        break;
+
+    case SIP_REG_TIMEOUT_TIMER:
+        ccsip_register_timeout_retry(timerMsg->usrData);
+        break;
+
+    case SIP_REG_EXPIRES_TIMER:
+        *cmd = SIP_TMR_REG_EXPIRE;
+        returnCode = FALSE;
+        break;
+
+    case SIP_LOCAL_EXPIRES_TIMER:
+        *cmd = SIP_TMR_INV_LOCALEXPIRE;
+        returnCode = FALSE;
+        break;
+
+    case SIP_SUPERVISION_TIMER:
+        *cmd = SIP_TMR_SUPERVISION_DISCONNECT;
+        returnCode = FALSE;
+        break;
+
+    case SIP_GLARE_AVOIDANCE_TIMER:
+        *cmd = SIP_TMR_GLARE_AVOIDANCE;
+        returnCode = FALSE;
+        break;
+
+    case SIP_KEEPALIVE_TIMER:
+        *cmd = SIP_TMR_STANDBY_KEEPALIVE;
+        returnCode = FALSE;
+        break;
+
+    case SIP_UNREGISTRATION_TIMER:
+        sip_platform_unregistration_callback(timerMsg->usrData);
+        break;
+
+    case SIP_SUBNOT_TIMER:
+        *cmd = SIP_TMR_MSG_RETRY_SUBNOT;
+        returnCode = FALSE;
+        break;
+
+    case SIP_SUBNOT_PERIODIC_TIMER:
+        sip_platform_subnot_periodic_timer_callback(timerMsg->usrData);
+        break;
+
+    case SIP_PUBLISH_RETRY_TIMER:
+        handle = (long)(timerMsg->usrData);
+        if (publish_handle_retry_timer_expire(handle) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"publish_handle_retry_timer_expire() "
+                              "returned error.\n", fname);
+        }
+        break;
+
+    case SIP_UNSOLICITED_TRANSACTION_TIMER:
+        subsmanager_unsolicited_notify_timeout(timerMsg->usrData);
+        break;
+
+    case SIP_NOTIFY_TIMER:
+        sip_regmgr_notify_timer_callback(timerMsg->usrData);
+        break;
+
+       case SIP_PASSTHROUGH_TIMER:
+               CCSIP_DEBUG_ERROR("%s: Pass Through Timer fired ! \n", fname);
+               break;
+
+    case SIP_REGALLFAIL_TIMER:
+        sip_regmgr_regallfail_timer_callback(timerMsg->usrData);
+        break;
+
+    default:
+        err_msg("%s: unknown timer %s\n", fname, timerMsg->expiredTimerName);
+        break;
+    }
+    return (returnCode);
+}
+
+
+/**
+ *
+ * SIPTaskProcessSIPMessage
+ *
+ * Process a received SIP message
+ *
+ * Parameters:   pSipMessage - the SIP message
+ *
+ * Return Value: SIP_OK or SIP_ERROR
+ *
+ */
+static void
+SIPTaskProcessSIPMessage (sipMessage_t *pSipMessage)
+{
+    static const char *fname = "SIPTaskProcessSIPMessage";
+    sipSMEvent_t    sip_sm_event;
+    ccsipCCB_t     *reg_ccb = NULL,*ccb = NULL;
+    sipStatusCodeClass_t code_class = codeClassInvalid;
+    sipMethod_t     method = sipMethodInvalid;
+    const char     *pCallID = NULL;
+    line_t          line_index = 1;
+    boolean         is_request = FALSE;
+    int             regConfigValue, response_code = 0;
+    int             requestStatus = SIP_MESSAGING_ERROR;
+    char            errortext[MAX_SIP_URL_LENGTH];
+    const char     *cseq = NULL;
+    sipCseq_t      *sipCseq = NULL;
+    uint16_t        result_code = 0;
+    const char     *max_fwd_hdr = NULL;
+    int32_t         max_fwd_hdr_val;
+    int             rc;
+    char            tmp_str[STATUS_LINE_MAX_LEN];
+    sipSCB_t       *scbp = NULL;
+    sipTCB_t       *tcbp = NULL;
+    int             scb_index;
+    //ccsip_event_data_t * evt_data_ptr = NULL;
+
+    sip_sm_event.u.pSipMessage = pSipMessage;
+    /*
+     * is_complete will be FALSE if we received fewer bytes than
+     * specified in the content_length field.  If that happens, we
+     * want to return a 400 Bad Request message.
+     */
+    if (!sippmh_is_message_complete(pSipMessage)) {
+        /* Send 400 error */
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_SDP_ERROR,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        free_sip_message(pSipMessage);
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "sippmh_is_message_complete()");
+        return;
+    }
+
+    /*
+     * Determine Type, Method, and Call ID of this SIP message
+     * If the SIP message is a response, also get the response code.
+     */
+    is_request = sippmh_is_request(pSipMessage);
+    if (is_request) {
+        /*
+         * Check the value of the max-forwards header - if present
+         */
+        max_fwd_hdr = sippmh_get_header_val(pSipMessage,
+                                            SIP_HEADER_MAX_FORWARDS, NULL);
+        if (max_fwd_hdr) {
+            max_fwd_hdr_val = sippmh_parse_max_forwards(max_fwd_hdr);
+            if (max_fwd_hdr_val < 0) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Invalid Max Fwd Value detected\n",
+                                  fname);
+                /* Send 483 error */
+                if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_MANY_HOPS,
+                                            SIP_CLI_ERR_MANY_HOPS_PHRASE,
+                                            SIP_WARN_MISC,
+                                            NULL, NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_BAD_REQ);
+                }
+                free_sip_message(pSipMessage);
+                return;
+            }
+        }
+
+        sipGetRequestMethod(pSipMessage, &method);
+
+        switch (method) {
+        case sipMethodInvalid:
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipGetResponseMethod");
+            /* Send 400 error */
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        SIP_WARN_MISC,
+                                        SIP_CLI_ERR_BAD_REQ_METHOD_UNKNOWN,
+                                        NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+            free_sip_message(pSipMessage);
+            return;
+
+        case sipMethodUnknown:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SIP method not implemented\n", fname);
+            // Send 501 error
+            if (sipSPISendErrorResponse(pSipMessage, SIP_SERV_ERR_NOT_IMPLEM,
+                                        SIP_SERV_ERR_NOT_IMPLEM_PHRASE, 0,
+                                        NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_SERV_ERR_NOT_IMPLEM);
+            }
+            free_sip_message(pSipMessage);
+            return;
+
+        case sipMethodRegister:
+        case sipMethodPrack:
+        case sipMethodComet:
+        case sipMethodMessage:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"SIP method not allowed\n", fname);
+            // Send 405 error
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_NOT_ALLOWED,
+                                        SIP_CLI_ERR_NOT_ALLOWED_PHRASE, 0,
+                                        NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_NOT_ALLOWED);
+            }
+            free_sip_message(pSipMessage);
+            return;
+
+        default:
+            break;
+        }
+    } else {
+        if (sipGetResponseMethod(pSipMessage, &method) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipGetResponseMethod");
+            free_sip_message(pSipMessage);
+            return;
+        }
+        if (sipGetResponseCode(pSipMessage, &response_code) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipGetResponseCode");
+            free_sip_message(pSipMessage);
+            return;
+        }
+        code_class = sippmh_get_code_class((uint16_t) response_code);
+    }
+
+    /* Get the CSeq */
+    cseq = sippmh_get_cached_header_val(pSipMessage, CSEQ);
+    if (!cseq) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to extract "
+                          "CSeq from message.\n", fname);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_VIA_OR_CSEQ,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        free_sip_message(pSipMessage);
+        return;
+    }
+    sipCseq = sippmh_parse_cseq(cseq);
+    if (!sipCseq) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to parse "
+                          "CSeq from message.\n", fname);
+        if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                    SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                    SIP_WARN_MISC,
+                                    SIP_CLI_ERR_BAD_REQ_VIA_OR_CSEQ,
+                                    NULL) != TRUE) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                              fname, SIP_CLI_ERR_BAD_REQ);
+        }
+        free_sip_message(pSipMessage);
+        return;
+    }
+
+    pCallID = sippmh_get_cached_header_val(pSipMessage, CALLID);
+    if (!pCallID) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Cannot obtain SIP Call ID.\n", fname);
+        /*
+         * Since we have no Call-ID, we can't create a response;
+         * therefore, we drop it.
+         */
+        cpr_free(sipCseq);
+        free_sip_message(pSipMessage);
+        return;
+    }
+
+    /*
+     * Unsolicited NOTIFY processing
+     */
+    if ((is_request) && (method == sipMethodNotify)) {
+        CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                         fname, SIP_METHOD_NOTIFY);
+        rc = SIPTaskProcessSIPNotify(pSipMessage);
+        if (rc != SIP_DEFER) {
+            if (rc != 0) {
+                // This Notify is in response to a previous SUBSCRIBE or REFER
+                (void) subsmanager_handle_ev_sip_subscribe_notify(pSipMessage);
+            }
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+        }
+    }
+
+    /*
+     * SUBSCRIBE processing. Subscription requests received from the network,
+     * are processed here and do not interact with the main call processing
+     * states. All SUBSCRIBE requests and responses are directed to the
+     * subscription manager. However NOTIFY responses need to be testes to
+     * see whether the NOTIFY request was generated via CC or SM
+     */
+    if ((is_request) && (method == sipMethodSubscribe)) {
+
+        config_get_value(CFGID_PROXY_REGISTER, &regConfigValue, sizeof(regConfigValue));
+
+        reg_ccb = sip_sm_get_ccb_by_index(REG_CCB_START);
+
+        if ((regConfigValue == 0) || (reg_ccb && reg_ccb->reg.registered)) {
+
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_SUBSCRIBE);
+            (void) subsmanager_handle_ev_sip_subscribe(pSipMessage, method, FALSE);
+        } else {
+            if (sipSPISendErrorResponse(pSipMessage, SIP_SERV_ERR_INTERNAL,
+                                        SIP_SERV_ERR_INTERNAL_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_SERV_ERR_INTERNAL);
+            }
+
+        }
+        cpr_free(sipCseq);
+        free_sip_message(pSipMessage);
+        return;
+    }
+    if (is_request == FALSE) {
+        switch (method) {
+        case sipMethodSubscribe:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv Subs Response.\n",
+                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            (void) subsmanager_handle_ev_sip_response(pSipMessage);
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+
+        case sipMethodNotify:
+            scbp = find_scb_by_callid(pCallID, &scb_index);
+            if (scbp != NULL) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv Notify response\n",
+                    DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+                (void) subsmanager_handle_ev_sip_response(pSipMessage);
+                cpr_free(sipCseq);
+                free_sip_message(pSipMessage);
+                return;
+            } else {
+                tcbp = find_tcb_by_sip_callid(pCallID);
+                if (tcbp != NULL) {
+                    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv Unsolicited Notify response\n",
+                        DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+                    (void) subsmanager_handle_ev_sip_unsolicited_notify_response(pSipMessage, tcbp);
+                    cpr_free(sipCseq);
+                    free_sip_message(pSipMessage);
+                    return;
+                }
+            }
+            break;
+
+        case sipMethodPublish:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv PUBLISH Response.\n",
+                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            (void) publish_handle_ev_sip_response(pSipMessage);
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+
+        case sipMethodInfo:
+            // XXX FIXME see the comments in sipSPISendInfo()
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv INFO Response (silently dropped).\n",
+                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+
+        default:
+            break;
+
+        }
+    }
+
+
+    /*
+     * Determine which line this SIP message is for.
+     */
+    result_code = sip_sm_determine_ccb(pCallID, sipCseq, pSipMessage,
+                                       is_request, &ccb);
+    if (result_code != 0) {
+        if (is_request) {
+            if (result_code == SIP_CLI_ERR_LOOP_DETECT) {
+                /* Send 482 error */
+                if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_LOOP_DETECT,
+                                            SIP_CLI_ERR_LOOP_DETECT_PHRASE,
+                                            SIP_WARN_MISC,
+                                            NULL, NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_LOOP_DETECT);
+                }
+            } else {
+                /* Send 400 error */
+                if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                            SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                            SIP_WARN_MISC,
+                                            NULL, NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_BAD_REQ);
+                }
+            }
+        } else { //is response
+            // This may be a response for a request generated via sub/not i/f
+            scbp = find_scb_by_callid(pCallID, &scb_index);
+            if (scbp != NULL) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv Refer Response.\n",
+                    DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+                (void) subsmanager_handle_ev_sip_response(pSipMessage);
+            } else {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_sm_determine_ccb(): "
+                                  "bad response. Dropping message.\n", fname);
+            }
+        }
+        cpr_free(sipCseq);
+        free_sip_message(pSipMessage);
+        return;
+    }
+
+    if (ccb) {
+        sip_sm_event.ccb = ccb;
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Line filter: Call ID match:  Destination line = "
+                         "<%d/%d>.\n", DEB_F_PREFIX_ARGS(SIP_ID, fname), ccb->index, ccb->dn_line);
+
+    } else {
+
+        boolean is_previous_call_id = FALSE;
+        line_t  previous_call_index = 0;
+
+        /*
+         * Unsolicited options processing
+         */
+        if ((is_request) && (method == sipMethodOptions)) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"recv SIP OPTIONS (outside of dialog) message.\n",
+                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname)); //
+            /*
+             * Send an options request to the gsm for this out of call
+             * options request. pSipMessage will be freed on return.
+             */
+            sip_cc_options(CC_NO_CALL_ID, CC_NO_LINE, pSipMessage);
+            cpr_free(sipCseq);
+            return;
+        }
+
+        /*
+         * Unsolicited INFO processing
+         */
+        if ((is_request) && (method == sipMethodInfo)) {
+            CCSIP_DEBUG_ERROR("%s: Error: recv out-of-dialog SIP INFO message.\n", fname); //
+            /* Send 481 Call Leg/Transaction Does Not Exist */
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_CALLEG,
+                                        SIP_CLI_ERR_CALLEG_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                fname, SIP_CLI_ERR_NOT_ALLOWED);
+            }
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+        }
+
+
+        /* + If this is a request and method is INVITE, obtain
+         *   the next available line and forward the request to
+         *   that state machine.  This is a new incoming call.
+         * + Otherwise, this message is spurious -- reject it.
+         *   Do not accept an incoming INVITE for a new call
+         *   if in quiet mode
+         */
+        is_previous_call_id = sip_sm_is_previous_call_id(pCallID,
+                                                         &previous_call_index);
+        if ((method == sipMethodInvite) && (is_request)) {
+            if (sip_mode_quiet) {
+                if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_NOT_AVAIL,
+                                            SIP_CLI_ERR_NOT_AVAIL_PHRASE, 0,
+                                            NULL, NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_NOT_AVAIL);
+                }
+                cpr_free(sipCseq);
+                free_sip_message(pSipMessage);
+                return;
+            }
+            else if (cprGetDepth(gsm_msgq) > MAX_DEPTH_OF_GSM_MSGQ_TO_ALLOW_NEW_INCOMING_CALL) {
+               /*
+                * CSCsz33584
+                * if gsm_msgq depth is larger than MAX_DEPTH_OF_GSM_MSGQ_TO_ALLOW_NEW_INCOMING_CALL,
+                * it's risky to accept new incoming call.
+                * just ignore the INVITE message to save CPU time so that the messages in gsm_msgq can be processed faster.
+                */
+               CCSIP_DEBUG_ERROR(DEB_F_PREFIX"gsm msgq depth too large, drop incoming INVITEs!!!\n",
+                                     DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+                cpr_free(sipCseq);
+                free_sip_message(pSipMessage);
+                return;
+            }
+
+            ccb = sip_sm_get_ccb_next_available(&line_index);
+            if (!ccb) {
+                /* All lines are busy.  Return 486 BUSY */
+                if (platGetPhraseText(STR_INDEX_NO_FREE_LINES,
+                                             (char *)tmp_str,
+                                             STATUS_LINE_MAX_LEN - 1) == CPR_SUCCESS) {
+                    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Line filter: Call ID match not "
+                                     "found: INVITE: %s\n Sending 486 BUSY\n",
+                                     DEB_F_PREFIX_ARGS(SIP_ID, fname), tmp_str);
+                }
+                if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BUSY_HERE,
+                                            SIP_CLI_ERR_BUSY_HERE_PHRASE, 0,
+                                            NULL, NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_BUSY_HERE);
+                }
+                cpr_free(sipCseq);
+                free_sip_message(pSipMessage);
+                return;
+            }
+            sip_sm_event.ccb = ccb;
+
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Line filter: Call ID match not "
+                             "found: INVITE: free ccb index = %d.\n",
+                             DEB_F_PREFIX_ARGS(SIP_ID, fname), line_index);
+
+        } else if (is_previous_call_id && (method != sipMethodAck) &&
+                   is_request) {
+            /*
+             * Detect whether the message is a non-ACK request addressing the
+             * previous call. If so, reTx the stored response.
+             */
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Line filter: Previous Call ID.\n",
+                DEB_F_PREFIX_ARGS(SIP_ID, fname));
+            if (SipRelDevEnabled) {
+                if (SIPTaskRetransmitPreviousResponse(pSipMessage, fname,
+                            pCallID, sipCseq, 0, TRUE) == SIP_OK) {
+                    cpr_free(sipCseq);
+                    free_sip_message(pSipMessage);
+                    return;
+                }
+            } else {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Line filter: Previous Call ID. "
+                                 "Reliable Delivery is OFF.\n", DEB_F_PREFIX_ARGS(SIP_ID, fname));
+            }
+
+            if (method == sipMethodBye) {
+                (void) sipSPISendErrorResponse(pSipMessage, 200,
+                                               SIP_SUCCESS_SETUP_PHRASE,
+                                               0, NULL, NULL);
+            } else {
+                (void) sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_CALLEG,
+                                               SIP_CLI_ERR_CALLEG_PHRASE,
+                                               0, NULL, NULL);
+            }
+
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+        } else if (is_previous_call_id && (method == sipMethodAck) &&
+                   is_request) {
+            /*
+             * Detect whether the message is an ACK addressing the previous
+             * call. If so, stop any outstanding reTx timers.
+             */
+            if (SipRelDevEnabled) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Stopping any outstanding "
+                                 "reTx timers...\n", DEB_F_PREFIX_ARGS(SIP_TIMER, fname));
+                sip_sm_check_retx_timers(sip_sm_get_ccb_by_index(previous_call_index),
+                                         pSipMessage);
+            }
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Not forwarding response to SIP SM.\n",
+                DEB_F_PREFIX_ARGS(SIP_FWD, fname));
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+        } else if (is_previous_call_id && (!is_request)) {
+            /*
+             * Detect whether the message is a response addressing the previous
+             * call. If the request is 200 OK, stop any outstanding reTx timers.
+             * This will prevent extraneous reTx of BYE/CANCEL messages at the
+             * the end of the call.
+             */
+            if (SipRelDevEnabled) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Stopping any outstanding "
+                                 "reTx timers...\n", DEB_F_PREFIX_ARGS(SIP_TIMER, fname));
+                sip_sm_check_retx_timers(sip_sm_get_ccb_by_index(previous_call_index),
+                                         pSipMessage);
+                // Check for stored responses
+                if (SIPTaskRetransmitPreviousResponse(pSipMessage, fname,
+                                                      pCallID, sipCseq,
+                                                      response_code, FALSE) == SIP_OK) {
+                    cpr_free(sipCseq);
+                    free_sip_message(pSipMessage);
+                    return;
+                }
+            }
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Not forwarding response to SIP SM.\n",
+                DEB_F_PREFIX_ARGS(SIP_FWD, fname));
+
+            if (method == sipMethodBye) {
+                SIPTaskProcessSIPPreviousCallByeResponse(pSipMessage,
+                                                         response_code,
+                                                         previous_call_index);
+            }
+            if (method == sipMethodInvite) {
+                SIPTaskProcessSIPPreviousCallInviteResponse(pSipMessage,
+                                                            response_code,
+                                                            previous_call_index);
+            }
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+
+        } else {
+            if (method == sipMethodRefer) {
+                if (is_request) {
+                        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received OOD Refer.\n",
+                            DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+                        if (subsmanager_handle_ev_sip_subscribe(pSipMessage, sipMethodRefer, FALSE) != SIP_ERROR) {
+                            // Successfully handled
+                            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Successfully handled OOD Refer.\n",
+                                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+                        } else {
+                            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Not able to handle OOD Refer.\n",
+                                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+                        }
+                } else {
+                    // This is a response to an OOD REFER generated via sub/not
+                    // interface
+                    scbp = find_scb_by_callid(pCallID, &scb_index);
+                    if (scbp != NULL) {
+                        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv Refer Response\n",
+                            DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+                        (void) subsmanager_handle_ev_sip_response(pSipMessage);
+                    }
+                }
+            } else {
+                if (SipRelDevEnabled) {
+                    // Check for stored responses
+                    if (SIPTaskRetransmitPreviousResponse(pSipMessage, fname,
+                                                          pCallID, sipCseq,
+                                                          response_code,
+                                                          FALSE) == SIP_OK) {
+                        cpr_free(sipCseq);
+                        free_sip_message(pSipMessage);
+                        return;
+                    }
+                }
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Line filter: Call ID match not "
+                                 "found: Rejecting.\n", DEB_F_PREFIX_ARGS(SIP_ID, fname));
+                if (is_request && (method != sipMethodAck)) {
+                    /* Send 481 error */
+                    if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_CALLEG,
+                                                SIP_CLI_ERR_CALLEG_PHRASE,
+                                                0, NULL, NULL) != TRUE) {
+                        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                          fname, SIP_CLI_ERR_CALLEG);
+                    }
+                }
+            }
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+        }
+    }
+
+    if (is_request) {
+        /*
+         * Process request
+         */
+        requestStatus = sipSPICheckRequest(ccb, pSipMessage);
+        switch (requestStatus) {
+        case SIP_MESSAGING_OK:
+            break;
+
+        case SIP_MESSAGING_NEW_CALLID:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPICheckRequest() returned "
+                              "SIP_MESSAGING_NEW_CALLID.\n", fname);
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+
+        case SIP_CLI_ERR_FORBIDDEN:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPICheckRequest() returned "
+                              "SIP_CLI_ERR_FORBIDDEN.\n", fname);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_FORBIDDEN,
+                                        SIP_CLI_ERR_FORBIDDEN_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_FORBIDDEN);
+            }
+
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+
+        case SIP_MESSAGING_DUPLICATE:
+            if (method == sipMethodInvite) {
+                char test_mac[MAC_ADDR_STR_LENGTH];
+                uint8_t mac_addr[MAC_ADDRESS_LENGTH];
+
+                platform_get_wired_mac_address(mac_addr);
+
+                /*
+                 * See if the call id has our mac address in it.
+                 * If so, then we called ourselves. Report back busy.
+                 */
+                snprintf(test_mac, MAC_ADDR_STR_LENGTH, "%.4x%.4x-%.4x",
+                         mac_addr[0] * 256 + mac_addr[1],
+                         mac_addr[2] * 256 + mac_addr[3],
+                         mac_addr[4] * 256 + mac_addr[5]);
+                if ((ccb->state == SIP_STATE_SENT_INVITE) &&
+                    (strncmp(test_mac, ccb->sipCallID, 13) == 0)) {
+                    get_sip_error_string(errortext, SIP_CLI_ERR_BUSY_HERE);
+                    (void) sipSPISendErrorResponse(pSipMessage,
+                                                   SIP_CLI_ERR_BUSY_HERE,
+                                                   errortext, 0, NULL, NULL);
+                }
+            }
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+
+        case SIP_SERV_ERR_INTERNAL:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPICheckRequest() returned "
+                              "SIP_SERV_ERR_INTERNAL.\n", fname);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_SERV_ERR_INTERNAL,
+                                        SIP_SERV_ERR_INTERNAL_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_SERV_ERR_INTERNAL);
+            }
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+
+        case SIP_CLI_ERR_BAD_REQ:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPICheckRequest() returned "
+                              "SIP_CLI_ERR_BAD_REQ.\n", fname);
+            if (sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                        SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_CLI_ERR_BAD_REQ);
+            }
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+
+        case SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA:
+        default:
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPICheckRequest() "
+                              "returned error.\n", fname);
+            /* Send error response */
+            if (method != sipMethodAck) {
+                if (requestStatus == SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA) {
+                    requestStatus = SIP_CLI_ERR_MEDIA;
+                } else if (requestStatus == SIP_MESSAGING_ENDPOINT_NOT_FOUND) {
+                    requestStatus = SIP_CLI_ERR_NOT_FOUND;
+                } else if (requestStatus == SIP_MESSAGING_NOT_ACCEPTABLE) {
+                    requestStatus = SIP_CLI_ERR_NOT_ACCEPT;
+                } else if (requestStatus != SIP_CLI_ERR_CALLEG) {
+                    requestStatus = 400;
+                }
+                get_sip_error_string(errortext, requestStatus);
+                if (sipSPISendErrorResponse(pSipMessage, (uint16_t)requestStatus,
+                                            errortext, 0, NULL, NULL) != TRUE) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                      fname, SIP_CLI_ERR_BAD_REQ);
+                }
+                if (method == sipMethodInvite) {
+                    ccb->wait_for_ack = TRUE;
+                }
+            }
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+        }
+
+        switch (method) {
+        case sipMethodInvite:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_INVITE);
+            sip_sm_event.type = E_SIP_INVITE;
+            break;
+
+        case sipMethodAck:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_ACK);
+            sip_sm_check_retx_timers(ccb, pSipMessage);
+            sip_sm_event.type = E_SIP_ACK;
+            break;
+
+        case sipMethodBye:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_BYE);
+            sip_sm_event.type = E_SIP_BYE;
+            break;
+
+        case sipMethodCancel:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_CANCEL);
+            sip_sm_event.type = E_SIP_CANCEL;
+            break;
+
+        case sipMethodSubscribe:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_SUBSCRIBE);
+            sip_sm_event.type = E_SIP_SUBSCRIBE;
+            break;
+
+        case sipMethodNotify:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_NOTIFY);
+            sip_sm_event.type = E_SIP_NOTIFY;
+            break;
+
+        case sipMethodRefer:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_REFER);
+            sip_sm_event.type = E_SIP_REFER;
+            break;
+        case sipMethodOptions:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_OPTIONS);
+            sip_sm_event.type = E_SIP_OPTIONS;
+            break;
+        case sipMethodUpdate:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_UPDATE);
+            sip_sm_event.type = E_SIP_UPDATE;
+            break;
+        case sipMethodInfo:
+            CCSIP_DEBUG_TASK(get_debug_string(DEBUG_SIP_MSG_RECV),
+                             fname, SIP_METHOD_INFO);
+            (void) ccsip_handle_info_package(ccb, pSipMessage);
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+            break;
+        default:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received unknown SIP request message.\n",
+                             DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            /* The message must be deallocated here */
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+        }
+    } else {
+        int responseStatus = SIP_MESSAGING_ERROR;
+
+        /*
+         * Process response
+         */
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Received SIP response.\n",
+            DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+        responseStatus = sipSPICheckResponse(ccb, pSipMessage);
+        if (responseStatus != SIP_MESSAGING_OK) {
+            if (responseStatus == SIP_MESSAGING_DUPLICATE) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Duplicate response detected. "
+                                 "Discarding...\n", DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            } else if (responseStatus == SIP_MESSAGING_ERROR_STALE_RESP) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"Stale response detected. "
+                                 "Discarding...\n", DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            } else if (responseStatus == SIP_MESSAGING_ERROR_NO_TRX) {
+                // This may be a response for a request generated via sub/not i/f
+                scbp = find_scb_by_callid(pCallID, &scb_index);
+                if (scbp != NULL) {
+                    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv Refer Response.\n",
+                        DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+                    (void) subsmanager_handle_ev_sip_response(pSipMessage);
+                }
+            } else {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPICheckResponse() "
+                                  "returned error. Discarding response\n",
+                                  fname);
+            }
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+        }
+
+        /* Response is ok.  Cancel the outstanding reTx timer if any */
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Stopping any outstanding reTx "
+                         "timers...\n", DEB_F_PREFIX_ARGS(SIP_TIMER, fname));
+        sip_sm_check_retx_timers(ccb, pSipMessage);
+
+        /* got a response, re-set re-transmit flag */
+        ccb->retx_flag = FALSE;
+        ccb->last_recvd_response_code = response_code;
+        /*
+         * TEL_CCB_START equates to zero and line_t is an unsigned type,
+         * so just check the end condition
+         */
+        if (ccb->index <= TEL_CCB_END) {
+            gCallHistory[ccb->index].last_rspcode_rcvd = code_class;
+        }
+
+        switch (code_class) {
+        case codeClass1xx:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv 1xx message.\n",
+                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            sip_sm_event.type = E_SIP_1xx;
+            break;
+
+        case codeClass2xx:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv 2xx message.\n",
+                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            sip_sm_event.type = E_SIP_2xx;
+            break;
+
+        case codeClass3xx:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv 3xx message.\n",
+                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            sip_sm_event.type = E_SIP_3xx;
+            break;
+
+        case codeClass4xx:
+        case codeClass5xx:
+        case codeClass6xx:
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv 4xx/5xx/6xx message.\n",
+                DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname));
+            sip_sm_event.type = E_SIP_FAILURE_RESPONSE;
+            switch (response_code) {
+            case SIP_CLI_ERR_UNAUTH:
+            case SIP_CLI_ERR_PROXY_REQD:
+                if (sipCseq->method == sipMethodAck) {
+                    sipSPISendFailureResponseAck(ccb, pSipMessage, FALSE, 0);
+                    cpr_free(sipCseq);
+                    free_sip_message(pSipMessage);
+                    return;
+                }
+                break;
+
+            default:
+                break;
+            }
+            break;
+
+        default:
+            /* unknown response, keep re-transmitting */
+            ccb->retx_flag = TRUE;
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unknown response class.\n", fname);
+            cpr_free(sipCseq);
+            free_sip_message(pSipMessage);
+            return;
+        }
+        // Change the event type for the UPDATE method since there is only one
+        // handler for this
+        if (method == sipMethodUpdate) {
+            sip_sm_event.type = E_SIP_UPDATE_RESPONSE;
+         }
+    }
+
+    /*
+     * Send event to the SIP SM or the SIP REGISTRATION SM
+     */
+    if (sip_sm_event.ccb->type == SIP_REG_CCB) {
+        sip_sm_event.type = (sipSMEventType_t)
+            ccsip_register_sip2sipreg_event(sip_sm_event.type);
+
+        if ((!is_request) && ((code_class == codeClass5xx) ||
+            (code_class == codeClass6xx))) {
+            sip_sm_event.type = (sipSMEventType_t) E_SIP_REG_FAILURE_RESPONSE;
+        }
+
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Register response does not have a body %d\n",
+        DEB_F_PREFIX_ARGS(SIP_MSG_RECV, fname),
+        (pSipMessage->mesg_body[0].msgBody == NULL) ? -1: pSipMessage->mesg_body[0].msgContentTypeValue);
+
+        if (sip_sm_event.type != (int) E_SIP_REG_NONE) {
+            if (sip_reg_sm_process_event(&sip_sm_event) < 0) {
+                CCSIP_DEBUG_ERROR(get_debug_string(REG_SM_PROCESS_EVENT_ERROR), fname, sip_sm_event.type);
+                if (is_request && (method != sipMethodAck)) {
+                    /* Send 500 error */
+                    if (sipSPISendErrorResponse(pSipMessage, 500,
+                                                SIP_SERV_ERR_INTERNAL_PHRASE, 0,
+                                                NULL, NULL) != TRUE) {
+                        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                          fname, SIP_SERV_ERR_INTERNAL);
+                    }
+                }
+                cpr_free(sipCseq);
+                free_sip_message(pSipMessage);
+                return;
+            } else {
+                cpr_free(sipCseq);
+                return;
+            }
+        }
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Ignoring non-register event= %d\n",
+                         DEB_F_PREFIX_ARGS(SIP_EVT, fname), sip_sm_event.type);
+        cpr_free(sipCseq);
+        free_sip_message(pSipMessage);
+        return;
+    }
+
+    if (sip_sm_process_event(&sip_sm_event) < 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_sm_process_event() returned "
+                          "error.\n", fname);
+        if (is_request && (method != sipMethodAck)) {
+            /* Send 500 error */
+            if (sipSPISendErrorResponse(pSipMessage, 500,
+                                        SIP_SERV_ERR_INTERNAL_PHRASE, 0,
+                                        NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_SERV_ERR_INTERNAL);
+            }
+        }
+        cpr_free(sipCseq);
+        free_sip_message(pSipMessage);
+        return;
+    }
+
+    cpr_free(sipCseq);
+}
+
+/**
+ *
+ * SIPTaskProcessConfigChangeNotify
+ *
+ * ???
+ *
+ * Parameters:   ???
+ *
+ * Return Value: zero(0)
+ *
+ */
+int
+SIPTaskProcessConfigChangeNotify (int32_t notify_type)
+{
+    static const char *fname = "SIPTaskProcessConfigChangeNotify";
+    int retval = 0;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Notify received type=%d\n",
+        DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname), notify_type);
+
+    if (notify_type & AA_RELOAD) {
+        if ((PHNGetState() == STATE_CONNECTED) ||
+            (PHNGetState() == STATE_UNPROVISIONED)) {
+            /*
+             * If the phone state isn't in STATE_CONNECTED then this change
+             * notify is being called because of bootup and not just a SIP
+             * menu configuration hang.  We only want to handle updates
+             * to the number of lines if the phone is already connected,
+             * the other case, booting, is handled by the boot code
+             */
+            (void) sipTransportInit();
+
+            /* need to unregister the phone */
+            ccsip_register_cancel(FALSE, TRUE);
+            ccsip_register_reset_proxy();
+            (void) sip_platform_ui_restart();
+        } else {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"PHNGetState() is not in STATE_CONNECTED, "
+                              "bypassing restart\n", fname);
+        }
+    } else if (notify_type & AA_REGISTER) {
+        ccsip_register_commit();
+    } else if (notify_type & AA_BU_REG) {
+        (void) sipTransportInit();
+        ccsip_backup_register_commit();
+    }
+
+    return (retval);
+}
+
+
+
+/**
+ *
+ * SIPTaskProcessSIPNotify
+ *
+ * The function processes the unsolicited NOTIFY.
+ *
+ * Parameters:  pSipMessage - pointer to sipMessage_t.
+ *
+ * Return Value: SIP_OK, SIP_ERROR, SIP_DEFER.
+ *
+ * Note: The parameter pSipMessage is expected by the caller to be
+ *       preserved i.e. it can not be freed by this
+ *       function or the functions called by this function.
+ */
+static int
+SIPTaskProcessSIPNotify (sipMessage_t *pSipMessage)
+{
+    static const char *fname = "SIPTaskProcessSIPNotify";
+    sipReqLine_t   *requestURI = NULL;
+    char           *pRequestURIUserStr = NULL;
+    boolean         request_uri_error = TRUE;
+    line_t          i = 0;
+    line_t          dn_line = 0;
+    const char     *event = NULL;
+    sipLocation_t  *uri_loc = NULL;
+    char            line_name[MAX_LINE_NAME_SIZE];
+    char            line_contact[MAX_LINE_CONTACT_SIZE];
+    const char     *to = NULL;
+    sipLocation_t  *to_loc = NULL;
+    sipUrl_t       *sipToUrl = NULL;
+    boolean         to_header_error = TRUE;
+    char           *pUser = NULL;
+
+
+    /*
+     * Parse the Event header
+     */
+    event = sippmh_get_header_val(pSipMessage, SIP_HEADER_EVENT,
+                                  SIP_C_HEADER_EVENT);
+
+    if (event == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Missing Event header\n", fname);
+        (void) sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       0, NULL, NULL);
+        return SIP_OK;
+    }
+
+    /*
+     * Find the destination DN
+     */
+    requestURI = sippmh_get_request_line(pSipMessage);
+    if (requestURI) {
+        if (requestURI->url) {
+            uri_loc = sippmh_parse_from_or_to(requestURI->url, TRUE);
+            if (uri_loc) {
+                if (uri_loc->genUrl->schema == URL_TYPE_SIP) {
+                    if (uri_loc->genUrl->u.sipUrl->user) {
+                        pRequestURIUserStr = uri_loc->genUrl->u.sipUrl->user;
+                        request_uri_error = FALSE;
+                    } else {
+                        sippmh_free_location(uri_loc);
+                        SIPPMH_FREE_REQUEST_LINE(requestURI);
+                    }
+                } else {
+                    sippmh_free_location(uri_loc);
+                    SIPPMH_FREE_REQUEST_LINE(requestURI);
+                }
+            } else {
+                SIPPMH_FREE_REQUEST_LINE(requestURI);
+            }
+        } else {
+            SIPPMH_FREE_REQUEST_LINE(requestURI);
+        }
+    }
+
+    if (request_uri_error) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"NOTIFY has error in Req-URI!\n", fname);
+        (void) sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       SIP_CLI_ERR_BAD_REQ_REQLINE_ERROR, NULL);
+        return SIP_OK;
+    }
+
+    if (cpr_strncasecmp(event, "refer", 5) == 0) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"NOTIFY: refer\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+        SIPTaskProcessSIPNotifyRefer(pSipMessage);
+        sippmh_free_location(uri_loc);
+        SIPPMH_FREE_REQUEST_LINE(requestURI);
+        return SIP_OK;
+    }
+
+    for (i = 1; i <= 1; i++) {
+        if (sip_config_check_line(i)) {
+            config_get_string((CFGID_LINE_NAME + i - 1), line_name, sizeof(line_name));
+            config_get_string((CFGID_LINE_CONTACT + i - 1), line_contact, sizeof(line_contact));
+            if ((sippmh_cmpURLStrings(pRequestURIUserStr, line_name, TRUE) == 0) ||
+                (sippmh_cmpURLStrings(pRequestURIUserStr, line_contact, TRUE) == 0)) {
+                dn_line = i;
+                break;
+            }
+        }
+    }
+
+    if (dn_line == 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"NOTIFY has unknown user in Req-URI!\n",
+                          fname);
+        (void) sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_NOT_FOUND,
+                                       SIP_CLI_ERR_NOT_FOUND_PHRASE,
+                                       0, NULL, NULL);
+        sippmh_free_location(uri_loc);
+        SIPPMH_FREE_REQUEST_LINE(requestURI);
+        return SIP_OK;
+    }
+
+    sippmh_free_location(uri_loc);
+    SIPPMH_FREE_REQUEST_LINE(requestURI);
+
+    /*
+     * Sanity check To header by parsing the header. Note it is not used, just sanitized.
+     */
+    to = sippmh_get_cached_header_val(pSipMessage, TO);
+    if (to) {
+        to_loc = sippmh_parse_from_or_to((char *)to, TRUE);
+        if (to_loc) {
+            if (to_loc->genUrl->schema == URL_TYPE_SIP) {
+                sipToUrl = to_loc->genUrl->u.sipUrl;
+                if (sipToUrl) {
+                    if (sipToUrl->user) {
+                        pUser = sippmh_parse_user(sipToUrl->user);
+                        if (pUser) {
+                            if (pUser[0] != '\0') {
+                                // To header is good.
+                                to_header_error = FALSE;
+                            }
+                            cpr_free(pUser);
+                        }
+                    }
+                }
+                sippmh_free_location(to_loc);
+            } else {
+                sippmh_free_location(to_loc);
+            }
+        }
+    }
+
+    if (to_header_error) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"NOTIFY has error in To header\n", fname);
+        (void) sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       SIP_WARN_MISC,
+                                       SIP_CLI_ERR_BAD_REQ_ToURL_ERROR, NULL);
+        return SIP_OK;
+    }
+
+    /*
+     * Request-URI and To header check out. Check Event to determine which
+     * Notify function to invoke.
+     */
+    if ((cpr_strcasecmp(event, "message-summary") == 0) ||
+        (cpr_strcasecmp(event, "simple-message-summary") == 0)) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"NOTIFY: MWI\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+        if (SIPTaskProcessSIPNotifyMWI(pSipMessage, dn_line) != 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad MWI NOTIFY!\n", fname);
+            (void) sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                           SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                           SIP_WARN_MISC,
+                                           "Bad MWI NOTIFY", NULL);
+            return SIP_OK;
+        }
+    } else if (cpr_strcasecmp(event, "check-sync") == 0) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"NOTIFY: check-sync\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+        SIPTaskProcessSIPNotifyCheckSync(pSipMessage);
+    } else if (cpr_strcasecmp(event, "service-control") == 0) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"NOTIFY: service-control\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+        return SIPTaskProcessSIPNotifyServiceControl(pSipMessage);
+    } else if (cpr_strcasecmp(event, SIP_EVENT_DIALOG) == 0) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"NOTIFY: %s\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname), SIP_EVENT_DIALOG);
+        return (2);
+    } else if (cpr_strcasecmp(event, SIP_EVENT_CONFIG) == 0) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"NOTIFY: %s\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname), SIP_EVENT_CONFIG);
+        return (2);
+    } else if (cpr_strcasecmp(event, SIP_EVENT_KPML) == 0) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"NOTIFY: %s\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname), SIP_EVENT_KPML);
+        return (2);
+    } else if (cpr_strcasecmp(event, SIP_EVENT_PRESENCE) == 0) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"NOTIFY: %s\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname), SIP_EVENT_PRESENCE);
+        return (2);
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unrecognized Event header\n", fname);
+        (void) sipSPISendErrorResponse(pSipMessage, SIP_CLI_ERR_BAD_REQ,
+                                       SIP_CLI_ERR_BAD_REQ_PHRASE,
+                                       0, NULL, NULL);
+    }
+
+    return SIP_OK;
+}
+
+/**
+ *
+ * SIPTaskProcessSIPNotifyMWI
+ *
+ * ???
+ *
+ * Parameters:   ???
+ *
+ * Return Value: SIP_OK or SIP_ERROR
+ *
+ */
+static int
+SIPTaskProcessSIPNotifyMWI (sipMessage_t* pSipMessage, line_t dn_line)
+{
+    sipMessageSummary_t mesgSummary;
+
+    if (!pSipMessage->mesg_body[0].msgBody ||
+        (pSipMessage->mesg_body[0].msgContentTypeValue != SIP_CONTENT_TYPE_MWI_VALUE &&
+        pSipMessage->mesg_body[0].msgContentTypeValue != SIP_CONTENT_TYPE_TEXT_PLAIN_VALUE)) {
+        return SIP_ERROR;
+    }
+
+    memset(&mesgSummary, 0, sizeof(sipMessageSummary_t));
+
+    if (sippmh_parse_message_summary(pSipMessage, &mesgSummary) < 0) {
+        return SIP_ERROR;
+    }
+
+    sip_cc_mwi(CC_NO_CALL_ID, dn_line, mesgSummary.mesg_waiting_on, mesgSummary.type,
+               mesgSummary.newCount, mesgSummary.oldCount, mesgSummary.hpNewCount, mesgSummary.hpOldCount);
+
+    /*
+     * Send 200 OK back
+     */
+    (void) sipSPISendErrorResponse(pSipMessage, 200, SIP_SUCCESS_SETUP_PHRASE,
+                            0, NULL, NULL);
+
+    return SIP_OK;
+}
+/**
+ *
+ * SIPTaskProcessSIPNotifyRefer
+ *
+ * The function processes NOTIFY refer.
+ *
+ * Parameters:   pSipMessage - pointer to the sipMessage_t.
+ *
+ * Return Value: None
+ *
+ */
+static void
+SIPTaskProcessSIPNotifyRefer (sipMessage_t *pSipMessage)
+{
+    static const char *fname = "SIPTaskProcessSIPNotifyRefer";
+    ccsipCCB_t *ccb = NULL;
+    const char *pCallID = NULL;
+    sipSCB_t   *scbp = NULL;
+    int         scb_index;
+
+    pCallID = sippmh_get_cached_header_val(pSipMessage, CALLID);
+    if (!pCallID) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Cannot obtain SIP Call ID.\n", fname);
+        return;
+    }
+
+    /*
+     * Determine which line this SIP message is for.
+     */
+    ccb = sip_sm_get_ccb_by_callid(pCallID);
+    if (ccb) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Line filter: Call ID match:  Destination line = "
+                         "<%d/%d>.\n", DEB_F_PREFIX_ARGS(SIP_ID, fname), ccb->index, ccb->dn_line);
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"ccb is NULL\n", fname);
+        // Check if this should be sent to the SM by looking up its callid
+        // If found, the we return from here
+        scbp = find_scb_by_callid(pCallID, &scb_index);
+        if (scbp != NULL) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Recv Notify.\n", DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+            (void) subsmanager_handle_ev_sip_subscribe_notify(pSipMessage);
+            return;
+        }
+    }
+    (void) sipSPISendErrorResponse(pSipMessage, 200, SIP_SUCCESS_SETUP_PHRASE,
+                                   0, NULL, NULL);
+
+    if (NULL != ccb) {
+        cc_feature_data_t data;
+
+        /* for some reason pingtel decided they were going to send
+         * Notify messages with call progression in the body. Let's
+         * just send the 200 back for these and not process them.
+         * We should only process a Notify with a final response
+         * in the body (ie. 200, 3xx, 4xx, 5xx, 6xx).
+         */
+        if (pSipMessage->mesg_body[0].msgBody == NULL) {
+            data.notify.cause = CC_CAUSE_OK;
+
+            if (fsmxfr_get_xcb_by_call_id(ccb->gsm_id) &&
+                (fsmxfr_get_xfr_type(ccb->gsm_id) == FSMXFR_TYPE_BLND_XFR)) {
+                data.notify.cause = CC_CAUSE_ERROR;
+                data.notify.method = CC_XFER_METHOD_REFER;
+            }
+        } else if ((strstr(pSipMessage->mesg_body[0].msgBody, "100")) ||
+                   (strstr(pSipMessage->mesg_body[0].msgBody, "180")) ||
+                   (strstr(pSipMessage->mesg_body[0].msgBody, "181")) ||
+                   (strstr(pSipMessage->mesg_body[0].msgBody, "182")) ||
+                   (strstr(pSipMessage->mesg_body[0].msgBody, "183"))) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"Ignoring Notify w/Progression\n",
+                              DEB_F_PREFIX_ARGS(SIP_NOTIFY, fname));
+            return;
+        } else if (strstr(pSipMessage->mesg_body[0].msgBody, "200")) {
+            data.notify.cause = CC_CAUSE_OK;
+            data.notify.method = CC_XFER_METHOD_REFER;
+        } else {
+            data.notify.cause = CC_CAUSE_ERROR;
+            data.notify.method = CC_XFER_METHOD_REFER;
+        }
+        data.notify.subscription = CC_SUBSCRIPTIONS_XFER;
+        data.notify.blind_xferror_gsm_id = 0;
+        sip_cc_feature(ccb->gsm_id, ccb->dn_line, CC_FEATURE_NOTIFY,
+                       (void *)&data);
+    }
+}
+
+
+/**
+ *
+ * SIPTaskProcessSIPNotifyCheckSync
+ *
+ * ???
+ *
+ * Parameters:   pSipMessage -
+ *
+ * Return Value: None
+ *
+ */
+static void
+SIPTaskProcessSIPNotifyCheckSync (sipMessage_t *pSipMessage)
+{
+    /*
+     * Send 200 OK back
+     */
+    (void) sipSPISendErrorResponse(pSipMessage, 200, SIP_SUCCESS_SETUP_PHRASE,
+                                   0, NULL, NULL);
+}
+
+/**
+ *
+ * SIPTaskProcessSIPNotifyServiceControl
+ *
+ * Handles an incoming unsolicited NOTIFY service control message
+ *
+ * Parameters:   Incoming pSipMessage
+ *
+ * Return Value: SIP_OK if request processed.
+ *               SIP_DEFER if request is deferred for processing
+ *               by ccsip_core.c.
+ *
+ */
+static int
+SIPTaskProcessSIPNotifyServiceControl (sipMessage_t *pSipMessage)
+{
+    const char *fname = "SIPTaskProcessSIPNotifyServiceControl";
+    sipServiceControl_t *scp;
+    int rc = SIP_OK;
+
+    scp = ccsip_get_notify_service_control(pSipMessage);
+
+    if (scp != NULL) {
+        // The platform code should not alter scp or its fields
+        if (scp->action == SERVICE_CONTROL_ACTION_CALL_PRESERVATION) {
+            rc = SIP_DEFER;
+        } else {
+            if (sipSPISendErrorResponse(pSipMessage, 200,
+                                        SIP_SUCCESS_SETUP_PHRASE,
+                                        0, NULL, NULL) != TRUE) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
+                                  fname, SIP_SUCCESS_SETUP);
+            }
+
+            // Hand over the event to platform
+            sip_platform_handle_service_control_notify(scp);
+        }
+        sippmh_free_service_control_info(scp);
+    }
+
+    return rc;
+}
+
+/**
+ *
+ * SIPTaskProcessSIPPreviousCallByeResponse
+ *
+ * ???
+ *
+ * Parameters:   pResponse           -
+ *               response_code       -
+ *               previous_call_index -
+ *
+ * Return Value: None
+ *
+ */
+static void
+SIPTaskProcessSIPPreviousCallByeResponse (sipMessage_t *pResponse,
+                                          int response_code,
+                                          line_t previous_call_index)
+{
+    static const char *fname = "SIPTaskProcessSIPPreviousCallByeResponse";
+    uint32_t        responseCSeqNumber = 0;
+    sipMethod_t     responseCSeqMethod = sipMethodInvalid;
+    boolean         bad_authentication = FALSE;
+    credentials_t   credentials;
+    sipAuthenticate_t authen;
+    const char     *authenticate = NULL;
+    int             nc_count = 0;
+
+    memset(&authen, 0, sizeof(authen)); // Initialize
+
+    switch (response_code) {
+    case SIP_CLI_ERR_UNAUTH:
+    case SIP_CLI_ERR_PROXY_REQD:
+        /* Check CSeq */
+        if (sipGetMessageCSeq(pResponse, &responseCSeqNumber,
+                    &responseCSeqMethod) < 0) {
+            return;
+        }
+        if (responseCSeqNumber !=
+                gCallHistory[previous_call_index].last_bye_cseq_number) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"CSeq# mismatch: BYE CSeq=%d, "
+                              "%d CSeq:%d\n", fname,
+                              gCallHistory[previous_call_index].last_bye_cseq_number,
+                              response_code, responseCSeqNumber);
+            return;
+        }
+
+        /* Obtain credentials */
+        cred_get_line_credentials(gCallHistory[previous_call_index].dn_line,
+                                  &credentials,
+                                  sizeof(credentials.id),
+                                  sizeof(credentials.pw));
+        /* Compute Authentication information */
+        authenticate = sippmh_get_header_val(pResponse,
+                                             AUTH_HDR(response_code), NULL);
+        if (authenticate != NULL) {
+            sip_authen_t *sip_authen = NULL;
+
+            sip_authen = sippmh_parse_authenticate(authenticate);
+            if (sip_authen) {
+                char *author_str = NULL;
+
+                if (sipSPIGenerateAuthorizationResponse(sip_authen,
+                                                        gCallHistory[previous_call_index].
+                                                        last_route_request_uri,
+                                                        SIP_METHOD_BYE,
+                                                        credentials.id,
+                                                        credentials.pw,
+                                                        &author_str,
+                                                        &nc_count, NULL)) {
+                    if (author_str != NULL)
+                    {
+                        authen.authorization = (char *)
+                            cpr_malloc(strlen(author_str) * sizeof(char) + 1);
+                        if (authen.authorization != NULL) {
+                            sstrncpy(authen.authorization, author_str,
+                                     strlen(author_str) * sizeof(char) + 1);
+                            authen.status_code = response_code;
+                        } else {
+                            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc() failed "
+                                              "for authen.authorization\n", fname);
+                            bad_authentication = TRUE;
+                        }
+                        cpr_free(author_str);
+                    } else {
+                        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"author_str returned by sipSPIGenerateAuthorizationResponse"
+                                          "is NULL", fname);
+                        bad_authentication = TRUE;
+                    }
+                } else {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sipSPIGenerateAuthorizationResponse()"
+                                      " returned null.\n", fname);
+                    bad_authentication = TRUE;
+                }
+                sippmh_free_authen(sip_authen);
+            } else {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sippmh_parse_authenticate() "
+                                  "returned null.\n", fname);
+                bad_authentication = TRUE;
+            }
+        } else {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sippmh_get_header_val(AUTH_HDR) "
+                              "returned null.\n", fname);
+            bad_authentication = TRUE;
+        }
+
+        if (bad_authentication) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad authentication header "
+                              "in %d\n", fname, response_code);
+            if (authen.authorization)
+                cpr_free(authen.authorization);
+            return;
+        }
+
+        /* Resend BYE with authorization */
+        (void) sipSPISendByeAuth(pResponse,
+                                 authen,
+                                 &(gCallHistory[previous_call_index].last_bye_dest_ipaddr),
+                                 gCallHistory[previous_call_index].last_bye_dest_port,
+                                 ++gCallHistory[previous_call_index].last_bye_cseq_number,
+                                 gCallHistory[previous_call_index].last_bye_also_string,
+                                 gCallHistory[previous_call_index].last_route,
+                                 gCallHistory[previous_call_index].last_route_request_uri,
+                                 previous_call_index);
+        if (authen.authorization) {
+            cpr_free(authen.authorization);
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+/**
+ *
+ * SIPTaskProcessSIPPreviousCallInviteResponse
+ *
+ * ???
+ *
+ * Parameters:   pResponse           -
+ *               response_code       -
+ *               previous_call_index -
+ *
+ * Return Value: None
+ *
+ */
+static void
+SIPTaskProcessSIPPreviousCallInviteResponse (sipMessage_t *pResponse,
+                                             int response_code,
+                                             line_t previous_call_index)
+{
+    static const char *fname = "SIPTaskProcessSIPPreviousCallInviteResponse";
+    uint32_t responseCSeqNumber = 0;
+    sipMethod_t responseCSeqMethod = sipMethodInvalid;
+
+    if ((response_code >= SIP_CLI_ERR_BAD_REQ) && (response_code < 700)) {
+        /* Check CSeq */
+        if (sipGetMessageCSeq(pResponse, &responseCSeqNumber,
+                              &responseCSeqMethod) < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_SYSTEMCALL_FAILED),
+                              fname, "Invalid CSeq");
+            return;
+        }
+        if (responseCSeqNumber !=
+                gCallHistory[previous_call_index].last_bye_cseq_number) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Last Bye CSeq=%d, Failure Code = %d, "
+                              "CSeq:%d\n", fname,
+                              gCallHistory[previous_call_index].last_bye_cseq_number,
+                              response_code, responseCSeqNumber);
+            return;
+        }
+
+        /* Send ACK */
+        sipSPISendFailureResponseAck(NULL, pResponse, TRUE, previous_call_index);
+
+    }
+}
+
+void
+SIPTaskPostRestart (boolean restart)
+{
+    ccsip_restart_req *msg;
+    static const char fname[] = "SIPTaskPostRestart";
+
+    msg = (ccsip_restart_req *) SIPTaskGetBuffer(sizeof(ccsip_restart_req));
+    if (msg == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to allocate IPC msg ccip_restart_req\n", fname);
+        return;
+    }
+    if (restart) {
+        /* This is a restart request from the platform */
+        msg->cmd = SIP_RESTART_REQ_RESTART;
+    } else {
+        /* This is a re-init request from the platform */
+        msg->cmd = SIP_RESTART_REQ_REINIT;
+    }
+    /* send a restart message to the SIP Task */
+    if (SIPTaskSendMsg(SIP_RESTART, (cprBuffer_t)msg,
+                       sizeof(ccsip_restart_req), NULL) == CPR_FAILURE) {
+        cpr_free(msg);
+    }
+    return;
+}
+
+static void
+SIPTaskProcessRestart (ccsip_restart_cmd cmd)
+{
+    static const char fname[] = "SIPTaskProcessRestart";
+
+    if (cmd == SIP_RESTART_REQ_RESTART) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Starting Restart Process\n",
+            DEB_F_PREFIX_ARGS(SIP_TASK, fname));
+
+        /* Restart SIP task */
+        sip_restart();
+    } else if (cmd == SIP_RESTART_REQ_REINIT) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Starting Re-init Process\n",
+            DEB_F_PREFIX_ARGS(SIP_TASK, fname));
+
+        // Re-initialize the various components
+        SIPTaskReinitialize(FALSE);
+
+      /* Clear the quite mode */
+        sip_mode_quiet = FALSE;
+    }
+}
+
+/*
+ * If checkConfig is 0/FALSE, process as a config change without checking
+ */
+void
+SIPTaskReinitialize (boolean checkConfig)
+{
+    static const char fname[] = "SIPTaskReinitialize";
+
+    // Initialize all GSM modules
+    cc_fail_fallback_gsm(CC_SRC_SIP, CC_RSP_COMPLETE, CC_REG_FAILOVER_RSP);
+
+    // If the config has changed, or if the check is being bypassed, re-init reg-mgr
+    if ( !checkConfig || sip_regmgr_check_config_change() ) {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"Config change detected: Restarting\n",
+            DEB_F_PREFIX_ARGS(SIP_TASK, fname));
+        sip_regmgr_process_config_change();
+        return;
+    } else {
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"No config change detected\n",
+            DEB_F_PREFIX_ARGS(SIP_TASK, fname));
+    }
+}
+
+void
+SIPTaskPostShutdown (int action, int reason, const char *reasonInfo)
+{
+    ccsip_shutdown_req_t *msg;
+    static const char fname[] = "SIPTaskPostShutdown";
+
+    msg = (ccsip_shutdown_req_t *) SIPTaskGetBuffer(sizeof(ccsip_shutdown_req_t));
+    if (msg == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to allocate IPC msg SIP_SHUTDOWN_REQ_SHUT\n", fname);
+        return;
+    }
+    msg->cmd = SIP_SHUTDOWN_REQ_SHUT;
+    msg->action = action;
+    msg->reason = reason;
+    if (reasonInfo) {
+        sstrncpy(sipUnregisterReason, reasonInfo, MAX_SIP_REASON_LENGTH);
+    }
+
+    /* send a restart message to the SIP Task */
+    if (SIPTaskSendMsg(SIP_SHUTDOWN, (cprBuffer_t)msg,
+                       sizeof(ccsip_shutdown_req_t), NULL) == CPR_FAILURE) {
+        cpr_free(msg);
+    }
+    return;
+}
+
+static void
+SIPTaskProcessShutdown (int action, int reason)
+{
+    static const char fname[] = "SIPTaskProcessShutdown";
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Starting Shutdown Process\n", DEB_F_PREFIX_ARGS(SIP_TASK, fname));
+    sip_mode_quiet = TRUE;
+    /* Shutdown SIP components */
+    sip_shutdown_phase1(action, reason);
+}
+/*
+ *  Function: destroy_sip_thread
+ *  Description:  kill sip msgQ and sip thread
+ *  Parameters:   none
+ *  Returns: none
+ */
+void destroy_sip_thread()
+{
+    static const char fname[] = "destroy_sip_thread";
+    DEF_DEBUG(DEB_F_PREFIX"Unloading SIP and destroying sip thread\n",
+        DEB_F_PREFIX_ARGS(SIP_CC_INIT, fname));
+
+    /* kill msgQ thread first, then itself */
+    (void) cprDestroyThread(sip_thread);
+}
diff --git a/libs/sipcc/core/sipstack/h/ccsip_callinfo.h b/libs/sipcc/core/sipstack/h/ccsip_callinfo.h
new file mode 100644 (file)
index 0000000..3619a89
--- /dev/null
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_CALLINFO_H_
+#define _CCSIP_CALLINFO_H_
+
+#include "cpr_types.h"
+#include "ccapi.h"
+#include "ccsip_core.h"
+
+#define MAX_SIP_HEADER_LENGTH  1024
+#define MAX_URI_LENGTH         256
+#define URN_REMOTECC           "urn:x-cisco-remotecc:"
+#define LAQUOT                 '<'
+#define RAQUOT                 '>'
+#define SPACE                  ' '
+#define TAB                    '\t'
+#define SEMI_COLON             ';'
+
+// Feature string that appear in the feature-urn.  For example in
+// <urn:x-cisco-remotecc:hold>, "hold" is the feature string.
+#define SIP_CI_HOLD_STR        "hold"
+#define SIP_CI_RESUME_STR      "resume"
+#define SIP_CI_BARGE_STR       "barge"
+#define SIP_CI_CBARGE_STR      "cbarge"
+#define SIP_CI_CALL_INFO_STR   "callinfo"
+#define SIP_CI_SILENT_STR      "silent"
+#define SIP_CI_COACHING_STR    "coaching"
+
+
+// Definitions relating to the hold-resume feature urn.
+#define SIP_CI_HOLD_REASON         "reason="
+#define SIP_CI_HOLD_REASON_XFER    "transfer"
+#define SIP_CI_HOLD_REASON_CONF    "conference"
+
+// Definitions relating to the callinfo feature urn.
+#define SIP_CI_SECURITY           "security="
+#define SIP_CI_SECURITY_UNKNOWN   "unknown"
+#define SIP_CI_SECURITY_NOT_AUTH  "NotAuthenticated"
+#define SIP_CI_SECURITY_AUTH      "Authenticated"
+#define SIP_CI_SECURITY_ENCRYPTED "Encrypted"
+#define SIP_CI_ORIENTATION        "orientation="
+#define SIP_CI_ORIENTATION_TO     "to"
+#define SIP_CI_ORIENTATION_FROM   "from"
+#define SIP_CI_POLICY             "policy="
+#define SIP_CI_POLICY_UNKNOWN     "unknown"
+#define SIP_CI_POLICY_CHAPERONE   "chaperone"
+#define SIP_CI_CALL_INSTANCE      "call-instance="
+#define SIP_CI_CTI_CALLID         "cti-callid="
+#define SIP_CI_UI_STATE           "ui-state="
+#define SIP_CI_UI_STATE_RINGOUT   "ringout"
+#define SIP_CI_UI_STATE_CONNECTED "connected"
+#define SIP_CI_PRIORITY           "priority="
+#define SIP_CI_PRIORITY_URGENT    "urgent"
+#define SIP_CI_PRIORITY_EMERGENCY "emergency"
+#define SIP_CI_UI_STATE_BUSY      "busy"
+#define SIP_CI_GCID               "gci="
+#define SIP_CI_DUSTINGCALL        "isDustingCall"
+
+// Definitions relating to a generic feature parm.
+#define SIP_CI_GENERIC            "purpose="
+#define SIP_CI_GENERIC_ICON       "icon"
+#define SIP_CI_GENERIC_INFO       "info"
+#define SIP_CI_GENERIC_CARD       "card"
+
+#define SKIP_LWS(p)            while (*p == SPACE || *p == TAB) { \
+                                      p++; \
+                               }
+#define SKIP_WHITE_SPACE(p)    while (*p == SPACE || *p == TAB || \
+                                      *p == '\n') { \
+                                      p++; \
+                               }
+
+/*
+ * Encoding function
+ */
+char* ccsip_encode_call_info_hdr(cc_call_info_t *call_info_p,
+                                 const char *misc_parms_p);
+
+void ccsip_free_call_info_header(cc_call_info_t *call_info_p);
+
+void ccsip_store_call_info(cc_call_info_t *call_info_p, ccsipCCB_t* ccb);
+void ccsip_process_call_info_header(sipMessage_t *request_p, ccsipCCB_t* ccb);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_cc.h b/libs/sipcc/core/sipstack/h/ccsip_cc.h
new file mode 100755 (executable)
index 0000000..be7ea68
--- /dev/null
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_CC_H_
+#define _CCSIP_CC_H_
+
+#include "ccapi.h"
+
+
+void sip_cc_setup(int gsm_call_id, int line, string_t calling_name,
+                  string_t calling_number, string_t alt_calling_number, boolean display_calling_number,
+                  string_t called_name, string_t called_number,
+                  boolean display_called_number, string_t orig_called_name,
+                  string_t orig_called_number, string_t last_redirect_name,
+                  string_t last_redirect_number, cc_call_type_e call_type,
+                  cc_alerting_type alert_info, vcm_ring_mode_t alerting_ring,
+                  vcm_tones_t alerting_tone, cc_call_info_t *call_info_p,
+                  boolean replaces, string_t recv_info_list, sipMessage_t *sip_msg);
+void sip_cc_setup_ack(int call_id, int line, cc_msgbody_info_t *msg_body);
+void sip_cc_proceeding(int gsm_call_id, int line);
+void sip_cc_alerting(int gsm_call_id, int line, sipMessage_t *sip_msg,
+                     int inband);
+void sip_cc_connected(int gsm_call_id, int line, string_t recv_info_list, sipMessage_t *sip_msg);
+void sip_cc_connected_ack(int gsm_call_id, int line, sipMessage_t *sip_msg);
+void sip_cc_release(int gsm_call_id, int line, cc_causes_t cause,
+                    const char *dialstring);
+void sip_cc_release_complete(int gsm_call_id, int line, cc_causes_t cause);
+void sip_cc_feature(int call_id, int line, int feature, void *data);
+void sip_cc_feature_ack(int call_id, int line, int feature, void *data,
+                        cc_causes_t cause);
+void sip_cc_mwi(int call_id, int line, boolean on, int type,
+                int newCount, int oldCount, int hpNewCount, int hpOldCount);
+void sip_cc_mv_msg_body_to_cc_msg(cc_msgbody_info_t *cc_msg,
+                                  sipMessage_t *sip_msg);
+boolean sip_cc_create_cc_msg_body_from_sip_msg(cc_msgbody_info_t *cc_msg,
+                                               sipMessage_t *sip_msg);
+void sip_cc_options(callid_t call_id, line_t line, sipMessage_t *pSipMessage);
+void sip_cc_audit(callid_t call_id, line_t line, boolean apply_ringout);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_common_cb.h b/libs/sipcc/core/sipstack/h/ccsip_common_cb.h
new file mode 100644 (file)
index 0000000..3547596
--- /dev/null
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_COMMON_CB_H_
+#define _CCSIP_COMMON_CB_H_
+
+#include "cpr_types.h"
+#include "phone_types.h"
+#include "ccapi.h"
+#include "dns_utils.h"
+#include "ccsip_platform.h"
+#include "ccsip_pmh.h"
+#include "ccsip_core.h"
+#include "xml_parser_defines.h"
+
+
+#define HOOK_STATUS_STR "hook status"
+#define BLF_SPEEDDIAL_STR "blf speed dial"
+
+typedef enum {
+    SUBNOT_CB,
+    PUBLISH_CB,
+    UNSOLICIT_NOTIFY_CB
+} ccsip_cb_type_e;
+
+
+typedef struct {
+    ccsip_cb_type_e         cb_type;
+    line_t                  dn_line;
+    cpr_ip_addr_t           dest_sip_addr;
+    uint32_t                dest_sip_port;  /* Destination port */
+    cpr_ip_addr_t           src_addr;       /* Source address */
+    uint32_t                local_port;     /* Source port */
+    srv_handle_t             SRVhandle;      /* handle for dns_gethostbysrv() */
+    unsigned int            retx_counter;
+    boolean                 retx_flag;
+    char                    sipCallID[MAX_SIP_CALL_ID];
+    sipAuthenticate_t       authen;
+    long                    orig_expiration;  /* Original time to expire */
+    long                    expires;          /* Running expiry timer */
+    ccsip_event_data_t     *event_data_p;
+    cc_subscriptions_t      event_type;     /* event type, such as presence */
+    cc_subscriptions_t      accept_type;     /* accept type, such as presence */
+} ccsip_common_cb_t;
+
+
+extern void ccsip_common_util_set_dest_ipaddr_port(ccsip_common_cb_t *cb_p);
+extern void ccsip_common_util_set_src_ipaddr(ccsip_common_cb_t *cb_p);
+extern void ccsip_common_util_set_retry_settings(ccsip_common_cb_t *cb_p, int *timeout_p);
+extern boolean ccsip_common_util_generate_auth(sipMessage_t *pSipMessage, ccsip_common_cb_t *cb_p,
+                                         const char *rsp_method, int response_code, char *uri);
+extern void ccsip_util_extract_user(char *url, char *user);
+extern void ccsip_util_get_from_entity(sipMessage_t *pSipMessage, char *entity);
+
+#endif //_CCSIP_COMMON_CB_H_
diff --git a/libs/sipcc/core/sipstack/h/ccsip_core.h b/libs/sipcc/core/sipstack/h/ccsip_core.h
new file mode 100644 (file)
index 0000000..39525b2
--- /dev/null
@@ -0,0 +1,866 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_CORE_H_
+#define _CCSIP_CORE_H_
+
+#include "cpr_types.h"
+#include "cpr_memory.h"
+#include "task.h"
+//#include "network.h"
+#include "ccapi.h"
+#include "ccsip_sdp.h"
+#include "ccsip_pmh.h"
+#include "config.h"
+#include "dns_utils.h"
+#include "ccsip_platform.h"
+#include "ccsip_platform_timers.h"
+#include "cc_constants.h"
+#include "sessionConstants.h"
+
+#define SIP_DEFER (-2)
+#define SIP_ERROR (-1)
+#define SIP_OK    (0)
+
+#define SIP_L_C_F_PREFIX "SIP : %d/%d : %s : " // requires 3 args: line_id, call_id, fname
+#define SIP_F_PREFIX "SIP : %s : " // requires 1 arg: fname
+
+#define SUPERVISION_DISCONNECT_TIMEOUT 32000
+#define SIP_WARNING_LENGTH 100
+
+#define RPID_DISABLED 0
+#define RPID_ENABLED  1
+
+#define MAX_INVITE_RETRY_ATTEMPTS      6
+#define MAX_NON_INVITE_RETRY_ATTEMPTS 10
+
+typedef enum {
+    SIP_STATE_NONE = -1,
+    SIP_STATE_BASE = 0,
+
+    SIP_STATE_IDLE = SIP_STATE_BASE,
+
+    SIP_STATE_SENT_INVITE,
+    SIP_STATE_SENT_INVITE_CONNECTED,
+
+    SIP_STATE_RECV_INVITE,
+    SIP_STATE_RECV_INVITE_PROCEEDING,
+    SIP_STATE_RECV_INVITE_ALERTING,
+    SIP_STATE_RECV_INVITE_CONNECTED,
+
+    SIP_STATE_ACTIVE,
+    SIP_STATE_SENT_MIDCALL_INVITE,
+    SIP_STATE_RECV_MIDCALL_INVITE_CCFEATUREACK_PENDING,
+    SIP_STATE_RECV_MIDCALL_INVITE_SIPACK_PENDING,
+
+    SIP_STATE_RELEASE,
+    SIP_STATE_BLIND_XFER_PENDING,
+    SIP_STATE_IDLE_MSG_TIMER_OUTSTANDING,
+
+    SIP_STATE_SENT_OOD_REFER,
+    SIP_STATE_RECV_UPDATEMEDIA_CCFEATUREACK_PENDING,
+
+    SIP_STATE_END = SIP_STATE_RECV_UPDATEMEDIA_CCFEATUREACK_PENDING
+} sipSMStateType_t;
+
+typedef enum
+{
+    SIP_REG_STATE_INV = -1,
+    SIP_REG_STATE_NONE = 0,
+    SIP_REG_STATE_BASE = 1,
+
+    SIP_REG_STATE_IDLE = SIP_REG_STATE_BASE,
+    SIP_REG_STATE_REGISTERING,
+    SIP_REG_STATE_REGISTERED,
+    SIP_REG_STATE_UNREGISTERING,
+    SIP_REG_STATE_IN_FALLBACK,
+    SIP_REG_STATE_STABILITY_CHECK,
+    SIP_REG_STATE_TOKEN_WAIT,
+    SIP_REG_STATE_END = SIP_REG_STATE_TOKEN_WAIT
+} sipRegSMStateType_t;
+
+typedef enum {
+    SIPSPI_EV_INVALID = -1,
+    SIPSPI_EV_BASE = 0,
+
+    E_SIP_INVITE = SIPSPI_EV_BASE,
+    E_SIP_ACK,
+    E_SIP_BYE,
+    E_SIP_CANCEL,
+    E_SIP_1xx,
+    E_SIP_2xx,
+    E_SIP_3xx,
+    E_SIP_NOTIFY,
+    E_SIP_FAILURE_RESPONSE,
+    E_SIP_REFER,
+    E_SIP_OPTIONS,
+    E_SIP_SUBSCRIBE,
+    E_SIP_UPDATE,
+
+    E_CC_SETUP,
+    E_CC_SETUP_ACK,
+    E_CC_PROCEEDING,
+    E_CC_ALERTING,
+    E_CC_CONNECTED,
+    E_CC_CONNECTED_ACK,
+    E_CC_RELEASE,
+    E_CC_RELEASE_COMPLETE,
+    E_CC_FEATURE,
+    E_CC_FEATURE_ACK,
+    E_CC_CAPABILITIES,
+    E_CC_CAPABILITIES_ACK,
+    E_CC_SUBSCRIBE,
+    E_CC_NOTIFY,
+    E_CC_INFO,
+
+    E_SIP_INV_EXPIRES_TIMER,
+    E_SIP_INV_LOCALEXPIRES_TIMER,
+    E_SIP_SUPERVISION_DISCONNECT_TIMER,
+    E_SIP_TIMER,
+    E_SIP_GLARE_AVOIDANCE_TIMER,
+
+    E_SIP_UPDATE_RESPONSE,
+    E_SIP_ICMP_UNREACHABLE,
+    SIPSPI_EV_END = E_SIP_ICMP_UNREACHABLE
+} sipSMEventType_t;
+
+typedef enum {
+    H_INVALID_EVENT = -1,
+    SIPSPI_EV_INDEX_BASE = 0,
+/*0*/H_IDLE_EV_SIP_INVITE = SIPSPI_EV_INDEX_BASE,                 /* ccsip_handle_idle_ev_sip_invite,                                     */
+/*1*/H_IDLE_EV_CC_SETUP,                                          /* ccsip_handle_idle_ev_cc_setup,                                       */
+/*2*/H_SENTINVITE_EV_SIP_1XX,                                     /* ccsip_handle_sentinvite_ev_sip_1xx,                                  */
+/*3*/H_SENTINVITE_EV_SIP_2XX,                                     /* ccsip_handle_sentinvite_ev_sip_2xx,                                  */
+/*4*/H_SENTINVITE_EV_SIP_FXX,                                     /* ccsip_handle_sentinvite_ev_sip_fxx,                                  */
+/*5*/H_DISCONNECT_LOCAL_EARLY,                                    /* ccsip_handle_disconnect_local_early,                                 */
+/*6*/H_DISCONNECT_REMOTE,                                         /* ccsip_handle_disconnect_remote,                                      */
+/*7*/H_SENTINVITECONNECTED_EV_CC_CONNECTED_ACK,                   /* ccsip_handle_sentinviteconnected_ev_cc_connected_ack,                */
+/*8*/H_DISCONNECT_LOCAL,                                          /* ccsip_handle_disconnect_local,                                       */
+/*9*/H_RECVINVITE_EV_CC_SETUP_ACK,                                /* ccsip_handle_recvinvite_ev_cc_setup_ack,                             */
+/*10*/H_RECVINVITE_EV_CC_PROCEEDING,                              /* ccsip_handle_recvinvite_ev_cc_proceeding,                            */
+/*11*/H_RECVINVITE_EV_CC_ALERTING,                                /* ccsip_handle_recvinvite_ev_cc_alerting,                              */
+/*12*/H_RECVINVITE_EV_CC_CONNECTED,                               /* ccsip_handle_recvinvite_ev_cc_connected,                             */
+/*13*/H_DISCONNECT_LOCAL_UNANSWERED,                              /* ccsip_handle_disconnect_local_unanswered,                            */
+/*14*/H_RECVINVITE_EV_SIP_ACK,                                    /* ccsip_handle_recvinvite_ev_sip_ack,                                  */
+/*15*/H_ACTIVE_EV_SIP_INVITE,                                     /* ccsip_handle_active_ev_sip_invite,                                   */
+/*16*/H_ACTIVE_EV_CC_FEATURE,                                     /* ccsip_handle_active_ev_cc_feature,                                   */
+/*17*/H_ACCEPT_2XX,                                               /* ccsip_handle_accept_2xx,                                             */
+/*18*/H_REFER_SIP_MESSAGE,                                        /* ccsip_handle_refer_sip_message,                                      */
+/*19*/H_ACTIVE_EV_CC_FEATURE_ACK,                                 /* ccsip_handle_active_ev_cc_feature_ack,                               */
+/*20*/H_SENTINVITE_MIDCALL_EV_SIP_2XX,                            /* ccsip_handle_sentinvite_midcall_ev_sip_2xx                           */
+/*21*/H_SENTINVITE_MIDCALL_EV_CC_FEATURE,                         /* ccsip_handle_sentinvite_midcall_ev_cc_feature                        */
+/*22*/H_RECVMIDCALLINVITE_CCFEATUREACKPENDING_EV_CC_FEATURE_ACK,  /* ccsip_handle_recvmidcallinvite_ccfeatureackpending_ev_cc_feature_ack */
+/*23*/H_RECVMIDCALLINVITE_SIPACKPENDING_EV_SIP_ACK,               /* ccsip_handle_recvmidcallinvite_sipackpending_ev_sip_ack,             */
+/*24*/H_DEFAULT_SIP_MESSAGE,                                      /* ccsip_handle_default_sip_message,                                    */
+/*25*/H_DEFAULT_SIP_RESPONSE,                                     /* ccsip_handle_default_sip_response,                                   */
+/*26*/H_DEFAULT,                                                  /* ccsip_handle_default,                                                */
+/*27*/H_SIP_INV_EXPIRES_TIMER,                                    /* ccsip_handle_disconnect_local_early,                                 */
+/*28*/H_SIP_OPTIONS,                                              /* ccsip_handle_answer_options_request,                                 */
+/*29*/H_SENTINVITE_EV_SIP_3XX,                                    /* ccsip_handle_sentinvite_ev_sip_3xx,                              */
+/*30*/H_RECV_ERR_EV_SIP_ACK,                                      /* ccsip_handle_recv_error_response_ev_sip_ack,*/
+/*31*/H_SENTBYE_EV_SIP_2XX,                                       /* ccsip_handle_sentbye_ev_sip_2xx,*/
+/*32*/H_SENTBYE_EV_SIP_1XX,                                       /* ccsip_handle_sentbye_ev_sip_1xx,*/
+/*33*/H_SENTBYE_EV_SIP_FXX,                                       /* ccsip_handle_sentbye_ev_sip_fxx,*/
+/*34*/H_SENTBYE_EV_SIP_INVITE,                                    /* ccsip_handle_sentbye_recvd_invite,*/
+/*35*/H_SENTBYE_SUPERVISION_DISCONNECT_TIMER,                     /* ccsip_handle_sendbye_ev_supervision_disconnect*/
+/*36*/H_RELEASE_COMPLETE,                                         /* ccsip_handle_release_complete, */
+/*37*/H_ACTIVE_2xx,                                               /* ccsip_handle_Active_2xx*/
+/*38*/H_BLIND_NOTIFY,                                             /* ccsip_handle_send_blind_notify, */
+/*39*/H_SENT_BLINDNTFY,                                           /* ccsip_handle_sentblindntfy_ev_sip_2xx, */
+/*40*/H_BYE_RELEASE,                                              /* ccsip_handle_release_ev_sip_bye, */
+/*41*/H_HANDLE_LOCALEXPIRES_TIMER,                                /* ccsip_handle_localexpires_timer */
+/*42*/H_DEFAULT_SIP_TIMER,                                        /* ccsip_handle_default_sip_timer */
+/*43*/H_EARLY_EV_SIP_UPDATE,                                      /* ccsip_handle_early_ev_sip_update */
+/*44*/H_EARLY_EV_SIP_UPDATE_RESPONSE,                             /* ccsip_handle_early_ev_sip_update_response */
+/*45*/H_EARLY_EV_CC_FEATURE,                                      /* ccsip_handle_early_ev_cc_feature */
+/*46*/H_EARLY_EV_CC_FEATURE_ACK,                                  /* ccsip_handle_early_ev_cc_feature_ack */
+/*47*/H_CONFIRM_EV_SIP_UPDATE,                                    /* ccsip_handle_active_ev_sip_update */
+/*48*/H_RECVUPDATEMEDIA_CCFEATUREACKPENDING_EV_CC_FEATURE_ACK,    /* ccsip_handle_recvupdatemedia_ccfeatureackpending_ev_cc_feature_ack    */
+/*49*/H_SIP_GLARE_AVOIDANCE_TIMER,                                /* ccsip_handle_timer_glare_avoidance */
+/*50*/H_RECVINVITE_SENTOK_NO_SIP_ACK,                             /* ccsip_handle_recvinvite_ev_expires_timer */
+/*51*/H_EV_SIP_UNSOLICITED_NOTIFY,                                /* ccsip_handle_unsolicited_notify */
+/*52*/H_RECVINVITE_EV_SIP_2XX,                                    /* ccsip_handle_recvinvite_ev_sip_2xx */
+/*53*/H_ICMP_UNREACHABLE,                                         /* ccsip_handle_icmp_unreachable */
+/*54*/H_DISCONNECT_MEDIA_CHANGE,                                  /* ccsip_handle_disconnect_media_change*/
+/*55*/H_DEFAULT_EV_CC_FEATURE,                                    /* ccsip_handle_default_ev_cc_feature */
+/*56*/H_DEFAULT_RECVREQ_ACK_PENDING_EV_CC_FEATURE,                /* ccsip_handle_default_recvreq_ack_pending_ev_cc_feature */
+/*57*/H_OOD_REFER_RESPONSE_EV_SIP_1xx,                            /* ccsip_handle_sent_ood_refer_ev_sip_1xx */
+/*58*/H_OOD_REFER_RESPONSE_EV_SIP_2xx,                            /* ccsip_handle_sent_ood_refer_ev_sip_2xx  */
+/*59*/H_OOD_REFER_RESPONSE_EV_SIP_fxx,                            /* ccsip_handle_sent_ood_refer_ev_sip_fxx  */
+/*60*/H_RELEASE_EV_CC_FEATURE,                                    /* ccsip_handle_release_ev_cc_feature  */
+/*61*/H_EV_CC_INFO,                                               /* ccsip_handle_ev_cc_info */
+/*62*/H_RELEASE_EV_RELEASE,                                       /* ccsip_handle_release_ev_release  */
+    SIPSPI_EV_INDEX_END = H_RELEASE_EV_RELEASE
+} sipSMAction_t;
+
+
+
+/*
+ * This structure defines TCP/UDP Connection
+ * Parameters.
+ * This is used during processing Contact
+ * and Route headers received in the SIP
+ * messages
+ */
+typedef enum {
+    SIP_SM_DIS_METHOD_BYE = 0,
+    SIP_SM_DIS_METHOD_CANCEL
+} sipSMDisMethod_t;
+
+typedef struct {
+    char       last_call_id[MAX_SIP_CALL_ID];
+    uint32_t   last_bye_cseq_number;
+    cpr_ip_addr_t   last_bye_dest_ipaddr;
+    uint16_t   last_bye_dest_port;
+    cpr_ip_addr_t   proxy_dest_ipaddr;
+    line_t     dn_line;
+    char       last_bye_also_string[MAX_SIP_URL_LENGTH];
+    char       last_route[MAX_SIP_URL_LENGTH];
+    char       last_route_request_uri[MAX_SIP_URL_LENGTH];
+    char       via_branch[VIA_BRANCH_LENGTH];
+    sipStatusCodeClass_t last_rspcode_rcvd;
+} sipCallHistory_t;
+
+typedef enum {
+    SIP_SM_NO_XFR = 0,
+    SIP_SM_BLND_XFR,
+    SIP_SM_ATTN_XFR
+} sipSMXfrType_t;
+
+typedef struct sipRedirectInfo_ {
+    sipContact_t *sipContact;  /* Contact header received in the 3xx */
+    uint16_t next_choice;      /* Index of next Contact location to use */
+} sipRedirectInfo_t;
+
+typedef struct {
+    int  retries_401_407;
+    int cred_type;
+    char *authorization;
+    int status_code;
+    sip_authen_t *sip_authen;
+    char cnonce[9];
+    int nc_count;
+    boolean new_flag;
+} sipAuthenticate_t;
+
+/* SIP REGISTER method info */
+typedef struct {
+    int registered;
+    int tmr_expire;
+    int act_time;
+    char proxy[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t  addr;
+    uint16_t port;
+    uint8_t  rereg_pending;
+} sipRegister_t;
+
+/* AVT payload info */
+typedef struct {
+    int payload_type; //TODO BLASBERG: uint8_t or uint16_t should be acceptable
+} sipAvtPayloadType_t;
+
+/* Identifies CCB type because registration & call control use the same CCBs */
+typedef enum {
+    SIP_NONE_CCB,
+    SIP_REG_CCB,
+    SIP_CALL_CCB
+} sipCCBTypes_t;
+
+/*
+ * Define the types of CC's
+ */
+typedef enum {
+    CC_CCM = CC_MODE_CCM,
+    CC_OTHER = CC_MODE_NONCCM,
+    MAX_CC_TYPES
+} CC_ID;
+
+/*
+ * Offer/answer state
+ */
+typedef enum {
+    OA_IDLE,
+    OA_OFFER_SENT,
+    OA_OFFER_RECEIVED,
+    OA_ANSWER_SENT,
+    OA_ANSWER_RECEIVED
+} sipOfferAnswerState;
+
+/* Indentifies how proxy selection is being done */
+typedef enum {
+    SIP_PROXY_DEFAULT,  /* Current selection is configured proxy */
+    SIP_PROXY_BACKUP,   /* Current selection is configured backup proxy */
+    SIP_PROXY_DO_NOT_CHANGE_MIDCALL /* Do not select a different proxy even on failure*/
+} sipCCBProxySelection;
+
+typedef struct
+{
+    union {
+        string_t sip_via_header; // For received requests
+        string_t sip_via_branch; // For sent requests
+    } u;
+    string_t    sip_via_sentby;
+    sipMethod_t cseq_method;
+    uint32_t    cseq_number;
+} sipTransaction_t;
+
+typedef struct
+{
+    char             sipCallID[MAX_SIP_CALL_ID];
+    callid_t         gsm_id;
+    callid_t         con_call_id;
+    callid_t         blind_xfer_call_id;
+
+    sipSMStateType_t state;
+    line_t           index;
+    line_t           dn_line;
+    boolean          hold_initiated;
+    uint32_t         retx_counter;
+    sipCCBTypes_t    type;
+
+    /*
+     * first_backup indicates whether or not the backup proxy has just been
+     * activated. After the first message is retransmitted, the flag will be
+     * reset to FALSE.
+     */
+    boolean              first_backup;
+    sipCCBProxySelection proxySelection;      /* Indicates how proxy selection is being done */
+    cpr_ip_addr_t        outBoundProxyAddr;   /* IP address of outbound proxy for this call */
+//    uint16_t             outBoundProxyPort;   /* Outbound proxy port for this call */
+    uint32_t             outBoundProxyPort;   /* Outbound proxy port for this call */
+
+    srv_handle_t          SRVhandle;           /* handle for dns_gethostbysrv() */
+    srv_handle_t          ObpSRVhandle;        /* SRVhandle for the outbound proxy */
+    int                  routeMode;           /* Current routemode set by UIMatchDialTemplate()  */
+    void                 *udpId;              /* handle to UDPApplIcmpHandler */
+
+    /*
+     * An INVITE/ACK/1xx/2xx can be accompanied with a Contact header.
+     * Future requests should go there. This field is used to store the
+     * parsed Contact header received with any of these messages.
+     */
+    sipContact_t *contact_info;
+
+    /* Refer To and refere by headers Needed for next generated call */
+
+    /*
+     * Any SIP request such as INVITE, BYE, CANCEL etc and INVITE 200 OK,
+     * 401 & 484 responses can have a Record-Route header. This field is
+     * used to store the parsed Record-Route header received in any of
+     * these messages.
+     */
+    sipRecordRoute_t *record_route_info;
+
+    string_t          calledDisplayedName;
+    string_t          callingNumber;
+    string_t          altCallingNumber;
+    string_t          callingDisplayName;
+    string_t          calledNumber;
+    boolean           displayCalledNumber;
+    boolean           displayCallingNumber;
+    uint16_t          calledNumberLen;
+    boolean           calledNumberFirstDigitDialed;
+
+    /*
+     * The following field encodes boolean bit flags (on or off) for
+     * loopback, inband_alerting, sip_tcp, incoming, added_to_table,
+     * do_call_history, rsvp_reserved, msgPassthru, sigoCall,
+     * sent_bye, sent_cancel, sent_bye_response, sent_3456xx, recd_456xx,
+     * harikiri, timed_out, sd_in_ack
+     */
+    uint32_t          flags;
+
+#define LOOPBACK            1
+#define INBAND_ALERTING     (1<<1)
+#define SIP_TCP             (1<<2)
+#define INCOMING            (1<<3)
+#define ADDED_TO_TABLE      (1<<4)
+#define DO_CALL_HISTORY     (1<<5)
+#define RSVP_RESERVED       (1<<6)
+#define SENT_BYE            (1<<7)
+#define SENT_CANCEL         (1<<8)
+#define RECD_BYE            (1<<9)
+#define SENT_3456XX         (1<<10)
+#define RECD_456XX          (1<<11)
+#define HARIKIRI            (1<<12)
+#define TIMED_OUT           (1<<13)
+#define SD_IN_ACK           (1<<14)
+#define MSG_PASSTHRU        (1<<15)
+#define SIGO_CALL           (1<<16)
+#define RECD_1xx            (1<<17)
+#define SEND_CANCEL         (1<<18)
+#define FINAL_NOTIFY        (1<<19)
+#define SENT_INVITE_REPLACE (1<<20)
+
+    /*
+     * SIP signalling channel info: source and destination
+     */
+    cpr_ip_addr_t       src_addr;            /* Source address */
+    cpr_ip_addr_t       dest_sip_addr;       /* Destination address */
+//    uint16_t            local_port;          /* Source port */
+//    uint16_t            dest_sip_port;       /* Destination port */
+    uint32_t            local_port;          /* Source port */
+    uint32_t            dest_sip_port;       /* Destination port */
+    int16_t             sip_socket_handle;
+
+    /*
+     * RTP/SDP
+     */
+      cc_msgbody_info_t local_msg_body; /* store local sent msg bodies */
+//    sipSdp_t          *src_sdp;
+//    ushort            src_port;
+//    sipSdp_t          *dest_sdp;
+//    uint16_t          dest_port;
+//    uint32_t          dest_addr;
+      char              *old_session_id;
+      char              *old_version_id;
+//    ushort            dest_sdp_media;
+//    boolean           rtp_rx_opened;
+//    boolean           rtp_tx_opened;
+//      cc_sdp_t          cc_sdp;
+
+    /*
+     * Headers
+     */
+    char             ReqURI[MAX_SIP_URL_LENGTH];   /* "Working" Req-URI */
+    string_t         ReqURIOriginal;  /* Original outgoing call Req-URI */
+    string_t         sip_from;
+    string_t         sip_to;
+    string_t         sip_to_tag;
+    string_t         sip_from_tag;
+    string_t         sip_contact;
+    string_t         sip_remote_party_id;
+    string_t         sip_reqby;
+    string_t         sip_require;
+    string_t         sip_unsupported;
+    char             *diversion[MAX_DIVERSION_HEADERS];
+#define MAX_REQ_OUTSTANDING 3
+    sipTransaction_t sent_request[MAX_REQ_OUTSTANDING];
+    sipTransaction_t recv_request[MAX_REQ_OUTSTANDING];
+    uint32_t         last_recv_request_cseq;
+    sipMethod_t      last_recv_request_cseq_method;
+    uint32_t         last_used_cseq;
+    uint32_t         last_recv_invite_cseq;
+    string_t         sip_referTo;
+    string_t         sip_referredBy;
+    string_t         referto;
+    string_t         sipxfercallid;
+    boolean          wastransferred;
+    boolean          blindtransferred;
+    unsigned int     xfer_status;
+    /* Store all of the parsed diversion header info */
+    sipDiversionInfo_t *div_info;
+
+    cc_call_type_e   call_type;
+
+    /* Store all of the parsed Remote-Party-ID headers */
+    sipRemotePartyIdInfo_t *rpid_info;
+    /* Shallow pointer to the "best" parsed Remote-Party-ID header */
+    sipRemotePartyId_t *best_rpid;
+
+    /* To save the Via headers on the INVITE */
+    sipMessage_t *last_request;
+
+    /*
+     * Features
+     */
+    sipSMXfrType_t xfr_inprogress;
+    cc_features_t  featuretype;
+
+    sipAvtPayloadType_t avt;
+
+    /*
+     * Registration/Authentication
+     */
+    sipRegister_t     reg;
+    sipAuthenticate_t authen;
+
+    /*
+     * Two fields that can be sent with the Refer-To header
+     */
+    char *refer_proxy_auth;
+#ifdef SIP_ACC_CONT
+    char *refer_acc_cont;
+#endif
+    /*
+    * This struct would be allocated when the call is actually redirected
+    * Idea is to save memory in the ccb for non-redirected calls.
+    */
+    sipRedirectInfo_t *redirect_info;
+
+    /*
+     * Enum of what was in the alert-info header.
+     */
+    cc_alerting_type alert_info;
+
+    /*
+     * Contents of incoming and outgoing call-info headers
+     */
+    cc_call_info_t *in_call_info;
+    cc_call_info_t *out_call_info;
+
+    /*
+     * Ringing/tone pattern to play
+     */
+    vcm_ring_mode_t alerting_ring;
+    vcm_tones_t alerting_tone;
+
+    /*
+     * Personal Directory
+     */
+    boolean call_entered_into_pd;
+    boolean wait_for_ack;
+    boolean send_delayed_bye;
+    boolean retx_flag;
+    boolean early_transfer;
+    boolean first_pass_3xx;
+    CC_ID cc_type;
+    void *cc_cfg_table_entry;
+    /*
+     * Contents of supported and required headers
+     */
+#define replaces_tag      1
+#define rel_tag           (1<<1)
+#define early_session_tag (1<<2)
+#define join_tag          (1<<3)
+#define path_tag          (1<<4)
+#define precondition_tag  (1<<5)
+#define pref_tag          (1<<6)
+#define privacy_tag       (1<<7)
+#define sec_agree_tag     (1<<8)
+#define timer_tag         (1<<9)
+#define norefersub_tag    (1<<10)
+#define cisco_callinfo_tag (1<< 11)
+#define cisco_srtp_fallback_tag (1<< 12)
+#define extended_refer_tag (1<<16)
+#define cisco_serviceuri_tag (1<<18)
+#define cisco_escapecodes_tag (1<<19)
+#define cisco_service_control_tag (1<<20)
+#define sdp_anat_tag            (1<< 21)
+#define unrecognized_tag        (1<<31)
+
+    uint32_t supported_tags;
+    uint32_t required_tags;
+
+#define SUPPORTED_TAGS  replaces_tag | join_tag | sdp_anat_tag | norefersub_tag
+
+
+    /*
+     * Contents of the the allow header accepted by the remote side
+     */
+#define ALLOW_ACK          1
+#define ALLOW_BYE          (1<<1)
+#define ALLOW_CANCEL       (1<<2)
+#define ALLOW_INFO         (1<<3)
+#define ALLOW_INVITE       (1<<4)
+#define ALLOW_MESSAGE      (1<<5)
+#define ALLOW_NOTIFY       (1<<6)
+#define ALLOW_OPTIONS      (1<<7)
+#define ALLOW_PRACK        (1<<8)
+#define ALLOW_PUBLISH      (1<<9)
+#define ALLOW_REFER        (1<<10)
+#define ALLOW_REGISTER     (1<<11)
+#define ALLOW_SUBSCRIBE    (1<<12)
+#define ALLOW_UPDATE       (1<<13)
+
+    uint16_t allow_methods;
+
+    sipOfferAnswerState oa_state;
+    int last_recvd_response_code;
+    sipJoinInfo_t       *join_info;
+    cc_feature_data_t   *feature_data;
+    int dup_flags;
+    void *mother_ccb;
+
+#define DUP_NO_FLAGS              0x00
+#define DUP_CCB                   0x01
+#define DUP_CCB_NEW_CALLID        0x02
+#define DUP_CCB_INIT_STATE        0x04
+#define DUP_CCB_REINIT_DNS        0x08
+#define DUP_CCB_STOLEN_FEAT_DATA  0x10
+
+    cc_kfact_t *kfactor_ptr;
+
+    boolean send_reason_header;
+
+    uint32_t callref;
+
+} ccsipCCB_t;
+
+
+typedef struct {
+    ccsipCCB_t ccbs[MAX_CCBS];
+    int        backup_active; /* Currently use reduce invite retry count */
+} ccsipGlobInfo_t;
+
+
+typedef struct {
+    sipSMEventType_t type;
+    ccsipCCB_t *ccb;
+    union {
+        sipMessage_t *pSipMessage;
+        cc_msg_t *cc_msg;
+        cpr_ip_addr_t UsrInfo;
+    } u;
+} sipSMEvent_t;
+
+typedef void (*sipSMEventActionFn_t)(ccsipCCB_t *ccb, sipSMEvent_t *event);
+typedef void (*shutdown_callback_fn)(void *data);
+
+typedef struct {
+    shutdown_callback_fn callback;
+    void                 *data;
+} shutdown_t;
+
+typedef enum {
+    SIP_SDP_SUCCESS = 0,
+    SIP_SDP_SESSION_AUDIT,
+    SIP_SDP_DNS_FAIL,
+    SIP_SDP_NO_MEDIA,
+    SIP_SDP_ERROR,
+    SIP_SDP_NOT_PRESENT
+} sipsdp_status_t;
+
+extern ccsipGlobInfo_t gGlobInfo;
+sipSMAction_t get_handler_index(sipSMStateType_t isipsmstate,
+                                sipSMEventType_t isipsmevent);
+
+void ccsip_handle_idle_ev_sip_invite(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_idle_ev_cc_setup(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+void ccsip_handle_sentinvite_ev_sip_1xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sentinvite_ev_sip_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sentinvite_ev_sip_3xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sentinvite_ev_sip_fxx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+void ccsip_handle_sentinviteconnected_ev_cc_connected_ack(ccsipCCB_t *ccb,
+                                                          sipSMEvent_t *event);
+
+void ccsip_handle_recvinvite_ev_cc_setup_ack(ccsipCCB_t *ccb,
+                                             sipSMEvent_t *event);
+void ccsip_handle_recvinvite_ev_cc_proceeding(ccsipCCB_t *ccb,
+                                              sipSMEvent_t *event);
+void ccsip_handle_recvinvite_ev_cc_alerting(ccsipCCB_t *ccb,
+                                            sipSMEvent_t *event);
+void ccsip_handle_recvinvite_ev_cc_connected(ccsipCCB_t *ccb,
+                                             sipSMEvent_t *event);
+void ccsip_handle_recvinvite_ev_sip_ack(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+void ccsip_handle_active_ev_cc_feature(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_active_ev_cc_feature_hold(ccsipCCB_t *ccb,
+                                            sipSMEvent_t *event);
+void ccsip_handle_active_ev_cc_feature_resume_or_media(ccsipCCB_t *ccb,
+                                              sipSMEvent_t *event);
+void ccsip_handle_active_ev_cc_feature_other(ccsipCCB_t *ccb,
+                                             sipSMEvent_t event);
+void ccsip_handle_active_ev_sip_invite(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+
+void ccsip_handle_recvmidcallinvite_ccfeatureackpending_ev_cc_feature_ack(
+                                                          ccsipCCB_t *ccb,
+                                                          sipSMEvent_t *event);
+void ccsip_handle_recvmidcallinvite_sipackpending_ev_sip_ack(ccsipCCB_t *ccb,
+                                                           sipSMEvent_t *event);
+
+void ccsip_handle_accept_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_active_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+
+void ccsip_handle_default(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_default_sip_message(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_default_sip_response(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_default_sip_timer(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+void ccsip_handle_disconnect_local(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_disconnect_local_early(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_disconnect_local_unanswered(ccsipCCB_t *ccb,
+                                              sipSMEvent_t *event);
+void ccsip_handle_disconnect_remote(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+void ccsip_handle_refer_sip_message(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+void ccsip_handle_active_ev_cc_feature_ack(ccsipCCB_t *ccb,
+                                            sipSMEvent_t *event);
+
+void ccsip_handle_active_ev_cc_feature_xfer(ccsipCCB_t *ccb,
+                                            sipSMEvent_t *event);
+void ccsip_handle_active_ev_cc_feature_indication(ccsipCCB_t *ccb,
+                                            sipSMEvent_t *event);
+
+void ccsip_handle_sentbye_recvd_invite(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sentbye_ev_sip_fxx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sentbye_ev_sip_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sentbye_ev_sip_1xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sendbye_ev_supervision_disconnect(ccsipCCB_t *ccb,
+                                                    sipSMEvent_t *event);
+
+void ccsip_handle_recv_error_response_ev_sip_ack(ccsipCCB_t *ccb,
+                                                  sipSMEvent_t *event);
+
+void ccsip_handle_release_complete(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_send_blind_notify(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sentblindntfy_ev_sip_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_release_ev_sip_bye(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+void ccsip_handle_process_in_call_options_request(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_cc_answer_options_request(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_cc_answer_audit_request(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_localexpires_timer(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+void ccsip_handle_early_ev_sip_update(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_early_ev_sip_update_response(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_early_ev_cc_feature(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_early_ev_cc_feature_ack(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_active_ev_sip_update(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_recvupdatemedia_ccfeatureackpending_ev_cc_feature_ack(
+                                                           ccsipCCB_t *ccb,
+                                                           sipSMEvent_t *event);
+void ccsip_handle_timer_glare_avoidance(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_recvinvite_ev_expires_timer(ccsipCCB_t *ccb,
+                                              sipSMEvent_t *event);
+void ccsip_handle_unsolicited_notify(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_recvinvite_ev_sip_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_icmp_unreachable(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_disconnect_media_change(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sent_ood_refer_ev_sip_1xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sent_ood_refer_ev_sip_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sent_ood_refer_ev_sip_fxx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_default_ev_cc_feature(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_default_recvreq_ack_pending_ev_cc_feature(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_sentinvite_midcall_ev_cc_feature(ccsipCCB_t *ccb,
+                                                   sipSMEvent_t *event);
+void ccsip_handle_sentinvite_midcall_ev_sip_2xx(ccsipCCB_t *ccb,
+                                                sipSMEvent_t *event);
+void ccsip_handle_release_ev_cc_feature(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_ev_cc_info(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void ccsip_handle_release_ev_release(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+int sip_sm_init(void);
+void sip_shutdown(void);
+void sip_shutdown_phase1(int, int reason);
+void sip_shutdown_phase2(int);
+void sip_restart(void);
+int sip_sm_ccb_init(ccsipCCB_t *ccb, line_t index, int DN,
+                    sipRegSMStateType_t initial_state);
+ccsipCCB_t *sip_sm_get_ccb_by_index(line_t index);
+ccsipCCB_t *sip_sm_get_ccb_by_ccm_id_and_index(int ccm_id, line_t idx);
+ccsipCCB_t *sip_sm_get_ccb_by_callid(const char *callid);
+ccsipCCB_t *sip_sm_get_ccb_next_available(line_t *line_number);
+ccsipCCB_t *sip_sm_get_ccb_by_gsm_id(callid_t gsm_id);
+ccsipCCB_t *sip_sm_get_ccb_by_target_call_id(callid_t con_id);
+ccsipCCB_t *sip_sm_get_target_call_by_gsm_id(callid_t gsm_id);
+ccsipCCB_t *sip_sm_get_target_call_by_con_call_id(callid_t con_call_id);
+boolean sip_is_releasing(ccsipCCB_t* ccb);
+callid_t sip_sm_get_blind_xfereror_ccb_by_gsm_id(callid_t gsm_id);
+uint16_t sip_sm_determine_ccb(const char *callid,
+                              sipCseq_t *sipCseq,
+                              sipMessage_t *pSipMessage,
+                              boolean is_request,
+                              ccsipCCB_t **ccb);
+void sip_sm_call_cleanup(ccsipCCB_t *ccb);
+void free_duped(ccsipCCB_t *dupCCB);
+
+
+int sip_sm_process_event(sipSMEvent_t *pEvent);
+int sip_sm_process_cc_event(cprBuffer_t buf);
+void sip_sm_util_normalize_name(ccsipCCB_t *ccb, char *dialString);
+
+
+const char *sip_util_state2string(sipSMStateType_t state);
+const char *sip_util_event2string(sipSMEventType_t event);
+const char *sip_util_method2string(sipMethod_t method);
+boolean sip_sm_is_bye_or_cancel_response(sipMessage_t *response);
+
+sipSMEventType_t sip_util_ccevent2sipccevent(cc_msgs_t cc_msg_type);
+const char *sip_util_feature2string(cc_features_t feature);
+
+void sip_create_new_sip_call_id(char *sipCallID, uint8_t *mac_address,
+                                char *pSrcAddrStr);
+void sip_util_get_new_call_id(ccsipCCB_t *ccb);
+boolean sip_sm_is_previous_call_id(const char *pCallID,
+                                   line_t *pPreviousCallLine);
+boolean sip_sm_util_is_timeinterval(const char *pStr);
+
+void sip_decrement_backup_active_count(ccsipCCB_t *ccb);
+//int sip_sm_active_calls(void);
+#ifdef DEBUG
+void print_ccb_memoryusage(ccsipCCB_t *ccb);
+#endif
+
+void sip_sm_200and300_update(ccsipCCB_t *ccb, sipMessage_t *response,
+                             int response_code);
+char *sip_sm_purify_tag(char *tag);
+boolean sip_sm_is_invite_response(sipMessage_t *response);
+boolean sip_sm_is_refer_response(sipMessage_t *response);
+boolean sip_sm_is_notify_response(sipMessage_t *response);
+
+void sip_sm_dequote_string(char *str, int max_size);
+void sip_sm_check_retx_timers(ccsipCCB_t *ccb, sipMessage_t *message);
+int strcasecmp_ignorewhitespace(const char *cs, const char *ct);
+
+void sip_util_make_ccmsgsdp(cc_sdp_t *pCcMsgSdp, ccsipCCB_t *ccb);
+
+int sip_dns_gethostbysrv(char *domain,
+                         cpr_ip_addr_t *ipaddr_ptr,
+                         uint16_t *port,
+                         srv_handle_t *srv_order,
+                         boolean retried_addr);
+int sip_dns_gethostbysrvorname(char *hname,
+                               cpr_ip_addr_t *ipaddr_ptr,
+                               uint16_t *port);
+void sip_util_make_tag(char *tag_str);
+void get_sip_error_string(char *errortext, int response);
+int ccsip_cc_to_sip_cause(cc_causes_t cause, char **phrase);
+void sip_sm_update_to_on_midcall_200(ccsipCCB_t *ccb, sipMessage_t *response);
+
+sipServiceControl_t *ccsip_get_notify_service_control(sipMessage_t *pSipMessage);
+boolean ccsip_is_special_name_to_mask_display_number(const char *name);
+void sip_sm_change_state(ccsipCCB_t *ccb, sipSMStateType_t new_state);
+
+#define SIP_SM_CALL_SETUP_NOT_COMPLETED(x) \
+        ((x->state == SIP_STATE_RECV_INVITE) || \
+         (x->state == SIP_STATE_RECV_INVITE_PROCEEDING) || \
+         (x->state == SIP_STATE_RECV_INVITE_ALERTING) || \
+         (x->state == SIP_STATE_RECV_INVITE_CONNECTED))
+#define SIP_SM_CALL_SETUP_RESPONDING(x) \
+        ((x->state == SIP_STATE_RECV_INVITE_PROCEEDING) || \
+         (x->state == SIP_STATE_RECV_INVITE_ALERTING))
+
+#define ccsip_is_replace_setup(replace) (replace)
+
+extern char *ccsip_find_preallocated_sip_local_tag(line_t dn_line);
+extern void ccsip_free_preallocated_sip_local_tag(line_t dn_line);
+extern char *getPreallocatedSipCallID(line_t dn_line);
+extern char *getPreallocatedSipLocalTag(line_t dn_line);
+extern ccsipCCB_t* create_dupCCB(ccsipCCB_t *origCCB, int dup_flags);
+
+/* Info Package stuff */
+#define MAX_INFO_HANDLER    32
+
+/*
+ * g_registered_info[] contains the Info Package strings (such as
+ * "conference") for the registered handlers.
+ *
+ * The index of g_registered_info[] goes from 0 to MAX_INFO_HANDLER - 1.
+ */
+extern char *g_registered_info[];
+
+typedef void (*info_package_handler_t)(line_t line, callid_t call_id,
+                                       const char *info_package,
+                                       const char *content_type,
+                                       const char *message_body);
+
+int ccsip_info_package_handler_init(void);
+void ccsip_info_package_handler_shutdown(void);
+int ccsip_register_info_package_handler(const char *info_package,
+                                        const char *content_type,
+                                        info_package_handler_t handler);
+int ccsip_deregister_info_package_handler(const char *info_package,
+                                          const char *content_type,
+                                          info_package_handler_t handler);
+void ccsip_parse_send_info_header(sipMessage_t *pSipMessage, string_t *recv_info_list);
+int ccsip_handle_info_package(ccsipCCB_t *ccb, sipMessage_t *pSipMessage);
+
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_credentials.h b/libs/sipcc/core/sipstack/h/ccsip_credentials.h
new file mode 100644 (file)
index 0000000..e23a433
--- /dev/null
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_CREDENTIALS_H_
+#define _CCSIP_CREDENTIALS_H_
+
+#include "cpr_types.h"
+#include "prot_configmgr.h"
+
+#define CRED_MAX_ID_LEN AUTH_NAME_SIZE
+#define CRED_MAX_PW_LEN 32
+
+#define CRED_USER       0x00000001
+#define CRED_LINE       0x00000002
+
+
+typedef struct _credentials {
+    char id[CRED_MAX_ID_LEN];
+    char pw[CRED_MAX_PW_LEN];
+} credentials_t;
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_macros.h b/libs/sipcc/core/sipstack/h/ccsip_macros.h
new file mode 100644 (file)
index 0000000..40a560b
--- /dev/null
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_MACROS_H_
+#define _CCSIP_MACROS_H_
+
+
+#define EVENT_ACTION_SM(x) \
+        (gSIPHandlerTable[x])
+
+#define UPDATE_FLAGS(x, y) \
+        (x = (x == STATUS_SUCCESS) ? y : x)
+
+#define GET_SIP_MESSAGE() sippmh_message_create()
+
+#define REG_CHECK_EVENT_SANITY(x, y) \
+        ((x - SIP_REG_STATE_BASE >= 0) && (x <= SIP_REG_STATE_END) && \
+         (y - SIPSPI_REG_EV_BASE >=0)  && (y <= SIPSPI_REG_EV_END))
+
+#define REG_EVENT_ACTION(x, y) \
+        (gSIPRegSMTable[x - SIP_REG_STATE_BASE][y - SIPSPI_REG_EV_BASE])
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_messaging.h b/libs/sipcc/core/sipstack/h/ccsip_messaging.h
new file mode 100644 (file)
index 0000000..0ee3b4d
--- /dev/null
@@ -0,0 +1,290 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_MESSAGING_H_
+#define _CCSIP_MESSAGING_H_
+
+#include "ccsip_sdp.h"
+#include "ccsip_pmh.h"
+#include "ccsip_credentials.h"
+#include "ccsip_core.h"
+#include "ccsip_subsmanager.h"
+#include "ccapi.h"
+
+#define SIP_MESSAGING_OK          (0)
+#define SIP_MESSAGING_ERROR       (1)
+#define SIP_MESSAGING_DUPLICATE   (2)
+#define SIP_MESSAGING_NEW_CALLID  (3)
+#define SIP_MESSAGING_ERROR_STALE_RESP (4)
+#define SIP_MESSAGING_ERROR_UNSUPPORTED_MEDIA (5)
+#define SIP_MESSAGING_ERROR_NO_TRX (6)
+#define SIP_MESSAGING_NOT_ACCEPTABLE (7)
+#define SIP_MESSAGING_ENDPOINT_NOT_FOUND (8)
+#define NUM_INITIAL_RECORD_ROUTE_BUFS  4
+
+typedef enum {
+    SIP_INVITE_TYPE_INVALID = 0,
+    SIP_INVITE_TYPE_NORMAL,
+    SIP_INVITE_TYPE_MIDCALL,
+    SIP_INVITE_TYPE_TRANSFER,
+    SIP_INVITE_TYPE_AUTHORIZATION,
+    SIP_INVITE_TYPE_REDIRECTED
+} sipInviteType_t;
+
+typedef enum {
+    SIP_RTP_INCLUDE_MEDIA_INVALID = 0,
+    SIP_RTP_INCLUDE_MEDIA_ALL,
+    SIP_RTP_INCLUDE_MEDIA_PREFERRED,
+    SIP_RTP_INCLUDE_MEDIA_MATCHING
+} sipRTPIncludeMedia_t;
+
+typedef enum {
+    SIP_REF_NONE = 0,
+    SIP_REF_XFER,
+    SIP_REF_DIR_XFER,
+    SIP_REF_TOKEN,
+    SIP_REF_UNKNOWN
+} sipRefEnum_e;
+
+typedef struct {
+    unsigned int flags;
+    unsigned int extflags;
+} sipMessageFlag_t;
+
+
+#define SIP_HEADER_CONTACT_BIT               1
+#define SIP_HEADER_RECORD_ROUTE_BIT          (1<<1)
+#define SIP_HEADER_ROUTE_BIT                 (1<<2)
+#define SIP_HEADER_SPARE_BIT0                (1<<3)
+#define SIP_HEADER_REQUESTED_BY_BIT          (1<<4)
+#define SIP_HEADER_DIVERSION_BIT             (1<<5)
+#define SIP_HEADER_AUTHENTICATION_BIT        (1<<6)
+#define SIP_HEADER_REFER_TO_BIT              (1<<7)
+#define SIP_HEADER_REFERRED_BY_BIT           (1<<8)
+#define SIP_HEADER_REPLACES_BIT              (1<<9)
+#define SIP_HEADER_EVENT_BIT                 (1<<10)
+#define SIP_HEADER_EXPIRES_BIT               (1<<11)
+#define SIP_HEADER_ACCEPT_BIT                (1<<12)
+#define SIP_HEADER_ALLOW_BIT                 (1<<13)
+#define SIP_HEADER_CISCO_GUID_BIT            (1<<14)
+#define SIP_HEADER_SPARE_BIT1                (1<<15)
+#define SIP_HEADER_UNSUPPORTED_BIT           (1<<16)
+#define SIP_HEADER_REMOTE_PARTY_ID_BIT       (1<<17)
+#define SIP_HEADER_PROXY_AUTH_BIT            (1<<18)
+#define SIP_HEADER_REQUIRE_BIT               (1<<19)
+#define SIP_HEADER_CALL_INFO_BIT             (1<<20)
+#define SIP_HEADER_SUPPORTED_BIT             (1<<21)
+#define SIP_HEADER_RETRY_AFTER_BIT           (1<<22)
+#define SIP_HEADER_ALLOW_EVENTS_BIT          (1<<23)
+#define SIP_HEADER_CONTENT_TYPE_BIT          (1<<24)
+#define SIP_HEADER_CONTENT_LENGTH_BIT        (1<<25)
+#define SIP_HEADER_JOIN_INFO_BIT             (1<<26)
+#define SIP_HEADER_ACCEPT_ENCODING_BIT       (1<<27)
+#define SIP_HEADER_ACCEPT_LANGUAGE_BIT       (1<<28)
+#define SIP_HEADER_OPTIONS_CONTENT_TYPE_BIT  (1<<29)
+#define SIP_HEADER_REASON_BIT                (1<<30)
+#define SIP_HEADER_RECV_INFO_BIT             (1U<<31)
+
+
+#define SIP_RFC_SUPPORTED_TAGS REQ_SUPP_PARAM_REPLACES      ","   \
+                               REQ_SUPP_PARAM_JOIN          ","   \
+                               REQ_SUPP_PARAM_SDP_ANAT      ","   \
+                               REQ_SUPP_PARAM_NOREFERSUB
+
+/*
+ * The REQ_SUPP_PARAM_EXTENED_REFER is only used in Cisco CCM environment.
+ * The tag is no longer in IANA.
+ */
+#define SIP_CISCO_SUPPORTED_TAGS REQ_SUPP_PARAM_EXTENED_REFER         "," \
+                                 REQ_SUPP_PARAM_CISCO_CALLINFO        "," \
+                                 REQ_SUPP_PARAM_CISCO_ESCAPECODES     "," \
+                                 REQ_SUPP_PARAM_CISCO_MONREC
+
+#define SIP_CISCO_SUPPORTED_REG_TAGS  \
+     SIP_RFC_SUPPORTED_TAGS       "," \
+     SIP_CISCO_SUPPORTED_TAGS
+
+
+boolean sipSPISendInvite(ccsipCCB_t *ccb,
+                         sipInviteType_t inviteType,
+                         boolean initInvite);
+boolean sipSPISendInviteMidCall(ccsipCCB_t *ccb, boolean expires);
+void sipSPISendInviteResponse100(ccsipCCB_t *ccb, boolean remove_to_tag);
+void sipSPISendInviteResponse180(ccsipCCB_t *ccb);
+void sipSPISendInviteResponse200(ccsipCCB_t *ccb);
+void sipSPISendInviteResponse302(ccsipCCB_t *ccb);
+
+boolean sipSPISendOptionResponse(ccsipCCB_t *ccb, sipMessage_t *);
+boolean sipSPIsendNonActiveOptionResponse(sipMessage_t *msg,
+                                          cc_msgbody_info_t *local_msg_body);
+void sipSPISendInviteResponse(ccsipCCB_t *ccb,
+                              uint16_t statusCode,
+                              const char *reason_phrase,
+                              uint16_t warnCode,
+                              const char *warn_phrase,
+                              boolean send_sd,
+                              boolean retx);
+void sipSPISendBye(ccsipCCB_t *ccb,
+                   char *alsoString,
+                   sipMessage_t *pForked200);
+void sipSPISendCancel(ccsipCCB_t *ccb);
+boolean sipSPISendByeOrCancelResponse(ccsipCCB_t *ccb,
+                                      sipMessage_t *request,
+                                      sipMethod_t sipMethodByeorCancel);
+boolean sipSPISendAck(ccsipCCB_t *ccb, sipMessage_t *response);
+boolean sipSPISendRegister(ccsipCCB_t *ccb,
+                           boolean no_dns_lookup,
+                           const char *user,
+                           int expires_int);
+
+boolean sipSPISendErrorResponse(sipMessage_t *msg, uint16_t status_code,
+                                const char *reason_phrase,
+                                uint16_t status_code_warning,
+                                const char *reason_phrase_warning,
+                                ccsipCCB_t *ccb);
+boolean sipSPISendLastMessage(ccsipCCB_t *ccb);
+boolean sipSPIGenerateAuthorizationResponse(sip_authen_t * sip_authen,
+                                            const char *uri,
+                                            const char *method,
+                                            const char *user_name,
+                                            const char *user_password,
+                                            char **author_str,
+                                            int *nc_count,
+                                            ccsipCCB_t *ccb);
+void sipSPIGenerateGenAuthorizationResponse(ccsipCCB_t *ccb,
+                                            sipMessage_t *request,
+                                            sipRet_t *flag,
+                                            char *method);
+
+void sipSPIGenerateTargetUrl(genUrl_t *genUrl, char *sipurlstr);
+void sipSPIGenerateSipUrl(sipUrl_t *sipUrl, char *sipurlstr);
+
+boolean sipSPISendRefer(ccsipCCB_t *ccb, char *referto,
+                        sipRefEnum_e referto_type);
+boolean sipSPISendReferResponse202(ccsipCCB_t *ccb);
+boolean sipSPISendNotify(ccsipCCB_t *ccb, int referto);
+boolean sipSPISendInfo(ccsipCCB_t *ccb, const char *info_package,
+                       const char *content_type, const char *message_body);
+boolean sipSPISendUpdate(ccsipCCB_t *ccb);
+boolean sipSPISendUpdateResponse(ccsipCCB_t *ccb,
+                                 boolean send_sdp,
+                                 cc_causes_t cause,
+                                 boolean retx);
+boolean sipSPISendNotifyResponse(ccsipCCB_t *ccb, cc_causes_t cause);
+boolean sipSPIAddStdHeaders(sipMessage_t *msg,
+                            ccsipCCB_t *ccb,
+                            boolean isResponse);
+sipMessage_t *sipSPIBuildRegisterHeaders(ccsipCCB_t *ccb,
+                    const char *user,
+                    int expires_int);
+boolean sipSPIAddLocalVia(sipMessage_t *msg,
+                          ccsipCCB_t *ccb,
+                          sipMethod_t method);
+boolean sipSPIAddRequestVia(ccsipCCB_t *ccb,
+                            sipMessage_t *response,
+                            sipMessage_t *request,
+                            sipMethod_t method);
+boolean sipSPIAddCiscoGuid(sipMessage_t *msg, ccsipCCB_t *ccb);
+boolean sipSPIAddRouteHeaders(sipMessage_t *msg,
+                              ccsipCCB_t *ccb,
+                              char *result_route,
+                              int result_route_length);
+sipRet_t sipAddDateHeader(sipMessage_t *sip_message);
+
+const char *sipGetMethodString(sipMethod_t methodname);
+
+void sip_option_response_rtp_media_get_info(cc_sdp_t *src_sdp);
+
+//sipSdp_t* CreateSDPtext();
+
+
+sipRet_t sipSPIAddContactHeader(ccsipCCB_t *ccb, sipMessage_t *request);
+sipRet_t sipSPIAddReasonHeader (ccsipCCB_t *ccb, sipMessage_t *request);
+boolean sipSPIGenerateReferredByHeader(ccsipCCB_t *ccb);
+boolean sipSPIGenRequestURI(ccsipCCB_t *ccb,
+                            sipMethod_t sipmethod,
+                            boolean initInvite);
+
+// Message Factory
+sipRet_t sipSPIAddCommonHeaders(ccsipCCB_t *ccb,
+                                sipMessage_t *request,
+                                boolean isResponse,
+                                sipMethod_t method,
+                                uint32_t response_cseq_number);
+boolean CreateRequest(ccsipCCB_t *ccb,
+                      sipMessageFlag_t messageflag,
+                      sipMethod_t sipmethod,
+                      sipMessage_t *request,
+                      boolean initInvite,
+                      uint32_t response_cseq_number);
+boolean CreateResponse(ccsipCCB_t *ccb,
+                       sipMessageFlag_t messageflag,
+                       uint16_t status_code,
+                       sipMessage_t *response,
+                       const char *reason_phrase,
+                       uint16_t status_code_warning,
+                       const char *reason_phrase_warning,
+                       sipMethod_t);
+boolean SendRequest(ccsipCCB_t *ccb,
+                    sipMessage_t *request,
+                    sipMethod_t method,
+                    boolean midcall,
+                    boolean reTx,
+                    boolean timer);
+boolean AddGeneralHeaders(ccsipCCB_t *ccb,
+                          sipMessageFlag_t messageflag,
+                          sipMessage_t *request,
+                          sipMethod_t method);
+boolean getCSeqInfo(sipMessage_t *request,
+                    sipCseq_t **request_cseq_structure);
+int sipSPICheckRequest(ccsipCCB_t *ccb, sipMessage_t *request);
+int sipSPICheckResponse(ccsipCCB_t *ccb, sipMessage_t *response);
+int sipSPICheckContact(const char *pContactStr);
+
+void sipGetRequestMethod(sipMessage_t *pRequest, sipMethod_t *pMethod);
+int sipGetResponseMethod(sipMessage_t *pResponse, sipMethod_t *pMethod);
+int sipGetResponseCode(sipMessage_t *pResponse, int *pResponseCode);
+int sipSPIIncomingCallSDP(ccsipCCB_t *ccb,
+                          boolean call_hold,
+                          boolean udpate_ver);
+char *sipSPIUrlDestination(sipUrl_t *sipUrl);
+int sipGetMessageCSeq(sipMessage_t *pMessage, uint32_t *pResultCSeqNumber,
+                      sipMethod_t * pResultCSeqMethod);
+void sipGetMessageToTag(sipMessage_t *pMessage, char *to_tag,
+                        int to_tag_max_length);
+boolean sipSPISendByeAuth(sipMessage_t *pResponse,
+                          sipAuthenticate_t authen,
+                          cpr_ip_addr_t *dest_ipaddr,
+                          uint16_t dest_port,
+                          uint32_t cseq_number,
+                          char *alsoString,
+                          char *last_call_route,
+                          char *last_call_route_request_uri,
+                          line_t previous_call_line);
+void free_sip_message(sipMessage_t *message);
+void sipSPISendFailureResponseAck(ccsipCCB_t *ccb,
+                                  sipMessage_t *response,
+                                  boolean prevcall,
+                                  line_t previous_call_line);
+/* ICMP Unreachable handler routine */
+void sip_platform_icmp_unreachable_callback(void *ccyb, uint32_t ipaddr);
+cc_disposition_type_t sip2ccdisp(uint8_t type);
+cc_content_type_t sip2cctype(uint8_t type);
+
+/* Transaction record manipulation methods */
+int16_t get_last_request_trx_index(ccsipCCB_t *ccb, boolean sent);
+int16_t get_next_request_trx_index(ccsipCCB_t *ccb, boolean sent);
+int16_t get_method_request_trx_index(ccsipCCB_t *ccb, sipMethod_t method,
+                                     boolean sent);
+void clean_method_request_trx(ccsipCCB_t *ccb, sipMethod_t method, boolean sent);
+line_t get_dn_line_from_dn(const char *watcher);
+boolean sipSPIGenerateRouteHeaderUAC(sipRecordRoute_t *, char *, int,
+                                     boolean *loose_routing);
+boolean sipSPIGenerateRouteHeaderUAS(sipRecordRoute_t *, char *, int,
+                                     boolean *loose_routing);
+boolean sipSPIGenerateContactHeader(sipContact_t *, char *, int);
+
+void get_reason_string(int unreg_reason, char *unreg_reason_str, int len);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_platform.h b/libs/sipcc/core/sipstack/h/ccsip_platform.h
new file mode 100644 (file)
index 0000000..36b8c92
--- /dev/null
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_PLATFORM_H_
+#define _CCSIP_PLATFORM_H_
+
+#include "phone_platform_constants.h"
+
+// SIP standard defines the max to be the path MTU if it is
+// known, otherwise it should be 1500
+// We are defining the max message size to be 2560, 1500 + 1060 bytes of
+// extended buffer space (which is also the same as the PacketBuffer size
+// #define PKTBUF_SIZ  3072 defined in phone.h
+#define SIP_UDP_MESSAGE_SIZE   3072 /* Max size of a SIP message */
+
+/* Constants */
+#define MAX_SIP_URL_LENGTH          512
+#define MAX_SIP_TAG_LENGTH          256
+#define MAX_SIP_DISPLAYNAME_LENGTH  64
+#define VIA_BRANCH_LENGTH           16
+
+/*
+ * MAX_SIP_DATE_LENGTH is used in ccsip_messaging for
+ * the SIP Date header routine.
+ * It has been defined here to be consistent with the rest
+ * of the defines for the SIP message
+ */
+#define MAX_SIP_DATE_LENGTH         63  //Extended for foreign language
+#define CCSIP_START_CSEQ            100
+#define MAX_SIP_CALL_ID             128 /* Max size of a Call ID header string */
+#define MAX_DIVERSION_HEADERS       25
+
+/*********************************************************
+ *
+ *
+ *  Phone constants
+ *
+ *
+ *********************************************************/
+#define MAX_REG_BACKUP         1    // Max number of backup registration CCBs
+#define MIN_TEL_LINES          0    // Min number of lines
+#define MAX_TEL_LINES          MAX_CALLS // Max number of calls
+#define MAX_PHYSICAL_DIR_NUM   6    // Max number of physical directory numbers
+#define MAX_UI_LINES_PER_DN    (MAX_LINES_PER_DN + 1) // Max number of lines per DN
+#define MAX_CCBS               (MAX_TEL_LINES + MAX_REG_LINES + MAX_REG_BACKUP)
+#define TEL_CCB_START          (MIN_TEL_LINES)
+#define TEL_CCB_END            (TEL_CCB_START + MAX_TEL_LINES -1)
+#define REG_CCB_START          (TEL_CCB_END + 1)
+#define REG_CCB_END            (REG_CCB_START + MAX_REG_LINES -1)
+#define REG_BACKUP_CCB         (REG_CCB_END + 1)
+#define REG_FALLBACK_CCB_START (REG_BACKUP_CCB + 1)
+#define REG_FALLBACK_CCB_END   (REG_FALLBACK_CCB_START +4)
+#define REG_BACKUP_DN          1
+#define REG_BACKUP_LINE        7
+#define MAX_LINES_7940         2
+#define INVALID_DN_LINE        (REG_FALLBACK_CCB_END + 1)
+#define INIT_DN_LINE           0
+
+
+void sip_platform_init(void);
+int sip_platform_ui_restart(void);
+extern void platform_print_sip_msg(const char *msg);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_platform_tcp.h b/libs/sipcc/core/sipstack/h/ccsip_platform_tcp.h
new file mode 100644 (file)
index 0000000..21c0b03
--- /dev/null
@@ -0,0 +1,99 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __CCSIP_PLATFORM_TCP__H__
+#define __CCSIP_PLATFORM_TCP__H__
+
+/* The maximum number of connections allowed */
+#define MAX_SIP_CONNECTIONS (64 - 2)
+
+#define SIP_TCP_SEND_OK 0
+#define SIP_TCP_SIZE_ERROR 1
+#define SIP_TCP_SEND_ERROR 2
+#define SIP_SOC_TCP 0
+#define SIP_SOC_TLS 1
+
+#define SIP_TCP_NOT_CONNECTED 0
+#define SIP_TCP_CONNECTED 1
+
+typedef struct _sendData
+{
+    struct _sendData *next;
+    char             *data;
+    uint16_t          bytesLeft;
+    uint16_t          bytesSent;
+    void             *context;
+    boolean           msg_display;
+    uint8_t           ip_sig_tos;
+} ccsipTCPSendData_t;
+
+typedef struct
+{
+    uint16 connectionId;
+} sipTCPTimerContext_t;
+
+typedef struct
+{
+    cpr_socket_t      fd;           /* Socket file descriptor */
+    cpr_sockaddr_storage addr;
+    cpr_ip_addr_t     ipaddr;       /* Remote IP address */
+    uint16_t          port;         /* Remote port # */
+    sock_state_t      state;
+    char             *prev_msg;
+    uint32_t          prev_bytes;
+    void             *context;      /* Connection Manager's context */
+    /* queue for partial socket writes */
+    sll_handle_t      sendQueue;
+    boolean           dirtyFlag;
+    boolean           pend_closure; /* Indicates to close the connection
+                                     * entry on completion of partial
+                                     * socket message writes
+                                     */
+    int               error_cause;
+    int               soc_type;
+} sip_tcp_conn_t;
+
+typedef struct
+{
+    cpr_socket_t read[MAX_SIP_CONNECTIONS];
+    cpr_socket_t write[MAX_SIP_CONNECTIONS];
+} sip_connection_t;
+
+
+extern sip_connection_t sip_conn;
+extern uint32_t nfds;
+extern fd_set read_fds;
+extern fd_set write_fds;
+extern sip_tcp_conn_t sip_tcp_conn_tab[MAX_CONNECTIONS];
+
+/*
+ *
+ * Function declarations
+ *
+ */
+int sip_tcp_fd_to_connid(cpr_socket_t fd);
+cpr_socket_t sip_tcp_create_connection(sipSPIMessage_t *spi_msg);
+void sip_tcp_read_socket(cpr_socket_t this_fd);
+int sip_tcp_channel_send(cpr_socket_t s,
+                         char *buf,
+                         uint32_t len);
+void sip_tcp_createconnfailed_to_spi(cpr_ip_addr_t *ipaddr,
+                                     uint16_t port,
+                                     void *context,
+                                     ccsipSockErrCodes_e errcode,
+                                     int connid);
+void sip_tcp_purge_entry(sipSPIConnId_t connid);
+void sipTcpFlushRetrySendQueue(sip_tcp_conn_t *entry);
+void sip_tcp_resend(int connid);
+extern int sip_tcp_get_free_conn_entry(void);
+extern int sip_tcp_attach_socket(cpr_socket_t s);
+extern void sip_tcp_init_conn_table(void);
+extern boolean sip_tcp_set_sock_options(int fd);
+void sipTransportClearServerHandle(cpr_ip_addr_t *ipaddr,
+                                   uint16_t port,
+                                   int connid);
+void sipTcpFreeSendQueue(int connid);
+extern cpr_socket_t sip_tcp_create_conn_using_blocking_socket (sipSPIMessage_t *spi_msg);
+
+#endif /* __CCSIP_PLATFORM_TCP__H__ */
diff --git a/libs/sipcc/core/sipstack/h/ccsip_platform_timers.h b/libs/sipcc/core/sipstack/h/ccsip_platform_timers.h
new file mode 100644 (file)
index 0000000..06a6f54
--- /dev/null
@@ -0,0 +1,164 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_PLATFORM_TIMERS_H_
+#define _CCSIP_PLATFORM_TIMERS_H_
+
+#include "cpr_types.h"
+#include "cpr_timers.h"
+#include "ccsip_platform.h"
+#include "ccsip_pmh.h"
+
+#define LINE_NOT_FOUND -1
+
+/*
+ * Defintions
+ */
+typedef void (*sipTimerCallbackFn_t)(cprTimer_t pTmrBlk);
+
+typedef struct
+{
+    cprTimer_t  timer;
+    cprTimer_t  reg_timer;
+    int         line;
+    char*        message_buffer;
+    int         message_buffer_len;
+    sipMethod_t message_type;
+    cpr_ip_addr_t   ipaddr;
+    uint16_t    port;
+    boolean     outstanding;
+} sipPlatformUITimer_t;
+
+typedef struct
+{
+    cprTimer_t timer;
+    int        line;
+    cpr_ip_addr_t   ipaddr;
+    uint16_t   port;
+} sipPlatformUIExpiresTimer_t;
+
+typedef struct
+{
+    cprTimer_t timer;
+    int        line;
+    boolean    started;
+} sipPlatformSupervisionTimer_t;
+
+
+extern sipPlatformUITimer_t sipPlatformUISMSubNotTimers[]; // Array of timers
+
+/*
+ * Prototypes
+ */
+int
+sip_platform_timers_init(void);
+void
+sip_platform_post_timer(uint32_t cmd, void *data);
+void
+sip_platform_msg_timers_init(void);
+int
+sip_platform_msg_timer_start(uint32_t msec,
+                             void *data,
+                             int line,
+                             char *message_buffer,
+                             int message_buffer_len,
+                             int message_type,
+                             cpr_ip_addr_t *ipaddr,
+                             uint16_t port,
+                             boolean isRegister);
+void
+sip_platform_msg_timer_stop(int line);
+boolean
+sip_platform_msg_timer_outstanding_get(int line);
+void
+sip_platform_msg_timer_outstanding_set(int line, boolean value);
+void
+sip_platform_msg_timer_callback(void *data);
+int
+sip_platform_expires_timer_start(uint32_t msec,
+                                 int line,
+                                 cpr_ip_addr_t *ipaddr,
+                                 uint16_t port);
+int
+sip_platform_expires_timer_stop(int line);
+void
+sip_platform_expires_timer_callback(void *data);
+int
+sip_platform_register_expires_timer_start(uint32_t msec,
+                                          int line);
+int
+sip_platform_register_expires_timer_stop(int line);
+int
+sip_platform_localexpires_timer_start(uint32_t msec,
+                                      int line,
+                                      cpr_ip_addr_t *ipaddr,
+                                      uint16_t port);
+int
+sip_platform_localexpires_timer_stop(int line);
+void
+sip_platform_localexpires_timer_callback(void *data);
+int
+sip_platform_msg_timer_update_destination(int line,
+                                          cpr_ip_addr_t *ipaddr,
+                                          uint16_t port);
+sipMethod_t
+sip_platform_msg_timer_messageType_get(int line);
+
+// Supervision timer
+int
+sip_platform_supervision_disconnect_timer_start(uint32_t msec,
+                                                int line);
+int
+sip_platform_supervision_disconnect_timer_stop(int line);
+void
+sip_platform_supervision_disconnect_timer_callback(void *data);
+
+// Sub/Not Timers
+int
+sip_platform_msg_timer_subnot_start(uint32_t msec,
+                                    sipPlatformUITimer_t *,
+                                    uint32_t index,
+                                    char *message_buffer,
+                                    int message_buffer_len,
+                                    int message_type,
+                                    cpr_ip_addr_t *ipaddr,
+                                    uint16_t port);
+void
+sip_platform_msg_timer_subnot_stop(sipPlatformUITimer_t *);
+void
+sip_platform_subnot_msg_timer_callback(void *data);
+int
+sip_platform_subnot_periodic_timer_start(uint32_t msec);
+int
+sip_platform_subnot_periodic_timer_stop(void);
+void
+sip_platform_subnot_periodic_timer_callback(void *data);
+int
+sip_platform_standby_keepalive_timer_start(uint32_t msec);
+int
+sip_platform_standby_keepalive_timer_stop();
+void
+sip_platform_standby_keepalive_callback(void *data);
+int
+sip_platform_unregistration_timer_start(uint32_t msec, boolean external);
+int
+sip_platform_unregistration_timer_stop();
+void
+sip_platform_unregistration_callback(void *data);
+void
+sip_platform_timers_shutdown(void);
+int
+sip_platform_notify_timer_start(uint32_t msec);
+int
+sip_platform_notify_timer_stop();
+int
+sip_platform_reg_all_fail_timer_start(uint32_t msec);
+int
+sip_platform_reg_all_fail_timer_stop(void);
+int
+sip_platform_pass_through_timer_start(uint32_t sec);
+int
+sip_platform_pass_through_timer_stop(void);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_platform_tls.h b/libs/sipcc/core/sipstack/h/ccsip_platform_tls.h
new file mode 100644 (file)
index 0000000..6d6fba9
--- /dev/null
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __CCSIP_PLATFORM_TLS__H__
+#define __CCSIP_PLATFORM_TLS__H__
+
+extern cpr_socket_t sip_tls_create_connection(sipSPIMessage_t *spi_msg,
+                                              boolean blocking,
+                                              sec_level_t sec);
+
+#endif /* __CCSIP_PLATFORM_TLS__H__ */
diff --git a/libs/sipcc/core/sipstack/h/ccsip_platform_udp.h b/libs/sipcc/core/sipstack/h/ccsip_platform_udp.h
new file mode 100644 (file)
index 0000000..9eddc20
--- /dev/null
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_PLATFORM_UDP_H_
+#define _CCSIP_PLATFORM_UDP_H_
+
+#include "cpr_types.h"
+#include "cpr_socket.h"
+#include "cpr_memory.h"
+
+int
+sip_platform_udp_channel_listen(cpr_ip_mode_e ip_mode, cpr_socket_t *s,
+                                cpr_ip_addr_t *local_ipaddr,
+                                uint16_t local_port);
+int
+sip_platform_udp_channel_accept(cpr_socket_t listenSoc,
+                                cpr_socket_t *s);
+int
+sip_platform_udp_channel_create(cpr_ip_mode_e ip_mode, cpr_socket_t *s,
+                                cpr_ip_addr_t *remote_ipaddr,
+                                uint16_t remote_port,
+                                uint32_t local_udp_port);
+int
+sip_platform_udp_channel_destroy(cpr_socket_t s);
+int
+sip_platform_udp_channel_send(cpr_socket_t s,
+                              char *buf,
+                              uint16_t buf_len);
+void
+sip_platform_udp_read_socket(cpr_socket_t s);
+
+int
+sip_platform_udp_channel_sendto(cpr_socket_t s,
+                                char *buf,
+                                uint32_t buf_len,
+                                cpr_ip_addr_t *dst_addr,
+                                uint16_t dst_port);
+int
+sip_platform_udp_channel_read(cpr_socket_t s,
+                              cprBuffer_t buf,
+                              uint16_t *len,
+                              cpr_sockaddr_t *soc_addr,
+                              cpr_socklen_t *soc_addr_len);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_pmh.h b/libs/sipcc/core/sipstack/h/ccsip_pmh.h
new file mode 100644 (file)
index 0000000..303eddd
--- /dev/null
@@ -0,0 +1,827 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_PMH_H_
+#define _CCSIP_PMH_H_
+
+#include "cpr_types.h"
+#include "pmhdefs.h"
+#include "httpish.h"
+#include "ccsip_protocol.h"
+#include "string_lib.h"
+#include "ccsip_platform.h"
+#include "ccapi.h"
+
+#define MAX_REFER_TO_HEADER_CONTENTS  6
+#define MAX_REMOTE_PARTY_ID_HEADERS   10
+#define MAX_REPLACES_HEADERS          1
+#define MAX_REFER_TO_HEADERS          2
+#define MAX_REFER_BY_HEADERS          1
+#define MAX_CALL_INFO_HEADERS         4
+
+#define MAX_SUB_STATE_HEADER_SIZE     96
+#define TEMP_PARSE_BUFFER_SIZE        10
+
+/*
+ * Maximum number of Location/Contact headers we will parse. These
+ * specify the locations to try to contact the called party, and I figure
+ * the user is not going to wait for ever, and simplicity helps.
+ */
+#define SIP_MAX_LOCATIONS 6
+
+/* Used when we create Via headers. Does not apply to incoming messages.*/
+#define SIP_MAX_VIA_LENGTH 128
+
+/* Broadsoft: Max length of "other" parameters in 3xx messages. */
+#define SIP_MAX_OTHER_PARAM_LENGTH 256
+
+#define TWO_POWER_31 2147483648UL
+
+/*
+ * Some of this corresponds directly to HTTP/1.1 message structure.
+ */
+typedef httpish_header    sip_header;
+typedef httpishMsg_t      sipMessage_t;
+typedef httpishReqLine_t  sipReqLine_t;
+typedef httpishRespLine_t sipRespLine_t;
+
+typedef httpishStatusCodeClass_t sipStatusCodeClass_t;
+
+typedef hStatus_t sipRet_t;
+
+typedef enum
+{
+    sipMethodInvalid = 0,
+    sipMethodRegister = 100,
+    sipMethodOptions,
+    sipMethodInvite,
+    sipMethodBye,
+    sipMethodCancel,
+    sipMethodPrack,
+    sipMethodComet,
+    sipMethodNotify,
+    sipMethodRefer,
+    sipMethodAck,
+    sipMethodMessage,
+    sipMethodSubscribe,
+    sipMethodPublish,
+    sipMethodUpdate,
+    sipMethodResponse,
+    sipMethodInfo,
+    sipMethodUnknown
+} sipMethod_t;
+
+typedef enum
+{
+    SIP_BASIC = 1,
+    SIP_DIGEST,
+    SIP_UNKNOWN
+} sip_scheme_t;
+
+typedef enum
+{
+    SIP_SUCCESS,
+    SIP_FAILURE,
+    SIP_INTERNAL_ERR,
+    SIP_UNACCEPTABLE_MEDIA_ERR,
+    SIP_PRE_CONDITION_FAIL_ERR,
+    SIP_NO_RESOURCE,
+    SIP_MSG_CREATE_ERR,
+    SIP_MSG_PARSE_ERR,
+    SIP_MSG_INCOMPLETE_ERR,
+    SIP_SUCCESS_DNS_PENDING,
+    SIP_SUCCESS_QOS_PENDING,
+    SIP_VIA_DNS_QUERY_REQUIRED,
+    SIP_SUCCESS_QOS_DNS_PENDING,
+    SIP_SUCCESS_DELAYED_MEDIA,
+    SIP_SUCCESS_QOS_DELAYED_MEDIA,
+    SIP_ADDR_UNAVAILABLE,
+    SIP_MAX_RC
+} ccsipRet_e;
+
+/* This is used to store the parsed attribute/value pair */
+typedef struct {
+    char *attr;
+    char *value;
+} attr_value_pair_t;
+
+typedef struct
+{
+    /*
+     * Not storing the schema because we don't deal with non-SIP URLs.
+     * The rest of the fields do not have any meaning if the URL
+     * in question is non-SIP. Our parse routine itself would return
+     * failure.
+     */
+    char    *user;
+    char    *password;
+    char    *host;
+    char    *maddr;
+    char    *other;
+    char    *method;
+    uint16_t port;
+    boolean  port_present;
+    int16_t  transport;
+    uint8_t  is_phone;
+    uint8_t  ttl_val;
+    char     num_headers;       /* number of headers in the avpair below */
+    attr_value_pair_t *headerp; /* everything after the '?' */
+    boolean  lr_flag;
+    boolean  is_ipv6;
+} sipUrl_t;
+
+typedef struct
+{
+    char *user;
+    char *isdn_subaddr;
+    char *post_dial;
+    char *unparsed_tsp;
+    char *future_ext;
+} telUrl_t;
+
+typedef enum
+{
+    URL_TYPE_SIP = 1,
+    URL_TYPE_TEL,
+    URL_TYPE_CID,
+    URL_TYPE_UNKNOWN
+} urlType_t;
+
+typedef struct
+{
+    urlType_t schema;
+    boolean   sips;
+    char     *str_start;
+    char     *phone_context;
+    char     *other_params[SIP_MAX_LOCATIONS];
+    union {
+        sipUrl_t *sipUrl;
+        telUrl_t *telUrl;
+    } u;
+} genUrl_t;
+
+typedef struct
+{
+    char        *str_start;  /* beginning of duplicated string */
+    sip_scheme_t scheme;
+    char        *user_pass;
+    char        *realm;
+    char        *d_username;
+    char        *unparsed_uri;
+    char        *response;
+    char        *algorithm;
+    char        *cnonce;
+    char        *opaque;
+    char        *qop;
+    char        *nc_count;
+    char        *auth_param; /* for future extension */
+    char        *nonce;
+} sip_author_t;
+
+typedef struct
+{
+    /* Could be Basic or PGP */
+    char        *str_start;  /* beginning of duplicated string */
+    sip_scheme_t scheme;
+    char        *user_pass;
+    char        *realm;
+    char        *version;
+    char        *algorithm;
+    char        *nonce;
+    char        *unparsed_domain;
+    char        *opaque;
+    char        *stale;
+    char        *qop;
+} sip_authen_t;
+
+typedef struct _sipLocation_t
+{
+    char     *loc_start;
+    char     *name;
+    genUrl_t *genUrl;
+    char     *tag;
+} sipLocation_t;
+
+typedef sipLocation_t sipFrom_t;
+typedef sipLocation_t sipTo_t;
+
+/*
+ * Definition of the flags in the sipContactParams_t.
+ * The flags in the sipContact_t is used to store boolean
+ * parameters (not numerical value).
+ */
+#define SIP_CONTACT_PARM_X_CISCO_NEWREG  (1 << 0)   /* new reg parameter */
+typedef struct
+{
+    int     action;     /* Proxy or Redirect */
+    char    *qval;
+    uint32_t expires;
+    char    *expires_gmt;
+    char    *extn_attr;
+    uint32_t flags;
+} sipContactParams_t;
+
+typedef struct
+{
+    sipLocation_t     *locations[SIP_MAX_LOCATIONS];
+    sipContactParams_t params[SIP_MAX_LOCATIONS];
+    uint16_t           num_locations;
+    boolean            new_flag;
+} sipContact_t;
+
+typedef struct
+{
+    sipLocation_t *locations[SIP_MAX_LOCATIONS];
+    uint16_t       num_locations;
+    boolean        new_flag;
+} sipRecordRoute_t;
+
+typedef enum
+{
+    sipTransportTypeUDP = 2345,
+    sipTransportTypeTCP
+} sipTransportType_t;
+
+/* DIVERSION HEADER STRUCTURE */
+
+typedef struct
+{
+    sipLocation_t *locations;
+//  char *reason;
+    uint32_t limit;
+    uint32_t counter;
+    char    *privacy;
+    char    *screen;
+} sipDiversion_t;
+
+typedef struct
+{
+    string_t orig_called_name;
+    string_t orig_called_number;
+    string_t last_redirect_name;
+    string_t last_redirect_number;
+} sipDiversionInfo_t;
+
+
+typedef struct
+{
+    /*
+     * We are not storing the protocol ( should be SIP always )
+     */
+    char *version;
+    char *transport;
+    char *host;
+    char *ttl;
+    char *maddr;
+    char *recd_host;
+    char *branch_param;
+    /* Remaining Via header(s) if any in the input string */
+    char *more_via;
+    uint16_t remote_port;
+    uint8_t  flags;
+    boolean is_ipv6;
+#define VIA_IS_HIDDEN    (0x01)
+} sipVia_t;
+
+typedef struct
+{
+    uint32_t    number;
+    sipMethod_t method;
+} sipCseq_t;
+
+
+typedef struct
+{
+    uint32_t    response_num;
+    uint32_t    cseq_num;
+    sipMethod_t method;
+} sipRack_t;
+
+typedef enum
+{
+    sipSessionTypeInvalid = 0,
+    sipSessionTypeMedia,
+    sipSessionTypeQoS,
+    sipSessionTypeSecurity
+} sipSessionType_t;
+
+/* Data structure for Replaces header */
+typedef struct
+{
+    char *str_start;
+    char *callid;
+    char *toTag;
+    char *fromTag;
+    char *signature_scheme;
+} sipReplaces_t;
+
+/* Data structure for Refer-To header */
+typedef struct
+{
+    char     *ref_to_start;
+    genUrl_t *targetUrl;
+    char     *sip_replaces_hdr;
+    char     *sip_acc_cont;
+    char     *sip_proxy_auth;
+} sipReferTo_t;
+
+/* Data structure for Refer method */
+typedef struct
+{
+    sipReferTo_t *refer_to_info;    /* Refer-To header */
+    const char   *referred_by_info; /* Referred-by header */
+} sipRefer_t;
+
+/* Data structures for Remote-Party-Id */
+typedef struct
+{
+    sipLocation_t *loc;
+    char          *screen;
+    char          *party_type;
+    char          *id_type;
+    char          *privacy;
+    char          *np;
+} sipRemotePartyId_t;
+
+typedef struct
+{
+    unsigned int        num_rpid; /* Number of rpid instantiations present */
+    sipRemotePartyId_t *rpid[MAX_REMOTE_PARTY_ID_HEADERS];
+} sipRemotePartyIdInfo_t;
+
+typedef enum
+{
+    SUBSCRIPTION_STATE_INVALID = 0,
+    SUBSCRIPTION_STATE_ACTIVE,
+    SUBSCRIPTION_STATE_PENDING,
+    SUBSCRIPTION_STATE_TERMINATED
+} sip_subs_state_e;
+
+typedef enum
+{
+    SUBSCRIPTION_STATE_REASON_INVALID = 0,
+    SUBSCRIPTION_STATE_REASON_DEACTIVATED,
+    SUBSCRIPTION_STATE_REASON_PROBATION,
+    SUBSCRIPTION_STATE_REASON_REJECTED,
+    SUBSCRIPTION_STATE_REASON_TIMEOUT,
+    SUBSCRIPTION_STATE_REASON_GIVEUP,
+    SUBSCRIPTION_STATE_REASON_NORESOURCE
+} sip_subs_state_reason_e;
+
+typedef struct
+{
+    sip_subs_state_e        state;
+    uint32_t                expires;
+    sip_subs_state_reason_e reason;
+    uint32_t                retry_after;
+} sipSubscriptionStateInfo_t;
+
+typedef enum {
+    SERVICE_CONTROL_ACTION_INVALID = 0,
+    SERVICE_CONTROL_ACTION_RESET,
+    SERVICE_CONTROL_ACTION_RESTART,
+    SERVICE_CONTROL_ACTION_CHECK_VERSION,
+    SERVICE_CONTROL_ACTION_CALL_PRESERVATION,
+    SERVICE_CONTROL_ACTION_APPLY_CONFIG
+} sip_service_control_action_e;
+
+typedef struct
+{
+    sip_service_control_action_e action;
+    char *registerCallID;
+    char *configVersionStamp;
+    char *dialplanVersionStamp;
+    char *softkeyVersionStamp;
+    char *fcpVersionStamp;
+    char *cucm_result;
+    char *firmwareLoadId;
+    char *firmwareInactiveLoadId;
+    char *loadServer;
+    char *logServer;
+    boolean ppid;
+} sipServiceControl_t;
+
+typedef struct
+{
+    char *call_id;
+    char *from_tag;
+    char *to_tag;
+} sipJoinInfo_t;
+
+typedef enum {
+    mwiVoiceType = 1,
+    mwiFaxType,
+    mwiTextMessage
+} messageCountType_t;
+
+typedef struct {
+    boolean mesg_waiting_on;
+    int32_t type;
+    int32_t newCount;
+    int32_t oldCount;
+    int32_t hpNewCount;
+    int32_t hpOldCount;
+} sipMessageSummary_t;
+
+/*
+ * Returns a sipMethod_t value given a character method name.
+ * Returns sipMethodUnknown if the method is not on the list of recognized
+ * methods.
+ */
+PMH_EXTERN sipMethod_t sippmh_get_method_code(const char *);
+
+PMH_EXTERN genUrl_t *sippmh_parse_url(char *url, boolean dup_flag);
+
+/*
+ * See comment above.
+ */
+PMH_EXTERN void sippmh_genurl_free(genUrl_t *);
+
+PMH_EXTERN sipLocation_t *sippmh_parse_from_or_to(char *from, boolean dup_flag);
+
+PMH_EXTERN sipLocation_t *sippmh_parse_nameaddr_or_addrspec(char *input_loc_ptr,
+                                                            char *start_ptr,
+                                                            boolean dup_flag,
+                                                            boolean name_addr_only_flag,
+                                                            char **more_ptr);
+
+/*
+ * See comment above.
+ */
+PMH_EXTERN void sippmh_free_location(sipLocation_t *);
+
+/*
+ * Same comments as parse_location
+ */
+PMH_EXTERN sipContact_t *sippmh_parse_contact(const char *contact);
+
+
+/*
+ * Same comments as parse_location
+ */
+PMH_EXTERN sipDiversion_t *sippmh_parse_diversion(const char *diversion,
+                                                  char *diversionhead);
+
+
+/*
+ * Same comments as free_location.
+ */
+PMH_EXTERN void sippmh_free_diversion(sipDiversion_t *);
+
+PMH_EXTERN void sippmh_free_diversion_info(sipDiversionInfo_t *div_info);
+
+/*
+ * Same comments as free_location.
+ */
+PMH_EXTERN void sippmh_free_contact(sipContact_t *);
+
+/*
+ * Same comments as parse_location
+ */
+PMH_EXTERN sipVia_t *sippmh_parse_via(const char *via);
+
+/*
+ *  Same comments as free_location.
+ */
+PMH_EXTERN void sippmh_free_via(sipVia_t *via);
+
+PMH_EXTERN void sippmh_process_via_header(sipMessage_t *sip_message,
+                                          cpr_ip_addr_t *source_ip_address);
+
+/*
+ * Same comments as parse_location
+ */
+PMH_EXTERN sipCseq_t *sippmh_parse_cseq(const char *cseq);
+
+PMH_EXTERN boolean sippmh_parse_rseq(const char *rseq, uint32_t *rseq_val);
+
+PMH_EXTERN sipRack_t *sippmh_parse_rack(const char *rack);
+
+/*
+ * Does a proper comparison of From: headers.(kind of like
+ * a URL comparison.
+ * TRUE if equal, FALSE if not.
+ */
+PMH_EXTERN boolean sippmh_are_froms_equal(sipFrom_t *from1, sipFrom_t *from2);
+
+/*
+ * Does a proper comparison of To: headers.(kind of like
+ * a URL comparison.
+ * TRUE if equal, FALSE if not.
+ * At this time, sipTo is really the sipLocation struct, but
+ * this is kept different in case things change.
+ */
+PMH_EXTERN boolean sippmh_are_tos_equal(sipTo_t *to1, sipTo_t *to2);
+
+/*
+ * Does a comparison of SIP URLs.
+ * TRUE if equal, FALSE if not.
+ */
+PMH_EXTERN boolean sippmh_are_urls_equal(sipUrl_t *url1, sipUrl_t *url2);
+
+/*
+ * Utility to get the callid of a message.
+ * Returns NULL if the header is not found.
+ * Returned pointer should not be freed.
+ */
+//PMH_EXTERN const char *sippmh_get_callid(sipMessage_t *msg);
+
+/*
+ * Adds a Cseq: header to the message.
+ * msg = sipMessage_t struct
+ * method, seq  = method, seq in the Cseq eg Cseq: 428 INVITE
+ * Assumes that the maximum length of the resulting header is
+ * less than 32 characters.
+ */
+PMH_EXTERN sipRet_t sippmh_add_cseq(sipMessage_t *msg, const char *method,
+                                    uint32_t seq);
+
+PMH_EXTERN sipRet_t sippmh_add_rack(sipMessage_t *msg, uint32_t rseq,
+                                    uint32_t cseq, const char *method);
+
+/*
+ * Utility to check whether the URL is valid.
+ * TRUE if yes, FALSE if not.
+ */
+PMH_EXTERN boolean sippmh_valid_url(genUrl_t *genUrl);
+
+PMH_EXTERN boolean sippmh_valid_reqline_url(genUrl_t *genUrl);
+
+PMH_EXTERN sipRecordRoute_t *sippmh_parse_record_route(const char *);
+
+PMH_EXTERN sipRecordRoute_t *sippmh_copy_record_route (sipRecordRoute_t *rr);
+
+PMH_EXTERN void sippmh_free_record_route(sipRecordRoute_t *);
+
+PMH_EXTERN cc_content_disposition_t *sippmh_parse_content_disposition(const char *input_content_disp);
+
+PMH_EXTERN sipReferTo_t *sippmh_parse_refer_to(char *ref_to);
+
+PMH_EXTERN sipReplaces_t *sippmh_parse_replaces(char *replcs, boolean dup_flag);
+
+PMH_EXTERN void sippmh_free_replaces(sipReplaces_t *repl);
+
+PMH_EXTERN void sippmh_free_refer_to(sipReferTo_t *ref);
+
+PMH_EXTERN void sippmh_free_refer(sipRefer_t *ref);
+
+PMH_EXTERN void sippmh_convertEscCharToChar(const char *inputStr,
+                                            size_t inputStrLen,
+                                            char *outputStr);
+
+PMH_EXTERN int sippmh_cmpURLStrings(const char *s1, const char *s2,
+                                    boolean ignore_case);
+
+PMH_EXTERN sip_author_t *sippmh_parse_authorization(const char *);
+
+PMH_EXTERN char *sippmh_generate_authorization(sip_author_t *);
+
+PMH_EXTERN void sippmh_free_author(sip_author_t *);
+
+PMH_EXTERN sip_authen_t *sippmh_parse_authenticate(const char *);
+
+PMH_EXTERN void sippmh_free_authen(sip_authen_t *);
+
+PMH_EXTERN sip_author_t *sippmh_parse_proxy_author(const char *);
+
+PMH_EXTERN sip_authen_t *sippmh_parse_proxy_authen(const char *);
+
+PMH_EXTERN sipRemotePartyId_t *sippmh_parse_remote_party_id(
+                                              const char *input_remote_party_id);
+
+PMH_EXTERN boolean sippmh_parse_kpml_event_id_params(char *params,
+                                                     char **call_id,
+                                                     char **from_tag,
+                                                     char **to_tag);
+
+PMH_EXTERN void sippmh_free_remote_party_id_info(sipRemotePartyIdInfo_t *rpid_info);
+
+PMH_EXTERN char *sippmh_parse_user(char *url);
+
+PMH_EXTERN int sippmh_parse_subscription_state(sipSubscriptionStateInfo_t *subsStateInfo,
+                                               const char *subs_state);
+
+PMH_EXTERN int sippmh_add_subscription_state(sipMessage_t *msg,
+                                             sipSubscriptionStateInfo_t *subsStateInfo);
+
+string_t sippmh_parse_displaystr(string_t displaystr);
+
+#define sippmh_parse_to(x) sippmh_parse_location(x)
+
+#define sippmh_free_to(x) sippmh_free_location(x)
+
+#define sippmh_parse_from(x) sippmh_parse_location(x)
+
+#define sippmh_free_from(x) sippmh_free_location(x)
+
+PMH_EXTERN int sippmh_add_call_info(sipMessage_t *sipMessage,
+                                    cc_call_info_t *callInfo);
+
+PMH_EXTERN uint32_t sippmh_parse_supported_require(const char *header,
+                                                   char **punsupported_options);
+
+PMH_EXTERN uint16_t sippmh_parse_allow_header(const char *header);
+
+PMH_EXTERN uint16_t sippmh_parse_accept_header(const char *header);
+
+PMH_EXTERN sipServiceControl_t
+            *sippmh_parse_service_control_body(char *msgBody, int msgLength);
+
+PMH_EXTERN void sippmh_free_service_control_info(sipServiceControl_t *scp);
+
+PMH_EXTERN sipJoinInfo_t *sippmh_parse_join_header(const char *header);
+
+PMH_EXTERN void sippmh_free_join_info(sipJoinInfo_t *join);
+
+PMH_EXTERN sipRet_t sippmh_add_join_header(sipMessage_t *message,
+                                           sipJoinInfo_t *join);
+
+PMH_EXTERN int32_t sippmh_parse_max_forwards(const char *max_fwd_hdr);
+
+PMH_EXTERN string_t sippmh_get_url_from_hdr(char *string);
+
+PMH_EXTERN int32_t sippmh_parse_message_summary(sipMessage_t *pSipMessage, sipMessageSummary_t *mesgSummary);
+
+/*
+ * The following SIP parser functions are the same as corresponding
+ * HTTP/1.1 message parser functions.
+ */
+#define sippmh_message_create()         httpish_msg_create()
+
+#define sippmh_message_free( x )        httpish_msg_free( x )
+
+#define sippmh_is_request( x )          httpish_msg_is_request( x , SIP_SCHEMA, SIP_SCHEMA_LEN)
+
+#define sippmh_is_message_complete( x ) httpish_msg_is_complete( x )
+
+#define sippmh_get_request_line( x )    httpish_msg_get_reqline( x )
+
+#define sippmh_get_response_line( x )   httpish_msg_get_respline( x )
+
+#define sippmh_free_request_line( x )   httpish_msg_free_reqline( x )
+
+#define sippmh_free_response_line( x )  httpish_msg_free_respline( x )
+
+#define sippmh_process_network_message( x, y, z) \
+        httpish_msg_process_network_msg( x, y, z)
+
+#define sippmh_get_code_class( x ) httpish_msg_get_code_class( x )
+
+#define sippmh_add_request_line( x, y, a, b ) \
+        httpish_msg_add_reqline( x, y, a, b )
+
+#define sippmh_add_response_line( x, y , a, b) \
+        httpish_msg_add_respline( x, y , a, b)
+
+#define sippmh_add_text_header( x, y, z ) \
+        httpish_msg_add_text_header( x, y, z )
+
+#define sippmh_add_int_header( x, y, z ) \
+        httpish_msg_add_int_header( x, y, z )
+
+#define sippmh_add_message_body( x, y, z, a, b, c, d) \
+        httpish_msg_add_body(x, y, z, a, b, c, d)
+
+#define sippmh_remove_header( x, y ) \
+        httpish_msg_remove_header( x, y )
+
+#define sippmh_msg_header_present( x, y ) \
+        httpish_msg_header_present(x, y)
+
+#define sippmh_get_header_val( x, y, c_y ) \
+        httpish_msg_get_header_val( x, y, c_y )
+
+#define sippmh_get_cached_header_val(x, y) \
+        httpish_msg_get_cached_header_val(x, y)
+
+#define sippmh_get_content_length( x ) \
+        httpish_msg_get_content_length( x )
+
+#define sippmh_get_all_headers( x, y ) \
+        httpish_msg_get_all_headers( x, y )
+
+#define sippmh_write_to_buf( x, y ) \
+        httpish_msg_write_to_buf( x, y )
+
+#define sippmh_write( x, y, z ) \
+        httpish_msg_write( x, y, z )
+
+#define sippmh_write_to_string( x ) \
+        httpish_msg_write_to_string( x )
+
+#define sippmh_get_num_headers( x ) \
+        httpish_msg_get_num_headers( x )
+
+#define sippmh_get_num_particular_headers( a, b, c, d, e ) \
+        httpish_msg_get_num_particular_headers( a, b, c, d, e )
+
+#define sippmh_get_header_vals( a, b, c, d, e) \
+        httpish_msg_get_header_vals( a, b, c, d, e)
+
+
+#define SIPPMH_VERSIONS_EQUAL(a, b) \
+        (cpr_strcasecmp(a, b) == 0)
+
+/*
+ * Utility to compare From: header values, given the values rather
+ * than the sipFrom_t struct.
+ * Returns TRUE if the same, FALSE if not.
+ * Expects NULL terminated strings.
+ */
+PMH_EXTERN boolean sippmh_compare_fromto(const char *a1, const char *a2);
+
+/*
+ * Same as compare_fromto, but for URL strings.
+ */
+PMH_EXTERN boolean sippmh_compare_urls(const char *u1, const char *u2);
+
+#define PARSE_ERR_NON_SIP_URL        1
+#define ERROR_1                      SIP_F_PREFIX"Non-SIP URL\n"
+
+#define PARSE_ERR_NO_MEMORY          2
+#define ERROR_2                      SIP_F_PREFIX"Out of memory\n"
+
+#define PARSE_ERR_SYNTAX             3
+#define ERROR_3                      SIP_F_PREFIX"Syntax error at %s\n"
+#define ERROR_3_1                    SIP_F_PREFIX"Unexpected char %c\n"
+
+#define PARSE_ERR_UNEXPECTED_EOS     4
+#define ERROR_4                      SIP_F_PREFIX"Unexpected end of string\n"
+
+#define PARSE_ERR_UNTERMINATED_STRING     5
+#define ERROR_5                      SIP_F_PREFIX"Unmatched \"\n"
+
+#define PARSE_ERR_UNMATCHED_BRACKET     6
+#define ERROR_6                      SIP_F_PREFIX"Unmatched <>\n"
+
+#define PARSE_ERR_INVALID_TTL_VAL       7
+#define ERROR_7                      SIP_F_PREFIX"%d: Invalid ttl value(range 0-255)\n"
+
+#define PARSE_ERR_NULL_PTR              8
+#define ERROR_8                      SIP_F_PREFIX"NULL Pointer\n"
+
+#define AT_SIGN                   '@'
+#define SEMI_COLON                ';'
+#define COLON                     ':'
+#define EQUAL_SIGN                '='
+#define ESCAPE_CHAR               '\\'
+#define TILDA                     '~'
+#define PERCENT                   '%'
+#define STAR                      '*'
+#define UNDERSCORE                '_'
+#define PLUS                      '+'
+#define SINGLE_QUOTE              '\''
+#define DOUBLE_QUOTE              '"'
+#define LEFT_ANGULAR_BRACKET      '<'
+#define RIGHT_ANGULAR_BRACKET     '>'
+#define LEFT_SQUARE_BRACKET       '['
+#define RIGHT_SQUARE_BRACKET      ']'
+#define LEFT_PARENTHESIS          '('
+#define RIGHT_PARENTHESIS         ')'
+#define DOLLAR_SIGN               '$'
+#define FORWARD_SLASH             '/'
+#define DOT                       '.'
+#define DASH                      '-'
+#define EXCLAMATION               '!'
+#define AMPERSAND                 '&'
+#define COMMA                     ','
+#define QUESTION_MARK             '?'
+#define SPACE                     ' '
+#define TAB                       '\t'
+#define NUMBER_SIGN               '#'
+#define LEFT_CURLY_BRACE          '{'
+#define RIGHT_CURLY_BRACE         '}'
+#define OR_SIGN                   '|'
+#define CARET                     '^'
+#define OPENING_SINGLE_QUOTE      '`'
+
+#define PROXY                     1
+#define REDIRECT                  2
+#define TRANSPORT_UNSPECIFIED     0
+#define TRANSPORT_UDP             1
+#define TRANSPORT_TCP             2
+#define TRANSPORT_TLS             3
+#define TRANSPORT_SCTP            4
+
+#define MAX_TTL_VAL               255
+
+#define SIPPMH_FREE_REQUEST_LINE(x)  sippmh_free_request_line(x); UTILFREE(x)
+#define SIPPMH_FREE_RESPONSE_LINE(x) sippmh_free_response_line(x); UTILFREE(x)
+#define SIPPMH_FREE_URL(x)           sippmh_genurl_free(x); UTILFREE(x)
+#define SIPPMH_FREE_MESSAGE(x)       sippmh_free_message(x) ; UTILFREE(x)
+
+size_t sippmh_convertURLCharToEscChar(const char *inputStr, size_t inputStrLen,
+                                      char *outputStr, size_t outputStrSize,
+                                      boolean null_terminate);
+
+size_t sippmh_converQuotedStrToEscStr(const char *inputStr, size_t inputStrLen,
+                         char *outputStr, size_t outputStrSize,
+                         boolean null_terminate);
+
+ccsipRet_e ccsip_process_network_message(sipMessage_t **sipmsg_p, char **buf,
+                                         unsigned long *nbytes_used,
+                                         char **display_msg);
+
+
+#endif /* _CCSIP_PMH_H_ */
diff --git a/libs/sipcc/core/sipstack/h/ccsip_protocol.h b/libs/sipcc/core/sipstack/h/ccsip_protocol.h
new file mode 100644 (file)
index 0000000..ca60335
--- /dev/null
@@ -0,0 +1,645 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_PROTOCOL_H_
+#define _CCSIP_PROTOCOL_H_
+
+#include "httpish_protocol.h"
+
+typedef struct sip_header_
+{
+    char *hname;
+    char *c_hname;
+} sip_header_t;
+
+
+#define SIP_VERSION "SIP/2.0"
+#define SIP_SCHEMA "SIP/"
+#define SIP_SCHEMA_LEN 4  /* length of the SIP_SCHEMA as defined above */
+
+/* Methods */
+
+#define SIP_METHOD_OPTIONS   "OPTIONS"
+#define SIP_METHOD_INVITE    "INVITE"
+#define SIP_METHOD_BYE       "BYE"
+#define SIP_METHOD_CANCEL    "CANCEL"
+#define SIP_METHOD_REGISTER  "REGISTER"
+#define SIP_METHOD_ACK       "ACK"
+#define SIP_METHOD_PRACK     "PRACK"
+#define SIP_METHOD_COMET     "COMET"
+#define SIP_METHOD_NOTIFY    "NOTIFY"
+#define SIP_METHOD_REFER     "REFER"
+#define SIP_METHOD_UPDATE    "UPDATE"
+#define SIP_METHOD_INFO      "INFO"
+#define SIP_METHOD_MESSAGE   "MESSAGE"
+#define SIP_METHOD_PUBLISH   "PUBLISH"
+#define SIP_METHOD_SUBSCRIBE "SUBSCRIBE"
+
+
+/* Headers */
+/*
+ * We maintain a direct pointer to the header value for the following
+ * headers.
+ * Cached headers : Begin
+ */
+#define FROM               0
+#define TO                 1
+#define VIA                2
+#define CALLID             3
+#define CSEQ               4
+#define CONTACT            5
+#define CONTENT_LENGTH     6
+#define CONTENT_TYPE       7
+#define RECORD_ROUTE       8
+#define REQUIRE            9
+#define ROUTE             10
+#define SUPPORTED         11
+/* Cached headers : End */
+
+#define SESSION            24
+#define EXPIRES            41
+#define LOCATION           42
+#define TIMESTAMP          43
+#define MAX_FORWARDS       44
+#define UNSUPPORTED        45
+#define ACCEPT             46
+#define ACCEPT_ENCODING    47
+#define ACCEPT_LANGUAGE    48
+#define ALLOW              49
+#define USER_AGENT         50
+#define SERVER             51
+#define DATE               52
+#define CISCO_GUID         53
+#define DIVERSION          54
+#define EVENT              55
+#define SESSION            24
+#define CALL_INFO          61
+#define JOIN               62
+
+/* Session Header Types - indicates the purpose of SDP bodies */
+#define SESSION_MEDIA      "Media"
+#define SESSION_QOS        "QoS"
+#define SESSION_SECURITY   "Security"
+
+/* Content-Disposition Types */
+#define SIP_CONTENT_DISPOSITION_UNKNOWN_VALUE 0
+#define SIP_CONTENT_DISPOSITION_RENDER_VALUE 1
+#define SIP_CONTENT_DISPOSITION_RENDER "render"
+#define SIP_CONTENT_DISPOSITION_SESSION_VALUE 2
+#define SIP_CONTENT_DISPOSITION_SESSION "session"
+#define SIP_CONTENT_DISPOSITION_ICON_VALUE 3
+#define SIP_CONTENT_DISPOSITION_ICON "icon"
+#define SIP_CONTENT_DISPOSITION_ALERT_VALUE 4
+#define SIP_CONTENT_DISPOSITION_ALERT "alert"
+#define SIP_CONTENT_DISPOSITION_PRECONDITION_VALUE 5
+#define SIP_CONTENT_DISPOSITION_PRECONDITION "precondition"
+
+/* Content-Disposition Handling */
+#define DISPOSITION_HANDLING "handling"
+#define DISPOSITION_HANDLING_REQUIRED "required"
+#define DISPOSITION_HANDLING_OPTIONAL "optional"
+
+/* Some headers common to HTTP .some of the headers can be received
+   in compact form ( SIP_C_ .. indicates compact header form ) */
+
+#define SIP_HEADER_CONTENT_LENGTH      HTTPISH_HEADER_CONTENT_LENGTH
+#define SIP_C_HEADER_CONTENT_LENGTH    HTTPISH_C_HEADER_CONTENT_LENGTH
+#define SIP_HEADER_CONTENT_TYPE        HTTPISH_HEADER_CONTENT_TYPE
+#define SIP_C_HEADER_CONTENT_TYPE      "c"
+#define SIP_HEADER_CONTENT_ENCODING    HTTPISH_HEADER_CONTENT_ENCODING
+#define SIP_C_HEADER_CONTENT_ENCODING  "e"
+#define SIP_HEADER_CONTENT_ID          HTTPISH_HEADER_CONTENT_ID
+
+#define SIP_HEADER_USER_AGENT    HTTPISH_HEADER_USER_AGENT
+#define SIP_HEADER_SERVER        HTTPISH_HEADER_SERVER
+#define SIP_HEADER_DATE          HTTPISH_HEADER_DATE
+
+#define SIP_HEADER_VIA           HTTPISH_HEADER_VIA
+#define SIP_C_HEADER_VIA         "v"
+#define SIP_HEADER_MAX_FORWARDS  HTTPISH_HEADER_MAX_FORWARDS
+#define SIP_HEADER_EXPIRES       HTTPISH_HEADER_EXPIRES
+#define SIP_HEADER_LOCATION      HTTPISH_HEADER_LOCATION
+#define SIP_C_HEADER_LOCATION    "m"
+
+/* Headers specific to SIP .Some of the headers can be received in
+   compact form */
+
+#define SIP_HEADER_FROM        "From"
+#define SIP_C_HEADER_FROM      "f"
+#define SIP_HEADER_TO          "To"
+#define SIP_C_HEADER_TO        "t"
+#define SIP_HEADER_CSEQ        "CSeq"
+#define SIP_HEADER_TIMESTAMP   "Timestamp"
+#define SIP_HEADER_CALLID      "Call-ID"
+#define SIP_C_HEADER_CALLID    "i"
+#define SIP_HEADER_REQUIRE     "Require"
+#define SIP_HEADER_SUPPORTED   "Supported"
+#define SIP_C_HEADER_SUPPORTED "k"
+#define SIP_HEADER_UNSUPPORTED "Unsupported"
+#define SIP_HEADER_CONTACT     "Contact"
+#define SIP_C_HEADER_CONTACT   SIP_C_HEADER_LOCATION
+#define SIP_HEADER_CISCO_GUID  "Cisco-Guid"
+#define SIP_HEADER_WARN        "Warning"
+
+#define SIP_HEADER_ACCEPT      "Accept"
+#define SIP_HEADER_ACCEPT_ENCODING "Accept-Encoding"
+#define SIP_HEADER_ACCEPT_LANGUAGE "Accept-Language"
+#define SIP_HEADER_ALLOW        "Allow"
+
+#define SIP_HEADER_RECORD_ROUTE "Record-Route"
+#define SIP_HEADER_ROUTE        "Route"
+#define SIP_HEADER_SESSION      "Session"
+#define SIP_HEADER_ALSO         "Also"
+#define SIP_HEADER_REQUESTED_BY "Requested-By"
+#define SIP_HEADER_DIVERSION    "Diversion"
+#define SIP_HEADER_CC_DIVERSION "CC-Diversion"
+#define SIP_HEADER_CC_REDIRECT  "CC-Redirect"
+#define SIP_HEADER_ALERT_INFO   "Alert-Info"
+
+#define SIP_HEADER_RSEQ         "RSeq"
+#define SIP_HEADER_RACK         "RAck"
+#define SIP_HEADER_CONTENT_DISP "Content-Disposition"
+
+/* call stats */
+#define SIP_RX_CALL_STATS       "RTP-RxStat"
+#define SIP_TX_CALL_STATS       "RTP-TxStat"
+
+/* Spam Specific */
+#define SIP_HEADER_AUTHORIZATION       "Authorization"
+#define SIP_HEADER_PROXY_AUTHORIZATION "Proxy-Authorization"
+#define SIP_HEADER_PROXY_AUTHENTICATE  "Proxy-Authenticate"
+#define SIP_HEADER_WWW_AUTHENTICATE    "WWW-Authenticate"
+
+/* Refer parameters */
+#define SIP_HEADER_REFER_TO        "Refer-To"
+#define SIP_C_HEADER_REFER_TO      "r"
+#define SIP_HEADER_REFERRED_BY     "Referred-By"
+#define SIP_C_HEADER_REFERRED_BY   "b"
+
+#define SIP_HEADER_REPLACES          "Replaces"
+#define SIP_HEADER_PROXY_AUTH        "Proxy-Authorization"
+#define SIP_HEADER_ACCEPT_CONTACT    "Accept-Contact"
+#define SIP_C_HEADER_ACCEPT_CONTACT  "a"
+
+#define SIP_HEADER_REMOTE_PARTY_ID   "Remote-Party-ID"
+
+#define SIP_HEADER_EVENT        "Event"
+#define SIP_C_HEADER_EVENT        "o"
+
+#define SIP_HEADER_SIPIFMATCH   "SIP-If-Match"
+#define SIP_HEADER_SIPETAG       "SIP-ETag"
+#define SIP_HEADER_RETRY_AFTER  "Retry-After"
+#define SIP_HEADER_MIN_EXPIRES  "Min-Expires"
+#define SIP_HEADER_REASON       "Reason"
+
+/* Event values */
+#define SIP_EVENT_REFER         "refer"
+#define SIP_EVENT_DIALOG        "dialog"
+#define SIP_EVENT_KPML          "kpml"
+#define SIP_EVENT_PRESENCE      "presence"
+#define SIP_EVENT_CONFIG        "sip-profile"
+#define SIP_EVENT_MWI           "message-summary"
+
+/* Event param */
+#define SIP_EVENT_ID            "id"
+
+/* Subscription states */
+#define SIP_HEADER_SUBSCRIPTION_STATE       "Subscription-State"
+#define SIP_SUBSCRIPTION_STATE_ACTIVE       "active"
+#define SIP_SUBSCRIPTION_STATE_PENDING      "pending"
+#define SIP_SUBSCRIPTION_STATE_TERMINATED   "terminated"
+/* Subscription state params */
+#define SIP_SUBSCRIPTION_STATE_EXPIRES      "expires"
+#define SIP_SUBSCRIPTION_STATE_REASON       "reason"
+#define SIP_SUBSCRIPTION_STATE_RETRY_AFTER  "retry-after"
+/* Subscription state reason values */
+#define SIP_SUBSCRIPTION_STATE_REASON_DEACTIVATED "deactivated"
+#define SIP_SUBSCRIPTION_STATE_REASON_PROBATION   "probation"
+#define SIP_SUBSCRIPTION_STATE_REASON_REJECTED    "rejected"
+#define SIP_SUBSCRIPTION_STATE_REASON_TIMEOUT     "timeout"
+#define SIP_SUBSCRIPTION_STATE_REASON_GIVEUP      "giveup"
+#define SIP_SUBSCRIPTION_STATE_REASON_NORESOURCE  "noresource"
+
+/* Content-Type values */
+#define SIP_CONTENT_TYPE_UNKNOWN_VALUE            0
+#define SIP_CONTENT_TYPE_UNKNOWN                  "application/unknown"
+
+#define SIP_CONTENT_TYPE_SDP_VALUE                1
+#define SIP_CONTENT_TYPE_SDP                      "application/sdp"
+
+#define SIP_CONTENT_TYPE_SIP_VALUE                2
+#define SIP_CONTENT_TYPE_SIP                      "application/sip"
+
+#define SIP_CONTENT_TYPE_SIPFRAG_VALUE            3
+#define SIP_CONTENT_TYPE_SIPFRAG                  "message/sipfrag"
+
+#define SIP_CONTENT_TYPE_DIALOG_VALUE             4
+#define SIP_CONTENT_TYPE_DIALOG                   "application/dialog-info+xml"
+
+#define SIP_CONTENT_TYPE_MWI_VALUE                5
+#define SIP_CONTENT_TYPE_MWI                      "application/simple-message-summary"
+
+#define SIP_CONTENT_TYPE_KPML_REQUEST_VALUE       6
+#define SIP_CONTENT_TYPE_KPML_REQUEST             "application/kpml-request+xml"
+
+#define SIP_CONTENT_TYPE_KPML_RESPONSE_VALUE      7
+#define SIP_CONTENT_TYPE_KPML_RESPONSE            "application/kpml-response+xml"
+
+#define SIP_CONTENT_TYPE_REMOTECC_REQUEST_VALUE   8
+#define SIP_CONTENT_TYPE_REMOTECC_REQUEST         "application/x-cisco-remotecc-request+xml"
+
+#define SIP_CONTENT_TYPE_REMOTECC_RESPONSE_VALUE  9
+#define SIP_CONTENT_TYPE_REMOTECC_RESPONSE        "application/x-cisco-remotecc-response+xml"
+
+#define SIP_CONTENT_TYPE_CTI_VALUE                10
+#define SIP_CONTENT_TYPE_CTI                      "application/x-cisco-cti+xml"
+
+#define SIP_CONTENT_TYPE_CMXML_VALUE              11
+#define SIP_CONTENT_TYPE_CMXML                    "application/x-cisco-remotecc-cm+xml"
+
+#define SIP_CONTENT_TYPE_MULTIPART_MIXED_VALUE    12
+#define SIP_CONTENT_TYPE_MULTIPART_MIXED          "multipart/mixed"
+
+#define SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE_VALUE    13
+#define SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE    "multipart/alternative"
+
+#define SIP_CONTENT_TYPE_TEXT_PLAIN_VALUE         14
+#define SIP_CONTENT_TYPE_TEXT_PLAIN               "text/plain"
+
+#define SIP_CONTENT_TYPE_PRESENCE_VALUE           15
+#define SIP_CONTENT_TYPE_PRESENCE                 "application/pidf+xml"
+
+#define SIP_CONTENT_TYPE_CONFIGAPP_VALUE         16
+#define SIP_CONTENT_TYPE_CONFIGAPP               "application/x-cisco-config+xml"
+
+#define SIP_CONTENT_TYPE_MEDIA_CONTROL           "application/media_control+xml"
+
+/* Content-Type for multipart-mime */
+#define SIP_CONTENT_TYPE_MULTIPART               "multipart/mixed"
+#define SIP_CONTENT_BOUNDARY                     "boundary"
+#define SIP_CONTENT_BOUNDARY_TEXT                "uniqueBoundary"
+
+/* Content-Encoding */
+/* At this time identity is the only one recognize */
+#define SIP_CONTENT_ENCODING_IDENTITY            "identity"
+#define SIP_CONTENT_ENCODING_IDENTITY_VALUE      0
+#define SIP_CONTENT_ENCODING_UNKNOWN_VALUE       1
+
+/* Call-info header */
+#define SIP_HEADER_CALL_INFO                     "Call-Info"
+#define SIP_CALL_INFO_PURPOSE                    "Purpose"
+#define SIP_CALL_INFO_PURPOSE_INFO               "info"
+#define SIP_CALL_INFO_PURPOSE_ICON               "icon"
+#define SIP_CALL_INFO_PURPOSE_CARD               "card"
+#define SIP_CALL_INFO_ORIENTATION                "Orientation"
+#define SIP_CALL_INFO_ORIENTATION_TO             "To"
+#define SIP_CALL_INFO_ORIENTATION_FROM           "From"
+#define SIP_CALL_INFO_SECURITY                   "Security"
+#define SIP_CALL_INFO_SECURITY_UNKNOWN           "Unknown"
+#define SIP_CALL_INFO_SECURITY_AUTHENTICATED     "Authenticated"
+#define SIP_CALL_INFO_SECURITY_NOT_AUTHENTICATED "NotAuthenticated"
+
+/* via-params */
+#define MAX_TTL_VAL             255
+#define SIP_WELL_KNOWN_PORT     5060
+#define SIP_WELL_KNOWN_PORT_STR "5060"
+#define VIA_HIDDEN              "hidden"
+#define VIA_TTL                 "ttl"
+#define VIA_MADDR               "maddr"
+#define VIA_RECEIVED            "received"
+#define VIA_BRANCH              "branch"
+#define VIA_BRANCH_START        "z9hG4bK"
+
+/* CC-Diversion Parameters */
+#define DIVERSION_REASON        "reason"
+#define DIVERSION_COUNTER       "counter"
+#define DIVERSION_LIMIT         "limit"
+#define DIVERSION_PRIVACY       "privacy"
+#define DIVERSION_SCREEN        "screen"
+
+/* CC-Redirect Parameters */
+#define CC_REDIRECT_REASON      "redir-reason"
+#define CC_REDIRECT_COUNTER     "redir-counter"
+#define CC_REDIRECT_LIMIT       "redir-limit"
+
+/* CC-Diversion Reason Parameter values */
+// REASON_UNKNOWN conflicts with winreg.h on Windows.
+#ifdef REASON_UNKNOWN
+#undef REASON_UNKNOWN
+#endif
+#define REASON_UNKNOWN          "unknown"
+#define REASON_USER_BUSY        "user-busy"
+#define REASON_NO_ANSWER        "no-answer"
+#define REASON_UNCONDITIONAL    "unconditional"
+#define REASON_DEFLECTION       "deflection"
+#define REASON_FOLLOW_ME        "follow-me"
+#define REASON_OUT_OF_SERVICE   "out-of-service"
+#define REASON_TIME_OF_DAY      "time-of-day"
+#define REASON_DO_NOT_DISTURB   "do-not-disturb"
+#define REASON_UNAVAILABLE      "unavailable"
+#define REASON_AWAY             "away"
+
+/* Replaces parameters */
+#define SIGNATURE_SCHEME        "scheme"
+#define TO_TAG                  "to-tag"
+#define FROM_TAG                "from-tag"
+
+/* Remote-Party-Id parameters */
+#define RPID_SCREEN             "screen"
+#define RPID_PARTY_TYPE         "party"
+#define RPID_ID_TYPE            "id-type"
+#define RPID_PRIVACY            "privacy"
+#define RPID_NP                 "np"
+
+#define RPID_CALLBACK           "x-cisco-callback-number="
+
+#define RPID_SCREEN_LEN         (sizeof(RPID_SCREEN) - 1)
+#define RPID_PARTY_TYPE_LEN     (sizeof(RPID_PARTY_TYPE) - 1)
+#define RPID_ID_TYPE_LEN        (sizeof(RPID_ID_TYPE) - 1)
+#define RPID_PRIVACY_LEN        (sizeof(RPID_PRIVACY) - 1)
+#define RPID_NP_LEN             (sizeof(RPID_NP) - 1)
+#define RPID_CALLBACK_LEN       (sizeof(RPID_CALLBACK) - 1)
+
+#define KPML_ID_CALLID          "call-id"
+#define KPML_ID_FROM_TAG        "from-tag"
+#define KPML_ID_TO_TAG          "to-tag"
+#define KPML_ID_CALLID_LEN      (sizeof(SIP_HEADER_CALLID)-1)
+#define KPML_ID_FROM_TAG_LEN    (sizeof(FROM_TAG)-1)
+#define KPML_ID_TO_TAG_LEN      (sizeof(TO_TAG)-1)
+
+#define PRIVACY_OFF        "off"
+#define PRIVACY_NAME       "name"
+#define PRIVACY_URI        "uri"
+#define PRIVACY_FULL       "full"
+#define SCREEN_NO          "no"
+#define SCREEN_YES         "yes"
+#define PARTY_TYPE_CALLING "calling"
+#define PARTY_TYPE_CALLED  "called"
+
+#define SIP_HEADER_ALLOW_EVENTS "Allow-Events"
+#define SIP_HEADER_MIME_VERSION "MIME-Version"
+#define SIP_MIME_VERSION_VALUE  "1.0"
+
+/* Require and Supported header params */
+#define REQ_SUPP_PARAM_REPLACES      "replaces"
+#define REQ_SUPP_PARAM_100REL        "100rel"
+#define REQ_SUPP_PARAM_EARLY_SESSION "early-session"
+#define REQ_SUPP_PARAM_JOIN          "join"
+#define REQ_SUPP_PARAM_PATH          "path"
+#define REQ_SUPP_PARAM_PRECONDITION  "precondition"
+#define REQ_SUPP_PARAM_PREF          "pref"
+#define REQ_SUPP_PARAM_PRIVACY       "privacy"
+#define REQ_SUPP_PARAM_SEC_AGREE     "sec-agree"
+#define REQ_SUPP_PARAM_TIMER         "timer"
+#define REQ_SUPP_PARAM_NOREFERSUB    "norefersub"
+#define REQ_SUPP_PARAM_EXTENED_REFER "extended-refer"
+#define REQ_SUPP_PARAM_CISCO_CALLINFO "X-cisco-callinfo"
+#define REQ_SUPP_PARAM_CISCO_SERVICEURI      "X-cisco-serviceuri"
+#define REQ_SUPP_PARAM_CISCO_ESCAPECODES     "X-cisco-escapecodes"
+#define REQ_SUPP_PARAM_CISCO_SERVICE_CONTROL "X-cisco-service-control"
+#define REQ_SUPP_PARAM_CISCO_SRTP_FALLBACK   "X-cisco-srtp-fallback"
+#define REQ_SUPP_PARAM_CISCO_CONFIG "X-cisco-config"
+#define REQ_SUPP_PARAM_SDP_ANAT "sdp-anat"
+/* Add defines for the SIP Interfcae Specification (SIS) protocol version tags*/
+#define REQ_SUPP_PARAM_CISCO_SISTAG "X-cisco-sis-"
+#define SIS_CURRENT_PROTOCOL_VERSION  "5.2.0"
+#define SIS_PROTOCOL_MAJOR_VERSION_SEADRAGON  1
+#define SIS_PROTOCOL_MAJOR_VERSION_MUSTER  2
+#define SIS_PROTOCOL_MAJOR_VERSION_UNISON  3
+#define SIS_PROTOCOL_MAJOR_VERSION_GUILD   4
+#define SIS_PROTOCOL_MAJOR_VERSION_ANGELFIRE   5
+#define SIS_PROTOCOL_MINOR_VERSION_ANGELFIRE   1
+#define SIS_PROTOCOL_MINOR_VERSION_MONTBLANC   1
+#define REQ_SUPP_PARAM_CISCO_SIPVER REQ_SUPP_PARAM_CISCO_SISTAG SIS_CURRENT_PROTOCOL_VERSION
+#define REQ_SUPP_PARAM_CISCO_MONREC "X-cisco-monrec"
+/* Add define for CME version negotiation */
+#define REQ_SUPP_PARAM_CISCO_CME_SISTAG "X-cisco-cme-sis-"
+
+/* Contact parameters */
+#define REQ_CONT_PARAM_CISCO_NEWREG          "X-cisco-newreg"
+
+/* Max-forwards header */
+#define SIP_MAX_FORWARDS_DEFAULT_STR     "70"
+#define SIP_MAX_FORWARDS_DEFAULT_VALUE   70
+
+/* Join header */
+#define SIP_HEADER_JOIN               "Join"
+#define SIP_HEADER_JOIN_FROM_TAG      "from-tag"
+#define SIP_HEADER_JOIN_TO_TAG        "to-tag"
+
+/* Info-Package headers */
+#define SIP_HEADER_SEND_INFO    "Send-Info"
+#define SIP_HEADER_RECV_INFO    "Recv-Info"
+#define SIP_HEADER_INFO_PACKAGE "Info-Package"
+
+/*
+ * ALERT : If you are defining a compact header more than 2 bytes long, modify
+ * routine compact_hdr_cmp() in file httpish.c
+ */
+
+/*
+ * Revised SIP drafts say that we use Contact and not Location in the header.
+ * But we are supporting both currently for MCI
+ */
+
+/*   S T A T U S   C O D E S  */
+
+/* Informational Status Codes 1xx */
+
+#define SIP_1XX_TRYING           100 /*  "Trying"  */
+#define SIP_1XX_RINGING          180 /*  "Ringing" */
+#define SIP_1XX_CALL_FWD         181 /*  "Call is being forwaded" */
+#define SIP_1XX_QUEUED           182 /*  "Queued" */
+#define SIP_1XX_SESSION_PROGRESS 183 /* "Session Progress" */
+
+/* Success Status Code 2xx */
+#define SIP_SUCCESS_SETUP        200 /* OK/Success */
+#define SIP_ACCEPTED             202 /* Accepted */
+#define SIP_STATUS_SUCCESS SIP_SUCCESS_SETUP
+
+/* Redirection Status Codes 3xx */
+#define SIP_RED_MULT_CHOICES     300 /* Multiple Choices */
+#define SIP_RED_MOVED_PERM       301 /* Moved Permanently */
+#define SIP_RED_MOVED_TEMP       302 /* Moved Temporarily */
+#define SIP_RED_SEE_OTHER        303 /* See Other */
+#define SIP_RED_USE_PROXY        305 /* Use Proxy */
+#define SIP_RED_ALT_SERVICE      380 /* Alternative Service */
+
+/* Client Error Status Codes 4xx */
+#define SIP_CLI_ERR_BAD_REQ      400 /* Bad Request */
+#define SIP_CLI_ERR_UNAUTH       401 /* Unauthorized */
+#define SIP_CLI_ERR_PAY_REQD     402 /* Payment Required */
+#define SIP_CLI_ERR_FORBIDDEN    403 /* Forbidden */
+#define SIP_CLI_ERR_NOT_FOUND    404 /* Not Found */
+#define SIP_CLI_ERR_NOT_ALLOWED  405 /* Method Not Allowed */
+#define SIP_CLI_ERR_NOT_ACCEPT   406 /* Not Acceptable */
+#define SIP_CLI_ERR_PROXY_REQD   407 /* Proxy Authentication Required */
+
+#define SIP_CLI_ERR_REQ_TIMEOUT  408 /* Request Timeout */
+#define SIP_CLI_ERR_CONFLICT     409 /* Conflict */
+#define SIP_CLI_ERR_GONE         410 /* Gone */
+#define SIP_CLI_ERR_LEN_REQD     411 /* Length Required */
+#define SIP_CLI_ERR_COND_FAIL    412 /* Conditional req failed */
+#define SIP_CLI_ERR_LARGE_MSG    413 /* Request Message Body Too Large */
+#define SIP_CLI_ERR_LARGE_URI    414 /* Request-URI Too Large */
+#define SIP_CLI_ERR_MEDIA        415 /* Unsupported Media Type */
+#define SIP_CLI_ERR_EXTENSION    420 /* Bad Extension */
+#define SIP_CLI_ERR_INTERVAL_TOO_SMALL 423 /* Duration too small */
+#define SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED 433 /* Anonymity Disallowed */
+#define SIP_CLI_ERR_NOT_AVAIL    480 /* Temporarily Not Available */
+#define SIP_CLI_ERR_CALLEG       481 /* Call Leg/Transaction Does Not Exist */
+#define SIP_CLI_ERR_LOOP_DETECT  482 /* Loop Detected */
+#define SIP_CLI_ERR_MANY_HOPS    483 /* Too Many Hops */
+#define SIP_CLI_ERR_ADDRESS      484 /* Address Incomplete */
+#define SIP_CLI_ERR_AMBIGUOUS    485 /* Ambiguous */
+#define SIP_CLI_ERR_BUSY_HERE    486 /* Busy here */
+#define SIP_CLI_ERR_REQ_CANCEL   487 /* Request Cancelled */
+#define SIP_CLI_ERR_NOT_ACCEPT_HERE 488 /* Not Acceptable Here */
+#define SIP_CLI_ERR_BAD_EVENT    489 /* Bad Event */
+#define SIP_CLI_ERR_REQ_PENDING  491 /* Request Pending */
+
+/* Server Error Status Codes 5xx */
+#define SIP_SERV_ERR_INTERNAL    500 /* Internal Server Error */
+#define SIP_SERV_ERR_NOT_IMPLEM  501 /* Not Implemented */
+#define SIP_SERV_ERR_BAD_GW      502 /* Bad Gateway */
+#define SIP_SERV_ERR_UNAVAIL     503 /* Service Unavailable */
+#define SIP_SERV_ERR_GW_TIMEOUT  504 /* Gateway Timeout */
+#define SIP_SERV_ERR_SIP_VER     505 /* SIP Version not supported */
+#define SIP_SERV_ERR_PRECOND_FAILED 580 /* Precondition Failed */
+
+/* Global Failure Status Codes 6xx */
+#define SIP_FAIL_BUSY            600 /* Busy */
+#define SIP_FAIL_DECLINE         603 /* Decline */
+#define SIP_FAIL_NOT_EXIST       604 /* Does not exist anywhere */
+#define SIP_FAIL_NOT_ACCEPT      606 /* Not Acceptable */
+
+/* SIP Warning Codes 3xx */
+#define SIP_WARN_INCOMPAT_NETWORK_PROT  300 /* Incompatible Network Protocol */
+#define SIP_WARN_INCOMPAT_NETWORK_ADDR  301 /* Incompatible Network Address */
+#define SIP_WARN_INCOMPAT_TRANS_PROT    302 /* Incompatible Transport Protocol */
+#define SIP_WARN_INCOMPAT_BANDW_UNITS   303 /* Incompatible Bandwidth Units */
+#define SIP_WARN_MEDIA_TYPE_UNAVAIL     304 /* Media Type(s) Unavailable */
+#define SIP_WARN_INCOMPAT_MEDIA_FORMAT  305 /* Incompatible Media Formats */
+#define SIP_WARN_UNKNOWN_MEDIA_ATTRIB   306 /* Media Attribute not understood */
+#define SIP_WARN_UNKNOWN_SDP_PARAM      307 /* SDP Parameter not understood */
+#define SIP_WARN_MISC                   399 /* Miscellaneous */
+
+/* Other warnings */
+#define SIP_WARN_PROCESSING_PREVIOUS_REQUEST 901
+
+/*   R E A S O N   P H R A S E S  */
+
+/* Informational  1xx */
+
+#define SIP_1XX_TRYING_PHRASE    "Trying"
+#define SIP_1XX_RINGING_PHRASE   "Ringing"
+#define SIP_1XX_CALL_FWD_PHRASE  "Call is being forwaded"
+#define SIP_1XX_QUEUED_PHRASE    "Queued"
+#define SIP_1XX_SESSION_PROGRESS_PHRASE "Session Progress"
+
+/* Success  2xx */
+#define SIP_SUCCESS_SETUP_PHRASE  "OK"
+#define SIP_ACCEPTED_PHRASE  "Accepted"
+
+/* Redirection  3xx */
+#define SIP_RED_MULT_CHOICES_PHRASE  "Multiple Choices"
+#define SIP_RED_MOVED_PERM_PHRASE    "Moved Permanently"
+#define SIP_RED_MOVED_TEMP_PHRASE    "Moved Temporarily"
+#define SIP_RED_SEE_OTHER_PHRASE     "See Other"
+#define SIP_RED_USE_PROXY_PHRASE     "Use Proxy"
+#define SIP_RED_ALT_SERVICE_PHRASE   "Alternative Service"
+
+/* Client Error  4xx */
+#define SIP_CLI_ERR_BAD_REQ_PHRASE     "Bad Request"
+#define SIP_CLI_ERR_UNAUTH_PHRASE      "Unauthorized"
+#define SIP_CLI_ERR_PAY_REQD_PHRASE    "Payment Required"
+#define SIP_CLI_ERR_FORBIDDEN_PHRASE   "Forbidden"
+#define SIP_CLI_ERR_NOT_FOUND_PHRASE   "Not Found"
+#define SIP_CLI_ERR_NOT_ALLOWED_PHRASE "Method Not Allowed"
+#define SIP_CLI_ERR_NOT_ACCEPT_PHRASE  "Not Acceptable"
+#define SIP_CLI_ERR_PROXY_REQD_PHRASE  "Proxy Authentication Required"
+
+#define SIP_CLI_ERR_REQ_TIMEOUT_PHRASE "Request Timeout"
+#define SIP_CLI_ERR_CONFLICT_PHRASE    "Conflict"
+#define SIP_CLI_ERR_GONE_PHRASE        "Gone"
+#define SIP_CLI_ERR_LEN_REQD_PHRASE    "Length Required"
+#define SIP_CLI_ERR_LARGE_MSG_PHRASE   "Request Message Body Too Large"
+#define SIP_CLI_ERR_LARGE_URI_PHRASE   "Request-URI Too Large"
+#define SIP_CLI_ERR_MEDIA_PHRASE       "Unsupported Media Type"
+#define SIP_CLI_ERR_EXTENSION_PHRASE   "Bad Extension"
+#define SIP_CLI_ERR_NOT_AVAIL_PHRASE   "Temporarily Not Available"
+#define SIP_CLI_ERR_CALLEG_PHRASE      "Call Leg/Transaction Does Not Exist"
+#define SIP_CLI_ERR_SUBS_DOES_NOT_EXIST_PHRASE "Subscription Does Not Exist"
+#define SIP_CLI_ERR_LOOP_DETECT_PHRASE "Loop Detected"
+#define SIP_CLI_ERR_MANY_HOPS_PHRASE   "Too Many Hops"
+#define SIP_CLI_ERR_ADDRESS_PHRASE     "Address Incomplete"
+#define SIP_CLI_ERR_AMBIGUOUS_PHRASE   "Ambiguous"
+#define SIP_CLI_ERR_BUSY_HERE_PHRASE   "Busy here"
+#define SIP_CLI_ERR_REQ_CANCEL_PHRASE  "Request Cancelled"
+#define SIP_CLI_ERR_NOT_ACCEPT_HERE_PHRASE "Not Acceptable Here"
+#define SIP_CLI_ERR_BAD_EVENT_PHRASE   "Bad Event"
+#define SIP_CLI_ERR_INTERVAL_TOO_SMALL_PHRASE "Interval too small"
+#define SIP_CLI_ERR_INTERVAL_TOO_LARGE_PHRASE "Interval too large"
+#define SIP_CLI_ERR_ANONYMITY_NOT_ALLOWED_PHRASE "Anonymity Disallowed"
+#define SIP_CLI_ERR_REQ_PENDING_PHRASE "Request Pending"
+
+/* Server Error  5xx */
+#define SIP_SERV_ERR_INTERNAL_PHRASE   "Internal Server Error"
+#define SIP_SERV_ERR_NOT_IMPLEM_PHRASE "Not Implemented"
+#define SIP_SERV_ERR_BAD_GW_PHRASE     "Bad Gateway"
+#define SIP_SERV_ERR_UNAVAIL_PHRASE    "Service Unavailable"
+#define SIP_SERV_ERR_GW_TIMEOUT_PHRASE "Gateway Timeout"
+#define SIP_SERV_ERR_SIP_VER_PHRASE    "SIP Version not supported"
+#define SIP_SERV_ERR_PRECOND_FAILED_PHRASE "Precondition Failed"
+
+/* Global Failure  6xx */
+#define SIP_FAIL_BUSY_PHRASE           "Busy"
+#define SIP_FAIL_DECLINE_PHRASE        "Decline"
+#define SIP_FAIL_NOT_EXIST_PHRASE      "Does not exist anywhere"
+#define SIP_FAIL_NOT_ACCEPT_PHRASE     "Not Acceptable"
+
+/* SIP Warning Codes 3xx Phrases */
+#define SIP_WARN_INCOMPAT_NETWORK_PROT_PHRASE "\"Incompatible Network Protocol\""
+#define SIP_WARN_INCOMPAT_NETWORK_ADDR_PHRASE "\"Incompatible Network Address\""
+#define SIP_WARN_INCOMPAT_TRANS_PROT_PHRASE "\"Incompatible Transport Protocol\""
+#define SIP_WARN_INCOMPAT_BANDW_UNITS_PHRASE "\"Incompatible Bandwidth Units\""
+#define SIP_WARN_MEDIA_TYPE_UNAVAIL_PHRASE "\"Media Type(s) Unavailable\""
+#define SIP_WARN_INCOMPAT_MEDIA_FORMAT_PHRASE "\"Incompatible Media Formats\""
+#define SIP_WARN_UNKNOWN_MEDIA_ATTRIB_PHRASE "\"Media Attribute not understood\""
+#define SIP_WARN_UNKNOWN_SDP_PARAM_PHRASE  "\"SDP Parameter not understood\""
+#define SIP_WARN_REFER_AMBIGUOUS_PHRASE  "\"Ambiguous.Multiple Contacts Found.\""
+
+#define SIP_STATUS_PHRASE_NONE         "Unknown"
+
+/* Client Error 400 Bad Request detailed explaination */
+#define SIP_CLI_ERR_BAD_REQ_MEDIA_COMP_FAILED     "Bad Request - 'Media info comparison failed'"
+#define SIP_CLI_ERR_BAD_REQ_SDP_ERROR             "Bad Request - 'Invalid SDP information'"
+#define SIP_CLI_ERR_BAD_REQ_RECORD_ROUTE          "Bad Request - 'Malformed/Missing Record Route'"
+#define SIP_CLI_ERR_BAD_REQ_CONTACT_FIELD         "Bad Request - 'Malformed/Missing Contact field'"
+#define SIP_CLI_ERR_BAD_REQ_CALLID_ABSENT         "Bad Request - 'Callid absent'"
+#define SIP_CLI_ERR_BAD_REQ_CSEQ_FIELD            "Bad Request - 'Error in Cseq field'"
+#define SIP_CLI_ERR_BAD_REQ_FROM_OR_TO_FIELD      "Bad Request - 'Error in From and/or To field'"
+#define SIP_CLI_ERR_BAD_REQ_MEDIA_NEGOTIATION     "Bad Request - 'Media Negotiation Failed'"
+#define SIP_CLI_ERR_BAD_REQ_URL_ERROR             "Bad Request - 'Malformed/Missing URL'"
+#define SIP_CLI_ERR_BAD_REQ_ToURL_ERROR           "Bad Request - 'Malformed/Missing TO: field'"
+#define SIP_CLI_ERR_BAD_REQ_FROMURL_ERROR         "Bad Request - 'Malformed/Missing FROM: field'"
+#define SIP_CLI_ERR_BAD_REQ_CALLID_ERROR          "Bad Request - 'Malformed/Missing CALLID field'"
+#define SIP_CLI_ERR_BAD_REQ_REQLINE_ERROR         "Bad Request - 'Malformed/Missing REQUEST LINE'"
+#define SIP_CLI_ERR_BAD_REQ_IPADDRESS_ERROR       "Bad Request - 'Invalid IP Address'"
+#define SIP_CLI_ERR_BAD_REQ_CONTENT_LENGTH_ERROR  "Bad Request - 'Content length Error'"
+#define SIP_CLI_ERR_BAD_REQ_CONTENT_TYPE_ERROR    "Bad Request - 'Malformed/Missing SIP CONTENT TYPE field'"
+#define SIP_CLI_ERR_BAD_REQ_PHRASE_DIVERSION      "Bad Request - 'Malformed CC-Diversion Header'"
+#define SIP_CLI_ERR_BAD_REQ_VIA_OR_CSEQ           "Bad Request - 'Malformed/Missing VIA OR CSEQ'"
+#define SIP_CLI_ERR_BAD_REQ_PHRASE_REFER          "Bad Request - 'Malformed REFER Request'"
+#define SIP_CLI_ERR_BAD_REQ_PHRASE_REFER_TO       "Bad Request - 'Malformed Refer-To Header'"
+#define SIP_CLI_ERR_BAD_REQ_PHRASE_REFER_BY       "Bad Request - 'Missing   Refer-By Header'"
+#define SIP_CLI_ERR_BAD_REQ_PHRASE_REPLACES       "Bad Request - 'Malformed Replaces Header'"
+#define SIP_CLI_ERR_BAD_REQ_METHOD_UNKNOWN        "Bad Request - 'Unable to obtain SIP method'"
+#define SIP_CLI_ERR_BAD_REQ_BAD_BODY_ENCODING     "Bad Request - 'Unable to decode body'"
+#define SIP_CLI_ERR_BAD_REQ_SUBSCRIPTION_DELETED  "Bad Request - 'Subscription Terminated'"
+#define SIP_CLI_ERR_BAD_REQ_NO_BODY               "Bad Request - 'Body Expected'"
+#define SIP_CLI_ERR_BAD_REQ_NO_SUBSCRIPTION_HEADER "Bad Request - 'Malformed/Missing Subscription-State Header'"
+#define SIP_CLI_ERR_BAD_REQ_CONTENT_ID_ERROR      "Bad Request - 'Invalid Content-Id field'"
+#define SIP_CLI_ERR_BAD_REQ_REQUIRE_HDR           "Bad Request - 'Malformed/Missing Require header'"
+#endif /*_CCSIP_PROTOCOL_H_*/
diff --git a/libs/sipcc/core/sipstack/h/ccsip_publish.h b/libs/sipcc/core/sipstack/h/ccsip_publish.h
new file mode 100644 (file)
index 0000000..3e823e1
--- /dev/null
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_PUBLISH_H_
+#define _CCSIP_PUBLISH_H_
+
+#include "publish_int.h"
+#include "ccsip_common_cb.h"
+#include "ccsip_subsmanager.h"
+
+/* the following is to take advantage of SUB/NOT periodic timer */
+#define TMR_PERIODIC_PUBLISH_INTERVAL TMR_PERIODIC_SUBNOT_INTERVAL
+
+#define PUBLISH_FAILED_START        (1000)
+#define PUBLISH_FAILED_NOCONTEXT    (PUBLISH_FAILED_START + 1)
+#define PUBLISH_FAILED_NORESOURCE   (PUBLISH_FAILED_START + 2)
+#define PUBLISH_FAILED_SEND         (PUBLISH_FAILED_START + 3)
+#define PUBLISH_FAILED_RESET        (PUBLISH_FAILED_START + 4)
+
+typedef struct {
+    ccsip_common_cb_t       hb;  /* this MUST be the first member */
+
+    cc_srcs_t               callback_task;  // CallBack Task ID
+    int                     resp_msg_id;    //Response Msg ID
+    char                   *entity_tag;
+    pub_handle_t            pub_handle;
+    pub_handle_t            app_handle;
+    char                    ruri[MAX_URI_LENGTH];  // Address of the resource
+    char                    full_ruri[MAX_SIP_URL_LENGTH];
+    char                    esc[MAX_URI_LENGTH];   // Event State Compositor
+    boolean                 outstanding_trxn;
+    sipPlatformUITimer_t    retry_timer;
+    sll_handle_t            pending_reqs; //holds pending requests
+} ccsip_publish_cb_t; //PUBLISH Control Block data structure
+
+extern int publish_handle_ev_app_publish(cprBuffer_t buf);
+extern int publish_handle_retry_timer_expire(uint32_t handle);
+extern void publish_handle_periodic_timer_expire(void);
+extern int publish_handle_ev_sip_response(sipMessage_t *pSipMessage);
+extern void publish_reset(void);
+extern cc_int32_t show_publish_stats(cc_int32_t argc, const char *argv[]);
+
+#endif // _CCSIP_PUBLISH_H_
diff --git a/libs/sipcc/core/sipstack/h/ccsip_register.h b/libs/sipcc/core/sipstack/h/ccsip_register.h
new file mode 100644 (file)
index 0000000..4148bdb
--- /dev/null
@@ -0,0 +1,157 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_REGISTER_H_
+#define _CCSIP_REGISTER_H_
+
+#include "cpr_types.h"
+#include "cpr_timers.h"
+#include "phone.h"
+#include "ccsip_core.h"
+#include "ccsip_credentials.h"
+#include "platform_api.h"
+
+#define MAX_REG_EXPIRES 3600
+#include "ccsip_subsmanager.h"
+#include "platform_api.h"
+
+#define MAX_RETRIES_401 3
+
+#define AUTH_HDR(status_code) \
+    (((status_code) == SIP_CLI_ERR_UNAUTH) ? \
+     (SIP_HEADER_WWW_AUTHENTICATE) : (SIP_HEADER_PROXY_AUTHENTICATE))
+
+#define AUTH_HDR_STR(status_code) \
+    (((status_code) == SIP_CLI_ERR_UNAUTH) ? \
+     ("WWW-Authenticate") : ("Proxy-Authenticate"))
+
+#define AUTH_BUGINF(status_code) \
+    (((status_code) == SIP_CLI_ERR_UNAUTH) ? \
+     ("SIP 401 Unauthorized") : ("SIP 407 Proxy Authentication required"))
+
+#define AUTH_NOTIFY(status_code) \
+    (((status_code) == SIP_CLI_ERR_UNAUTH) ? \
+     ("                      401 <---") : ("                      407 <---"))
+
+#define AUTHOR_HDR(status_code) \
+    (((status_code) == SIP_CLI_ERR_UNAUTH) ? \
+     (SIP_HEADER_AUTHORIZATION) : (SIP_HEADER_PROXY_AUTHORIZATION))
+
+
+/*
+  These numbers need to match up
+  with whats defined on the J-Side
+  The Master copy should be here because
+  the reason needs to be sent out in the register
+  message
+*/
+#define UNREG_REASON_UNSPECIFIED                       0
+//Common with what SCCP uses...need to match with J-Side
+//Important! should be defined in plat_api.h for thirdparty application to use.
+#define UNREG_REASON_TCP_TIMEOUT                       CC_UNREG_REASON_TCP_TIMEOUT        // 10
+#define UNREG_REASON_CM_RESET_TCP                      CC_UNREG_REASON_CM_RESET_TCP       //12
+#define UNREG_REASON_CM_ABORTED_TCP                    CC_UNREG_REASON_CM_ABORTED_TCP     //13
+#define UNREG_REASON_CM_CLOSED_TCP                     CC_UNREG_REASON_CM_CLOSED_TCP      //14
+#define UNREG_REASON_REG_TIMEOUT                       CC_UNREG_REASON_REG_TIMEOUT        //17
+#define UNREG_REASON_FALLBACK                          CC_UNREG_REASON_FALLBACK           //18
+#define UNREG_REASON_PHONE_KEYPAD                      CC_UNREG_REASON_PHONE_KEYPAD       //20
+#define UNREG_REASON_RESET_RESET                       CC_UNREG_REASON_RESET_RESET        //22
+#define UNREG_REASON_RESET_RESTART                     CC_UNREG_REASON_RESET_RESTART      //23
+#define UNREG_REASON_PHONE_REG_REJ                     CC_UNREG_REASON_PHONE_REG_REJ      //24
+#define UNREG_REASON_PHONE_INITIALIZED                 CC_UNREG_REASON_PHONE_INITIALIZED  //25
+#define UNREG_REASON_VOICE_VLAN_CHANGED                CC_UNREG_REASON_VOICE_VLAN_CHANGED //26
+
+//sip specific ones...need to match with J-Side
+#define UNREG_REASON_VERSION_STAMP_MISMATCH            CC_UNREG_REASON_VERSION_STAMP_MISMATCH          //100
+#define UNREG_REASON_VERSION_STAMP_MISMATCH_CONFIG     CC_UNREG_REASON_VERSION_STAMP_MISMATCH_CONFIG   //101
+#define UNREG_REASON_VERSION_STAMP_MISMATCH_SOFTKEY    CC_UNREG_REASON_VERSION_STAMP_MISMATCH_SOFTKEY  //102
+#define UNREG_REASON_VERSION_STAMP_MISMATCH_DIALPLAN   CC_UNREG_REASON_VERSION_STAMP_MISMATCH_DIALPLAN //103
+#define UNREG_REASON_APPLY_CONFIG_RESTART              CC_UNREG_REASON_APPLY_CONFIG_RESTART            //104
+#define UNREG_REASON_CONFIG_RETRY_RESTART              CC_UNREG_REASON_CONFIG_RETRY_RESTART            //105
+#define UNREG_REASON_TLS_ERROR                         CC_UNREG_REASON_TLS_ERROR                       //106
+#define UNREG_REASON_RESET_TO_INACTIVE_PARTITION       CC_UNREG_REASON_RESET_TO_INACTIVE_PARTITION     //107
+#define UNREG_REASON_VPN_CONNECTIVITY_LOST             CC_UNREG_REASON_VPN_CONNECTIVITY_LOST           //108
+
+
+#define PRIMARY_LINE           (1)
+
+typedef enum {
+    SIP_REG_ERROR,
+    SIP_REG_OK
+} sip_reg_return_code;
+
+typedef enum
+{
+    SIP_REG_INVALID=-1,
+    SIP_REG_IDLE,
+    SIP_REG_REGISTERING,
+    SIP_REG_REGISTERED,
+    SIP_REG_UNREGISTERING,
+    SIP_REG_PRE_FALLBACK,
+    SIP_REG_IN_FAILOVER,
+    SIP_REG_POST_FAILOVER,
+    SIP_REG_STANDBY_FAILOVER,
+    SIP_REG_NO_CC,
+    SIP_REG_NO_STANDBY,
+    SIP_REG_NO_REGISTER
+} ccsip_register_states_t;
+
+typedef enum
+{
+    E_SIP_REG_NONE = 0,
+    SIPSPI_REG_EV_BASE = 1,
+
+    E_SIP_REG_REG_REQ = SIPSPI_REG_EV_BASE,
+    E_SIP_REG_CANCEL,
+    E_SIP_REG_1xx,
+    E_SIP_REG_2xx,
+    E_SIP_REG_3xx,
+    E_SIP_REG_4xx,
+    E_SIP_REG_FAILURE_RESPONSE,
+    E_SIP_REG_TMR_ACK,
+    E_SIP_REG_TMR_EXPIRE,
+    E_SIP_REG_TMR_WAIT,
+    E_SIP_REG_TMR_RETRY,
+    E_SIP_REG_CLEANUP,
+    SIPSPI_REG_EV_END = E_SIP_REG_CLEANUP
+} sipRegSMEventType_t;
+
+
+typedef struct
+{
+    int     line;
+    boolean cancel;
+} ccsip_register_msg_t;
+
+
+int  sip_reg_sm_process_event(sipSMEvent_t *pEvent);
+sipRegSMEventType_t ccsip_register_sip2sipreg_event(int sip_event);
+int  ccsip_register_init(void);
+void ccsip_register_timeout_retry(void *data);
+void ccsip_register_all_lines(void);
+void ccsip_register_cancel(boolean cancel_reg, boolean backup_proxy);
+void ccsip_ccm_register_cancel(boolean cancel_reg);
+void ccsip_register_set_state(ccsip_register_states_t state);
+ccsip_register_states_t ccsip_register_get_state(void);
+ccsip_register_states_t ccsip_register_get_register_state(void);
+void ccsip_register_reset_proxy(void);
+void cred_get_line_credentials(line_t line, credentials_t *pcredentials,
+                               int id_len, int pw_len);
+boolean cred_get_user_credentials(line_t line, credentials_t *pcredentials);
+boolean cred_get_credentials_r(ccsipCCB_t *ccb, credentials_t *pcredentials);
+void ccsip_register_commit(void);
+void ccsip_backup_register_commit(void);
+void ccsip_register_cleanup(ccsipCCB_t *ccb, boolean start);
+void ccsip_register_set_register_state(ccsip_register_states_t state);
+int  ccsip_register_send_msg(uint32_t cmd, line_t line);
+void ccsip_handle_ev_default(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_reg_sm_change_state(ccsipCCB_t *ccb, sipRegSMStateType_t new_state);
+boolean ccsip_register_all_unregistered();
+void sip_stop_ack_timer(ccsipCCB_t *ccb);
+void ccsip_register_shutdown(void);
+boolean ccsip_get_ccm_date(char *date_value);
+boolean ccsip_is_line_registered(line_t line);
+boolean process_retry_after(ccsipCCB_t *ccb, sipMessage_t *response);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_reldev.h b/libs/sipcc/core/sipstack/h/ccsip_reldev.h
new file mode 100644 (file)
index 0000000..cf6a764
--- /dev/null
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_RELDEV_H_
+#define _CCSIP_RELDEV_H_
+
+#include "cpr_types.h"
+#include "ccsip_pmh.h"
+#include "ccsip_core.h"
+
+#define RELDEV_MAX_USER_NAME_LEN    64
+#define RELDEV_MAX_HOST_NAME_LEN    64
+#define RELDEV_NO_STORED_MSG        (-1)
+
+typedef struct
+{
+    char     message_buf[SIP_UDP_MESSAGE_SIZE];
+    uint32_t message_buf_len;
+    cpr_ip_addr_t dest_ipaddr;
+    uint16_t dest_port;
+} sipRelDevCoupledMessage_t;
+
+typedef struct
+{
+    boolean                   is_request;
+    char                      call_id[MAX_SIP_CALL_ID];
+    uint32_t                  cseq_number;
+    sipMethod_t               cseq_method;
+    char                      tag[MAX_SIP_TAG_LENGTH];
+    char                      from_user[RELDEV_MAX_USER_NAME_LEN];
+    char                      from_host[RELDEV_MAX_HOST_NAME_LEN];
+    char                      to_user[RELDEV_MAX_USER_NAME_LEN];
+    int                       response_code;
+    sipRelDevCoupledMessage_t coupled_message;
+    boolean                   valid_coupled_message;
+    //int                       line;
+} sipRelDevMessageRecord_t;
+
+void sipRelDevMessageStore(sipRelDevMessageRecord_t *pMessageRecord);
+boolean sipRelDevMessageIsDuplicate(sipRelDevMessageRecord_t *pMessageRecord,
+                                    int *index);
+int sipRelDevCoupledMessageStore(sipMessage_t *pCoupledMessage,
+                                 const char *call_id,
+                                 uint32_t cseq_number,
+                                 sipMethod_t cseq_method,
+                                 boolean is_request,
+                                 int status_code,
+                                 cpr_ip_addr_t *dest_ipaddr,
+                                 uint16_t dest_port,
+                                 boolean ignore_tag);
+int sipRelDevCoupledMessageSend(int index);
+void sipRelDevMessagesClear(const char *call_id,
+                            const char *from_user,
+                            const char *from_host,
+                            const char *to_user);
+void sipRelDevAllMessagesClear();
+uint32_t sipRelDevGetStoredCoupledMessage(int index,
+                                          char *dest_buffer,
+                                          uint32_t max_buff);
+
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_sdp.h b/libs/sipcc/core/sipstack/h/ccsip_sdp.h
new file mode 100644 (file)
index 0000000..b927a83
--- /dev/null
@@ -0,0 +1,159 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_SDP_H_
+#define _CCSIP_SDP_H_
+
+
+#include "cpr_types.h"
+#include "pmhutils.h"
+#include "sdp.h"
+#include "ccapi.h"
+
+
+/* SDP bitmask values */
+#define CCSIP_SRC_SDP_BIT       0x1
+#define CCSIP_DEST_SDP_BIT      0x2
+
+/*
+ * Create a description or a SIP SDP (sip_info) with
+ * appropriate values initialized
+ */
+PMH_EXTERN boolean sip_sdp_init(void);
+PMH_EXTERN sdp_t *sipsdp_create(const char *peerconnection);
+PMH_EXTERN cc_sdp_t *sipsdp_info_create(void);
+PMH_EXTERN void sipsdp_src_dest_free(uint16_t flags, cc_sdp_t **sdp_info);
+PMH_EXTERN void sipsdp_src_dest_create(const char *peerconnection,
+    uint16_t flags, cc_sdp_t **sdp_info);
+PMH_EXTERN void sipsdp_free(cc_sdp_t **sip_sdp);
+
+/*
+ * Stream related sdp utility functions
+ */
+//PMH_EXTERN sip_sdp_stream_t *sipsdp_stream_create(void);
+//PMH_EXTERN void sipsdp_add_stream_to_list(sip_sdp_stream_t *stream,
+//                                     sip_sdp_t *sip_sdp);
+//PMH_EXTERN void sipsdp_remove_stream_from_list(sip_sdp_stream_t **stream);
+//PMH_EXTERN void sipsdp_remove_streams_list(sip_sdp_stream_t **stream_list);
+
+#define SIPSDP_MAX_SESSION_VERSION_LENGTH 32
+
+/*
+ * Standard session-level parameters
+ */
+#define SIPSDP_VERSION              0
+// RAMC_DEBUG #define SIPSDP_ORIGIN_USERNAME      "CiscoSystemsSIP-GW-UserAgent"
+#define SIPSDP_ORIGIN_USERNAME      "Mozilla-SIPUA"
+#define SIPSDP_SESSION_NAME         "SIP Call"
+
+/* Possible encoding names fo static payload types*/
+#define SIPSDP_ATTR_ENCNAME_PCMU      "PCMU"
+#define SIPSDP_ATTR_ENCNAME_PCMA      "PCMA"
+#define SIPSDP_ATTR_ENCNAME_G729      "G729"
+#define SIPSDP_ATTR_ENCNAME_G723      "G723"
+#define SIPSDP_ATTR_ENCNAME_G726      "G726-32"
+#define SIPSDP_ATTR_ENCNAME_G728      "G728"
+#define SIPSDP_ATTR_ENCNAME_GSM       "GSM"
+#define SIPSDP_ATTR_ENCNAME_CN        "CN"
+#define SIPSDP_ATTR_ENCNAME_G722      "G722"
+#define SIPSDP_ATTR_ENCNAME_ILBC      "iLBC"
+#define SIPSDP_ATTR_ENCNAME_H263v2    "H263-1998"
+#define SIPSDP_ATTR_ENCNAME_H264      "H264"
+#define SIPSDP_ATTR_ENCNAME_VP8       "VP8"
+#define SIPSDP_ATTR_ENCNAME_L16_256K  "L16"
+#define SIPSDP_ATTR_ENCNAME_ISAC      "ISAC"
+#define SIPSDP_ATTR_ENCNAME_OPUS      "opus"
+
+/* Possible encoding names for DTMF tones dynamic payload types */
+#define SIPSDP_ATTR_ENCNAME_TEL_EVENT "telephone-event"
+#define SIPSDP_ATTR_ENCNAME_FRF_DIGIT "frf-dialed-digit"
+
+/* Possible encoding names for other dynamic payload types */
+#define SIPSDP_ATTR_ENCNAME_CLEAR_CH  "X-CCD"
+#define SIPSDP_ATTR_ENCNAME_G726R16   "G726-16"
+#define SIPSDP_ATTR_ENCNAME_G726R24   "G726-24"
+#define SIPSDP_ATTR_ENCNAME_GSMEFR    "GSM-EFR"
+
+/* RTPMAP encoding names added from MGCP for compatibility with MGCP
+ * These could be coming in from Cisco MGCP gateway's SDP via a
+ * softswitch and SIP GW must interoperate.
+ */
+
+#define SIPSDP_ATTR_ENCNAME_G729_A_STR_DOTTED                 "G.729a"
+#define SIPSDP_ATTR_ENCNAME_G729_B_STR_DOTTED                 "G.729b"
+#define SIPSDP_ATTR_ENCNAME_G729_B_LOW_COMPLEXITY_STR_DOTTED  "G.729b-L"
+#define SIPSDP_ATTR_ENCNAME_G729_A_B_STR_DOTTED               "G.729ab"
+
+#define SIPSDP_ATTR_ENCNAME_G7231_HIGH_RATE_STR_DOTTED        "G.723.1-H"
+#define SIPSDP_ATTR_ENCNAME_G7231_A_HIGH_RATE_STR_DOTTED      "G.723.1a-H"
+#define SIPSDP_ATTR_ENCNAME_G7231_LOW_RATE_STR_DOTTED         "G.723.1-L"
+#define SIPSDP_ATTR_ENCNAME_G7231_A_LOW_RATE_STR_DOTTED       "G.723.1a-L"
+
+/*
+ * NSE/XNSE encoding names
+ */
+#define SIPSDP_ATTR_ENCNAME_XNSE       "X-NSE"
+#define SIPSDP_ATTR_ENCNAME_NSE        "NSE"
+
+
+/* Possible clock rates */
+#define RTPMAP_CLOCKRATE  8000
+#define RTPMAP_VIDEO_CLOCKRATE  90000
+#define RTPMAP_L16_CLOCKRATE  16000
+#define RTPMAP_ISAC_CLOCKRATE  16000
+#define RTPMAP_OPUS_CLOCKRATE      48000
+#define FMTP_MAX_AVERAGE_BIT_RATE  40000
+#define ATTR_PTIME                 20
+#define ATTR_MAXPTIME              120
+#define WEBRTC_DATA_CHANNEL_PROT   "webrtc-datachannel"
+
+#define SIPSDP_CONTENT_TYPE         "application/sdp"
+
+#define MAX_RTP_PAYLOAD_TYPES        7
+
+#define BITRATE_5300_BPS  5300
+#define BITRATE_6300_BPS  6300
+
+/*
+ * T.38 attribute parameters
+ */
+#define SIPSDP_ATTR_T38_VERSION_DEF             0
+#define SIPSDP_ATTR_T38_FILL_BIT_REMOVAL_DEF    FALSE
+#define SIPSDP_ATTR_T38_TRANSCODING_MMR_DEF     FALSE
+#define SIPSDP_ATTR_T38_TRANSCODING_JBIG_DEF    FALSE
+/* the following two definitions are from VSIToH245BuildFastStartT38OLC() */
+#define SIPSDP_ATTR_T38_MAX_BUFFER_DEF          200
+#define SIPSDP_ATTR_T38_MAX_DATAGRAM_DEF        72
+
+/*
+ * Supported NTE range. Note that if the supported NTEs ever becomes a
+ * non-contiguous set of values, then it will have to be stored as an
+ * sdp. See the definition of negotiated_nte_sdp for an example. Further,
+ * where Sdp_ne_cmp_range() is invoked, Sdp_ne_cmp_list() will have to be
+ * used instead.
+ */
+#define SIPSDP_NTE_SUPPORTED_LOW      0  /* Min value of DTMF event table */
+#define SIPSDP_FRF_SUPPORTED_HIGH     15 /* for dtmf-relay cisco-rtp */
+#define SIPSDP_NTE_SUPPORTED_HIGH     16 /* for dtmf-relay rtp-nse */
+
+/*
+ * Create the "on-wire" version, i.e. ready to put into a SIP message.
+ * Memory is allocated and should be freed by the user when done
+ * Returns NULL on failure.
+ */
+PMH_EXTERN char *sipsdp_write_to_buf(cc_sdp_t *, uint32_t *);
+
+#define SIPSDP_FREE(x) \
+if (x) \
+{ \
+    sdp_free_description(x); \
+}
+
+#define SIPSDP_MAX_PAYLOAD_TYPES 15
+#define MAX_RTP_MEDIA_TYPES   6
+#define SIPSDP_NTE_DTMF_MIN   0  /* Min value of DTMF event table */
+#define SIPSDP_NTE_DTMF_MAX  15  /* Max DTMF event value supported here */
+
+
+#endif /*_CCSIP_SDP_H_*/
diff --git a/libs/sipcc/core/sipstack/h/ccsip_sim.h b/libs/sipcc/core/sipstack/h/ccsip_sim.h
new file mode 100644 (file)
index 0000000..89cff0a
--- /dev/null
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_SIM_H_
+#define _CCSIP_SIM_H_
+
+
+void SIPTaskTestUdpSend(void);
+void SIPTaskSimulateInviteRecv(void);
+void SIPTaskSimulateOffhook(void);
+void SIPTaskSimulateAckRecv(void);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_spi_utils.h b/libs/sipcc/core/sipstack/h/ccsip_spi_utils.h
new file mode 100755 (executable)
index 0000000..95aeb51
--- /dev/null
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_UTILS_H_
+#define _CCSIP_UTILS_H_
+
+#include "cpr_types.h"
+#include "ccsip_pmh.h"
+
+boolean
+sipSPI_validate_hostname(char *);
+
+boolean
+sipSPI_validate_ip_addr_name(char *);
+
+extern int
+sipSPICheckDomainToken(char *token);
+
+boolean
+sipSPI_validate_hostname (char *str);
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_subsmanager.h b/libs/sipcc/core/sipstack/h/ccsip_subsmanager.h
new file mode 100644 (file)
index 0000000..fe66ac9
--- /dev/null
@@ -0,0 +1,433 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_SUBSMANAGER_H_
+#define _CCSIP_SUBSMANAGER_H_
+
+#include "cpr_types.h"
+#include "cpr_ipc.h"
+#include "cpr_timers.h"
+#include "cpr_socket.h"
+#include "ccsip_core.h"
+#include "phone_platform_constants.h"
+#include "singly_link_list.h"
+#include "ccsip_common_cb.h"
+
+/*
+ * States
+ */
+typedef enum {
+    SUBS_STATE_IDLE = 0,        // Initial state of the SCB
+    SUBS_STATE_REGISTERED,
+
+    /* out going subscribe SCB states */
+    SUBS_STATE_SENT_SUBSCRIBE,  // Sent SUBSCRIBE
+    SUBS_STATE_RCVD_NOTIFY,     // Received NOTIFY
+    SUBS_STATE_SENT_SUBSCRIBE_RCVD_NOTIFY,  // Sent SUBSCRIBE & Received NOTIFY
+
+    /* incoming subscribe SCB states */
+    SUBS_STATE_RCVD_SUBSCRIBE,  // Received SUBSCRIBE
+    SUBS_STATE_SENT_NOTIFY,     // Sent NOTIFY
+    SUBS_STATE_RCVD_SUBSCRIBE_SENT_NOTIFY, // Received SUBSCRIBE & Sent NOTIFY
+
+    /*
+     * No outstanding outgoing/incoming messages to handle.
+     * This state is after all outstanding SUBSCRIBE/NOTIFY
+     * transactions are complete.
+     */
+    SUBS_STATE_ACTIVE,
+
+    SUBS_STATE_INVALID
+} subsStateType_t;
+
+typedef enum {
+    SUBSCRIPTION_NULL = 0,
+    SUBSCRIPTION_TERMINATE
+} subscriptionState;
+
+/*
+ * Subscription ID data type
+ */
+typedef uint32_t sub_id_t;
+
+#define MAX_EVENT_NAME_LEN           32
+#define CCSIP_SUBS_START_CSEQ        1000
+
+/* There may be multiple subscription pending on given call
+ * the subcription for KPML, remote-cc, offhook notification  etc.
+ */
+#define MAX_SCBS                     ((MAX_TEL_LINES * 2) < 32 ? 32 : (MAX_TEL_LINES * 2))
+#define LIMIT_SCBS_USAGE             (MAX_SCBS - ((MAX_SCBS * 20) / 100))
+#define TMR_PERIODIC_SUBNOT_INTERVAL 5
+#define CCSIP_SUBS_INVALID_SUB_ID   (sub_id_t)(-1)
+#define MAX_SCB_HISTORY              10
+
+typedef enum {
+    SM_REASON_CODE_NORMAL = 0,
+    SM_REASON_CODE_ERROR,
+    SM_REASON_CODE_SHUTDOWN,
+    SM_REASON_CODE_ROLLOVER,
+    SM_REASON_CODE_RESET_REG
+} ccsip_reason_code_e;
+
+
+// Application defined callback functions
+
+// Data to app to indicate response to a sent SUBSCRIBE
+typedef struct {
+    int  status_code;
+    long expires;
+} ccsip_subs_result_data_t;
+
+// Data to app to indicate a received SUBSCRIBE
+typedef struct {
+    ccsip_event_data_t *eventData;
+    int               expires;
+    int               line;
+    string_t          from;
+    string_t          to;
+} ccsip_subs_ind_data_t;
+
+// Data to app to indicate a received NOTIFY
+typedef struct {
+    ccsip_event_data_t     *eventData;
+    sip_subs_state_e        subscription_state; // From the subs-state header
+    sip_subs_state_reason_e subscription_state_reason;
+    uint32_t                expires;
+    uint32_t                retry_after;
+    uint32_t                cseq;
+    char                    entity[CC_MAX_DIALSTRING_LEN]; // used to store From user for incoming unsolicited NOTIFY.
+} ccsip_notify_ind_data_t;
+
+// Data to app to indicate response to a sent NOTIFY
+typedef struct {
+    int status_code;
+} ccsip_notify_result_data_t;
+
+// Data to app to indicate incoming subscription terminated
+typedef struct {
+    int status_code;
+} ccsip_subs_terminate_data_t;
+
+// Containing structure for all stack->app messages
+typedef struct ccsip_sub_not_data_t {
+    int                 msg_id;
+    sub_id_t            sub_id;
+    int                 sub_duration;
+    cc_subscriptions_t  event;
+    line_t              line_id;
+    callid_t            gsm_id;
+    boolean             norefersub;
+    long               request_id;
+    ccsip_reason_code_e reason_code;
+    union {
+        ccsip_subs_ind_data_t       subs_ind_data;
+        ccsip_subs_result_data_t    subs_result_data;
+        ccsip_notify_ind_data_t     notify_ind_data;
+        ccsip_notify_result_data_t  notify_result_data;
+        ccsip_subs_terminate_data_t subs_term_data;
+    } u;
+} ccsip_sub_not_data_t;
+
+
+// Function to pass an incoming subscribe request
+typedef void (*ccsipSubsIndCallbackFn_t)(ccsip_sub_not_data_t *msg_data);
+
+// Function to pass response to a subscribe request
+typedef void (*ccsipSubsResultCallbackFn_t)(ccsip_sub_not_data_t *msg_data);
+
+// Function to pass an incoming Notify request
+typedef void (*ccsipNotifyIndCallbackFn_t)(ccsip_sub_not_data_t *msg_data);
+
+// Function to pass results of notify request
+typedef void (*ccsipNotifyResultCallbackFn_t)(ccsip_sub_not_data_t *msg_data);
+
+// Function to pass general errors
+typedef void (*ccsipSubsTerminateCallbackFn_t)(ccsip_sub_not_data_t *msg_data);
+typedef void (*ccsipGenericCallbackFn_t)(ccsip_sub_not_data_t *msg_data);
+
+
+
+// Return status code
+#define SUBSCRIPTION_IN_PROGRESS     1010
+#define SUBSCRIPTION_SUCCEEDED       1020
+#define SUBSCRIPTION_REJECTED        1030
+#define SUBSCRIPTION_FAILED          1040
+#define NOTIFY_REQUEST_FAILED        1050
+#define SUBSCRIBE_REQUEST_FAILED     1060
+#define SUBSCRIBE_FAILED_NORESOURCE  1061
+#define SUBSCRIBE_FAILED_BADEVENT    1062
+#define SUBSCRIBE_FAILED_BADINFO     1062
+#define NETWORK_SUBSCRIPTION_EXPIRED 1070
+#define APPLICATION_SUBSCRIPTION_EXPIRED 1080
+#define REQUEST_TIMEOUT              1090
+
+// Data passed by app to register for incoming SUBSCRIBE
+typedef struct sipspi_subscribe_reg_t_ {
+    cc_subscriptions_t eventPackage; // Registering for which event
+    ccsipSubsIndCallbackFn_t subsIndCallback; // Callback function
+    cc_srcs_t subsIndCallbackTask;   // Callback task
+    int       subsIndCallbackMsgID;  // msg_id to use in callback
+    ccsipSubsTerminateCallbackFn_t subsTermCallback; // Callback function when remote side terminates subs
+    int       subsTermCallbackMsgID; // Terminate msg-id
+    long      min_duration; // Min duration for which SUB should be accepted
+    long      max_duration; // Max duration for which SUB should be accepted
+} sipspi_subscribe_reg_t;
+
+// Data passed by app to initiate a SUBSCRIBE
+typedef struct sipspi_subscribe_t_ {
+    sub_id_t           sub_id;       // ID for reSubscribe
+    cc_subscriptions_t eventPackage; // Event package to send subscribe for
+    cc_subscriptions_t acceptPackage; // Accept header
+    long               duration;     // subscription duration (0=unsubscribe)
+    char               subscribe_uri[CC_MAX_DIALSTRING_LEN];
+    char               subscriber_uri[CC_MAX_DIALSTRING_LEN]; // From field
+    long               request_id;   // Returned to subscriber in response
+
+    ccsipSubsResultCallbackFn_t subsResultCallback;
+    ccsipNotifyIndCallbackFn_t notifyIndCallback;
+    ccsipSubsTerminateCallbackFn_t subsTermCallback;
+
+    cc_srcs_t          subsNotCallbackTask;
+    int                subsResCallbackMsgID;
+    int                subsNotIndCallbackMsgID;
+    int                subsTermCallbackMsgID;
+
+    cpr_ip_addr_t      dest_sip_addr; // Destination address
+    uint16_t           dest_sip_port; // Destination port
+
+    callid_t           call_id;
+    line_t             dn_line;       // Associated line, if any
+
+    boolean            auto_resubscribe; // stack reSubscribes at expiry
+    boolean            norefersub;
+    ccsip_event_data_t *eventData;    // Determined by the eventPackage value
+
+} sipspi_subscribe_t;
+
+// Data by app to respond to a received SUBSCRIBE
+typedef struct sipspi_subscribe_resp_t_ {
+    sub_id_t sub_id;         // Subscription id
+    uint16_t response_code;  // Response that should be sent
+    int      duration;       // Max duration for the subscribe
+} sipspi_subscribe_resp_t;
+
+// Data by app to initiate a NOTIFY
+typedef struct sipspi_notify_t_ {
+    sub_id_t            sub_id;     // Subscription id
+    // Info for different notify bodies
+    ccsipNotifyResultCallbackFn_t notifyResultCallback;
+    int                 subsNotResCallbackMsgID;
+    ccsip_event_data_t *eventData;  // Determined by the eventPackage value
+    cc_subscriptions_t eventPackage; // Event package
+    subscriptionState   subState;
+    cc_srcs_t           subsNotCallbackTask; // Opt.: If not already specified
+} sipspi_notify_t;
+
+// Data by app to respond to a received NOTIFY
+typedef struct sipspi_notify_resp_t_ {
+    sub_id_t sub_id;
+    int      response_code;
+    int      duration;
+    uint32_t cseq;
+} sipspi_notify_resp_t;
+
+// Data by app to terminate an existing subscription
+typedef struct sipspi_subscribe_term_t_ {
+    sub_id_t sub_id;
+    long     request_id;
+    cc_subscriptions_t eventPackage; // Event package
+    boolean immediate;
+} sipspi_subscribe_term_t;
+
+/*
+typedef struct sipspi_remotecc_reg_t_{
+} sipspi_remotecc_reg_t;
+
+typedef struct sipspi_remotecc_refer_t_{
+} sipspi_remotecc_refer_t;
+
+typedef struct sipspi_remotecc_refer_resp_t_{
+} sipspi_remotecc_refer_resp_t;
+
+typedef struct sipspi_remotecc_notify_t_{
+} sipspi_remotecc_notify_t;
+
+typedef struct sipspi_remotecc_notify_resp_t_{
+} sipspi_remotecc_notify_resp_t;
+
+typedef struct sipspi_remotecc_term_t_{
+} sipspi_remotecc_term_t;
+*/
+
+typedef struct sipspi_msg_t_ {
+    union {
+        sipspi_subscribe_reg_t  subs_reg;
+        sipspi_subscribe_t      subscribe;
+        sipspi_subscribe_resp_t subscribe_resp;
+        sipspi_notify_t         notify;
+        sipspi_notify_resp_t    notify_resp;
+        sipspi_subscribe_term_t subs_term;
+        /*
+         * sipspi_remotecc_reg_t         remotecc_reg;
+         * sipspi_remotecc_refer_t       remotecc_refer;
+         * sipspi_remotecc_refer_resp_t  remotecc_refer_resp;
+         * sipspi_remotecc_notify_t      remotecc_notify;
+         * sipspi_remotecc_notify_resp_t remotecc_notify_resp;
+         * sipspi_remotecc_term_t        remotecc_term;
+         */
+    } msg;
+} sipspi_msg_t;
+
+// List of app->stack messages
+typedef struct sipspi_msg_list_t_ {
+    uint32_t cmd;
+    sipspi_msg_t *msg;
+    struct sipspi_msg_list_t_ *next;
+} sipspi_msg_list_t;
+
+// Subscription Control Block
+typedef struct {
+    ccsip_common_cb_t       hb; /* this MUST be the first memeber in the struct */
+
+    line_t             line;
+    // Subscription ID
+    sub_id_t           sub_id;
+
+    // SCB State
+    boolean            pendingClean;
+    unsigned char      pendingCount;
+
+    // Subscriber details
+    boolean            internal; // Internal or external
+
+    // Callback and messaging details
+    ccsipSubsIndCallbackFn_t subsIndCallback;
+    cc_srcs_t          subsIndCallbackTask;
+    cc_srcs_t          subsNotCallbackTask;
+    int                subsIndCallbackMsgID;
+    ccsipSubsResultCallbackFn_t subsResultCallback;
+    int                subsResCallbackMsgID;
+    ccsipNotifyIndCallbackFn_t notifyIndCallback;
+    int                notIndCallbackMsgID;
+    ccsipSubsTerminateCallbackFn_t subsTermCallback;
+    int                subsTermCallbackMsgID;
+    ccsipNotifyResultCallbackFn_t notifyResultCallback;
+    int                notResCallbackMsgID;
+
+    short              sip_socket_handle;
+    boolean            useDeviceAddressing;
+    callid_t           gsm_id;
+    long               request_id;
+
+    // Subscription details
+    subsStateType_t    smState;
+    subsStateType_t    outstandingIncomingNotifyTrxns; // only used for incoming NOTIFYs
+    unsigned long      min_expires;
+    unsigned long      max_expires;
+    ccsipCCB_t        *ccbp;             /* associated CCB, if any */
+    char               event_name[MAX_EVENT_NAME_LEN];
+    boolean            auto_resubscribe; /* Resubscribe automatically */
+    boolean            norefersub;
+
+    // Messaging details
+    uint32_t           last_sent_request_cseq;
+    sipMethod_t        last_sent_request_cseq_method;
+    uint32_t           last_recv_request_cseq;
+    sipMethod_t        last_recv_request_cseq_method;
+
+    // Saved headers
+    char               SubURI[MAX_SIP_URL_LENGTH];
+    char               SubURIOriginal[MAX_SIP_URL_LENGTH];
+    char               SubscriberURI[MAX_SIP_URL_LENGTH];
+    string_t           sip_from;
+    string_t           sip_to;
+    string_t           sip_to_tag;
+    string_t           sip_from_tag;
+    string_t           sip_contact;
+    string_t           cached_record_route;
+    sipContact_t      *contact_info;
+    sipRecordRoute_t  *record_route_info;
+    sll_handle_t       incoming_trxns; // to store via (branch attribute) to track transactions.
+    string_t           callingNumber;
+
+    // Subscription headers
+    sip_subs_state_e  subscription_state;
+    sip_subs_state_reason_e subscription_state_reason;
+    uint32_t          retry_after;
+
+
+    // Linked list of pending app messages
+    sipspi_msg_list_t *pendingRequests;
+} sipSCB_t;
+
+// Transaction Control Block
+typedef struct {
+    ccsip_common_cb_t       hb; /* this MUST be the first memeber in the struct */
+    char                    full_ruri[MAX_SIP_URL_LENGTH];
+    cprTimer_t              timer; /* transaction timer */
+    uint32_t                trxn_id;
+} sipTCB_t;
+
+// Structure to keep track of recent subscriptions
+typedef struct {
+    char               last_call_id[MAX_SIP_CALL_ID];
+    char               last_from_tag[MAX_SIP_TAG_LENGTH];
+    cc_subscriptions_t eventPackage;
+} sipSubsHistory_t;
+
+#define MAX_SUB_EVENTS    5
+#define MAX_SUB_EVENT_NAME_LEN 16
+extern const char eventNames[MAX_SUB_EVENTS][MAX_SUB_EVENT_NAME_LEN];
+
+/*
+ * Externally called function headers
+ */
+// For initializing and shutting down
+int sip_subsManager_init();
+int sip_subsManager_shut();
+
+// Function to handle subscription requests from applications
+int subsmanager_handle_ev_cc_feature_subscribe(sipSMEvent_t *);
+int subsmanager_handle_ev_cc_feature_notify(sipSMEvent_t *);
+
+int subsmanager_handle_ev_app_subscribe_register(cprBuffer_t buf);
+int subsmanager_handle_ev_app_subscribe(cprBuffer_t buf);
+int subsmanager_handle_ev_app_subscribe_response(cprBuffer_t buf);
+int subsmanager_handle_ev_app_notify(cprBuffer_t buf);
+void subsmanager_handle_ev_app_unsolicited_notify(cprBuffer_t buf, line_t line);
+int subsmanager_handle_ev_app_notify_response(cprBuffer_t buf);
+int subsmanager_handle_ev_app_subscription_terminated(cprBuffer_t buf);
+int subsmanager_handle_retry_timer_expire(int scb_index);
+void subsmanager_handle_periodic_timer_expire(void);
+int subsmanager_test_start_routine();
+
+// Functions to handle remotecc requests from applications
+// int subsmanager_handle_ev_app_remotecc_register();
+// int subsmanager_handle_ev_app_remotecc_refer();
+// int subsmanager_handle_ev_app_remotecc_refer_response();
+// int subsmanager_handle_ev_app_remotecc_notify();
+// int subsmanager_handle_ev_app_remotecc_notify_response();
+// int subsmanager_handle_ev_app_remotecc_terminated();
+
+// Functions to handle requests from network
+int subsmanager_handle_ev_sip_subscribe(sipMessage_t *pSipMessage,
+                                        sipMethod_t sipMethod,
+                                        boolean in_dialog);
+int subsmanager_handle_ev_sip_subscribe_notify(sipMessage_t *pSipMessage);
+
+// Function to handle response from network
+int subsmanager_handle_ev_sip_response(sipMessage_t *pSipMessage);
+
+void free_event_data(ccsip_event_data_t *event_data);
+int sip_subsManager_rollover(void);
+int sip_subsManager_reset_reg(void);
+void submanager_update_ccb_addr(ccsipCCB_t *ccb);
+boolean add_content(ccsip_event_data_t *eventData, sipMessage_t *request, const char *fname);
+void pres_unsolicited_notify_ind(ccsip_sub_not_data_t * msg_data);
+sipTCB_t *find_tcb_by_sip_callid(const char *callID_p);
+int subsmanager_handle_ev_sip_unsolicited_notify_response(sipMessage_t *pSipMessage, sipTCB_t *tcbp);
+void subsmanager_unsolicited_notify_timeout(void *data);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/ccsip_task.h b/libs/sipcc/core/sipstack/h/ccsip_task.h
new file mode 100644 (file)
index 0000000..801168e
--- /dev/null
@@ -0,0 +1,89 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCSIP_TASK_H_
+#define _CCSIP_TASK_H_
+
+#include "cpr_types.h"
+#include "cpr_ipc.h"
+#include "cpr_socket.h"
+#include "cpr_memory.h"
+
+/*
+ * List of timers that the SIP task is responsible for.
+ * CPR will send a msg to the SIP task when these
+ * timers expire. CPR expects a timer id when the timer
+ * is created, this enum serves that purpose.
+ */
+typedef enum {
+    SIP_ACK_TIMER,
+    SIP_WAIT_TIMER,
+    SIP_RETRY_TIMER,
+    SIP_MSG_TIMER,
+    SIP_EXPIRES_TIMER,
+    SIP_REG_TIMEOUT_TIMER,
+    SIP_REG_EXPIRES_TIMER,
+    SIP_LOCAL_EXPIRES_TIMER,
+    SIP_SUPERVISION_TIMER,
+    SIP_GLARE_AVOIDANCE_TIMER,
+    SIP_KEEPALIVE_TIMER,
+    SIP_SUBNOT_TIMER,
+    SIP_SUBNOT_PERIODIC_TIMER,
+    SIP_PUBLISH_RETRY_TIMER,
+    SIP_UNSOLICITED_TRANSACTION_TIMER,
+    SIP_DIAL_TIMEOUT_TIMER,
+    SIP_FAIL_OVER_START_TIMER,
+    SIP_FAIL_OVER_COMPLETE_TIMER,
+    SIP_FALL_BACK_START_TIMER,
+    SIP_FALL_BACK_COMPLETE_TIMER,
+    SIP_UNREGISTRATION_TIMER,
+    SIP_REGALLFAIL_TIMER,
+    SIP_NOTIFY_TIMER,
+       SIP_PASSTHROUGH_TIMER
+} sipTimerList_t;
+
+
+/* Action Values for SIPTaskPostShutdown function */
+#define SIP_INTERNAL 0
+#define SIP_EXTERNAL 1
+#define SIP_STOP     2
+
+#define MAX_SIP_REASON_LENGTH  64
+#define UNREG_NO_REASON        0
+#define VERSION_STAMP_MISMATCH 1
+/*
+ * The code creating the SIP timers needs to have
+ * access to the sip_msgq variable since CPR
+ * needs to know where to send the timer expiration
+ * message.
+ */
+extern cprMsgQueue_t sip_msgq;
+
+typedef struct
+{
+    boolean taskInited; // FALSE
+    cprMsgQueue_t msgQueue;
+} sipGlobal_t;
+
+/*
+ * External Data
+ */
+extern sipGlobal_t sip;
+extern char sipHeaderServer[];
+extern char sipHeaderUserAgent[];
+extern char sipUnregisterReason[];
+
+/*
+ * Prototypes
+ */
+void         SIPTaskInit(void);
+void         SIPTaskProcessListEvent(uint32_t cmd, void *msg, void *pUsr, uint16_t len);
+int          SIPTaskProcessUDPMessage(cprBuffer_t msg, uint16_t len, cpr_sockaddr_storage from);
+int          SIPTaskProcessConfigChangeNotify(int32_t notify_type);
+cpr_status_e SIPTaskSendMsg(uint32_t cmd, cprBuffer_t msg, uint16_t len, void *usr);
+cprBuffer_t  SIPTaskGetBuffer(uint16_t size);
+void         SIPTaskReinitialize(boolean checkConfig);
+void         SIPTaskPostShutdown(int, int reason, const char *reasonInfo);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/h/httpish.h b/libs/sipcc/core/sipstack/h/httpish.h
new file mode 100644 (file)
index 0000000..23c8e87
--- /dev/null
@@ -0,0 +1,302 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _HTTPISH_H_
+#define _HTTPISH_H_
+
+#include "cpr_types.h"
+#include "pmhdefs.h"
+#include "httpish_protocol.h"
+#include "pmhutils.h"
+#include "util_ios_queue.h"
+
+#define HTTPISH_MIN_STATUS_CODE 100
+#define HTTPISH_HEADER_CACHE_SIZE  12
+#define HTTPISH_HEADER_NAME_SIZE   256
+
+typedef struct h_header
+{
+    struct h_header *next;
+    char *header;
+} httpish_header;
+
+typedef struct {
+    char *hdr_start;
+    char *val_start;
+} httpish_cache_t;
+
+#define HTTPISH_MAX_BODY_PARTS 6
+typedef struct {
+    uint8_t  msgContentDisp;
+    boolean  msgRequiredHandling;
+    uint8_t  msgContentTypeValue;
+    char    *msgContentType;
+    char    *msgBody;
+    uint32_t msgLength;
+    char    *msgContentId;
+    uint8_t  msgContentEnc;
+} msgBody_t;
+
+typedef struct _httpMsg
+{
+    struct _httpMsg *next;
+    boolean         retain_flag;    /* If TRUE, Do not free the msg */
+    char           *mesg_line;
+    queuetype      *headers;
+    msgBody_t       mesg_body[HTTPISH_MAX_BODY_PARTS];
+    char           *raw_body;
+    int32_t         content_length;
+    uint8_t         num_body_parts;
+    boolean         is_complete;
+    boolean         headers_read;
+    /* Cache the most commonly used headers */
+    httpish_cache_t hdr_cache[HTTPISH_HEADER_CACHE_SIZE];
+    /* this is the complete message received/sent at the socket */
+    char           *complete_message;
+} httpishMsg_t;
+
+typedef struct
+{
+    char *method;
+    char *url;
+    char *version;
+} httpishReqLine_t;
+
+
+typedef struct
+{
+    char    *reason_phrase;
+    uint16_t status_code;
+    char    *version;
+} httpishRespLine_t;
+
+typedef enum
+{
+    STATUS_SUCCESS = 0,
+    STATUS_FAILURE,
+    STATUS_UNKNOWN,
+    HSTATUS_SUCCESS = STATUS_SUCCESS,
+    HSTATUS_FAILURE = STATUS_FAILURE,
+    HSTATUS_UNKNOWN = STATUS_UNKNOWN
+} hStatus_t;
+
+typedef enum
+{
+    codeClassInvalid = 0,
+    codeClass1xx = 1,
+    codeClass2xx = 2,
+    codeClass3xx = 3,
+    codeClass4xx = 4,
+    codeClass5xx = 5,
+    codeClass6xx = 6
+} httpishStatusCodeClass_t;
+
+
+/* Creates a message and initializes its internal members */
+PMH_EXTERN httpishMsg_t *httpish_msg_create(void);
+
+/* Frees internal members of the message, such as headers */
+PMH_EXTERN void httpish_msg_free(httpishMsg_t *);
+
+/*
+ * Returns TRUE if the message is a  HTTPish Request, FALSE if not.
+ * Return value will be wrong if the function is called before the
+ * message is complete(see message_is_complete)
+ */
+PMH_EXTERN boolean httpish_msg_is_request(httpishMsg_t *, const char *, int);
+
+/*
+ * A message may be complete or incomplete at a point in time, since
+ * one message may be formed of multiple network packets. This function
+ * returns TRUE if the message is in fact complete, FALSE if not
+ */
+PMH_EXTERN boolean httpish_msg_is_complete(httpishMsg_t *);
+
+/*
+ * Gets a request line structure out of the message i.e parses the message
+ * line. Null on failure, incomplete message etc. User should call
+ * httpish_msg_free_reqline when done to free the allocated memory
+ * inside the structure.
+ */
+PMH_EXTERN httpishReqLine_t *httpish_msg_get_reqline(httpishMsg_t *);
+
+/* Frees internal members of the request line structure */
+PMH_EXTERN void httpish_msg_free_reqline(httpishReqLine_t *);
+
+/*
+ * Gets a response line structure out of the message i.e parses the message
+ * line. Null on failure, incomplete message etc. User should call
+ * httpish_msg_free_respline when done to free the allocated memory
+ * inside the structure.
+ */
+PMH_EXTERN httpishRespLine_t *httpish_msg_get_respline(httpishMsg_t *);
+
+/* Frees internal members of the response line structure */
+PMH_EXTERN void httpish_msg_free_respline(httpishRespLine_t *);
+
+/*
+ * Adds a message line to the message given a request line elements.
+ * This makes it a Request message
+ * This routine allocates memory internally, which is freed on
+ * calling httpish_msg_free()
+ */
+PMH_EXTERN hStatus_t httpish_msg_add_reqline(httpishMsg_t *,
+                                             const char *method,
+                                             const char *url,
+                                             const char *version);
+
+/*
+ * Adds a message line to the message given response line elements.
+ * This makes it a Response message
+ * This routine allocates memory internally, which is freed on
+ * calling httpish_msg_free(). Status codes are assumed to be a
+ * maximum of 6 characters long ie <= 999999
+ */
+PMH_EXTERN hStatus_t httpish_msg_add_respline(httpishMsg_t *,
+                                              const char *version,
+                                              uint16_t status_code,
+                                              const char *reason_phrase);
+
+/*
+ *  Adds a header with a text value to message.
+ *    hname = name of the header for eg. "Content-Type"
+ *    hval = value of the header for eg. "application/sdp"
+ *    This routine allocates memory internally, which is freed on
+ *  calling httpish_msg_free()
+ */
+PMH_EXTERN hStatus_t httpish_msg_add_text_header(httpishMsg_t *msg,
+                                                 const char *hname,
+                                                 const char *hval);
+
+/*
+ *  Adds a header with a integer value to message.
+ *    hname = name of the header for eg. "Content-Length"
+ *    hval = value of the header for eg. 234
+ *    This routine allocates memory internally, which is freed on
+ *    calling httpish_msg_free()
+ */
+/* Assumes the int is less than 10 characters*/
+PMH_EXTERN hStatus_t httpish_msg_add_int_header(httpishMsg_t *msg,
+                                                const char *hname,
+                                                int32_t hvalue);
+
+/*
+ * Removes a header from the message.
+ * hname = name of the header. eg. hname = "From"
+ * Memory for that header string is freed.
+ */
+PMH_EXTERN hStatus_t httpish_msg_remove_header(httpishMsg_t *msg,
+                                               const char *hname);
+
+
+/*
+ *  Returns the value of the header if found, NULL if not.
+ *  hname = name of the header, eg. "Content-Length"
+ *  The pointer should not be freed by the user. (It will be
+ *  freed on freeing the message)
+ */
+PMH_EXTERN const char *httpish_msg_get_header_val(httpishMsg_t *,
+                                                  const char *hname,
+                                                  const char *c_hname);
+
+PMH_EXTERN hStatus_t httpish_msg_get_header_vals(httpishMsg_t *,
+                                                 const char *hname,
+                                                 const char *c_hname,
+                                                 uint16_t *nheaders,
+                                                 char **header_vals);
+
+PMH_EXTERN const char *httpish_msg_get_cached_header_val(httpishMsg_t *, int);
+
+
+/*
+ *  Gets an array of pointers to all the headers. The number of
+ *    headers is returned in num_headers.
+ *    If invoked such :
+ *    all_the_headers = httpish_msg_get_all_headers(...),
+ *    memory is allocated only for "all_the_headers" array, not for
+ *    its individual elements. Hence only the pointer to the all_the_headers
+ *    array should be freed.
+ */
+PMH_EXTERN char **httpish_msg_get_all_headers(httpishMsg_t *msg, uint32_t *num_headers);
+
+PMH_EXTERN uint32_t httpish_msg_get_num_headers(httpishMsg_t *msg);
+
+
+PMH_EXTERN uint16_t httpish_msg_get_num_particular_headers(httpishMsg_t *msg,
+                                                           const char *hname,
+                                                           const char *c_hname,
+                                                           char *header_val[],
+                                                           uint16_t max_headers);
+
+/*
+ * Utility function to get value of the Content-Length header. Returns -1
+ * if the header is not present.
+ */
+PMH_EXTERN int32_t httpish_msg_get_content_length(httpishMsg_t *);
+
+
+/*
+ * Adds the content body to the message. Results in the addition of the
+ * Content-Length header as well.
+ */
+PMH_EXTERN hStatus_t httpish_msg_add_body(httpishMsg_t *msg,
+                                          char *body,
+                                          uint32_t nbytes,
+                                          const char *content_type,
+                                          uint8_t msg_disposition,
+                                          boolean required,
+                                          char *content_id);
+
+PMH_EXTERN boolean httpish_msg_header_present(httpishMsg_t *,
+                                              const char *hname);
+
+
+
+/*
+ *  Allocates a buffer and writes the message into it in the network
+ *        format i.e ready to send. On return, nbytes is filled in with
+ *        the number of bytes contained in the message buffer. Returns
+ *        NULL on failure. User needs to free memory when done.
+ */
+PMH_EXTERN char *httpish_msg_write_to_buf(httpishMsg_t * msg, uint32_t *nbytes);
+
+/* Writes it out as a null terminated string. Expected use is debug */
+PMH_EXTERN char *httpish_msg_write_to_string(httpishMsg_t * msg);
+
+/*
+ * Writes out a message in the network format(ie ready to send),
+ * in a user provided buffer. nbytes is filled in with the number
+ * of bytes in buf on entry and filled in with the
+ * actual number of bytes written if the return value is SUCCESS.
+ * There is no attempt to grow or realloc the buffer ie FAILURE
+ * is returned if the buffer is not large enough.
+ */
+PMH_EXTERN hStatus_t httpish_msg_write(httpishMsg_t *msg,
+                                       char *buf,
+                                       uint32_t *nbytes);
+
+/*
+ *  This function is used to read bytes from a network packet into the
+ *        message structure.
+ *  msg = previously created httpishMsg using httpish_msg_create()
+ *  nmsg = network message
+ *  bytes_read = Number of bytes read from nmsg is filled in when the
+ *               function returns.
+ *  It is expected that after this returns HSTATUS_SUCCESS,
+ *  httpish_is_message_complete() is called to see whether a complete
+ *  message was received(or a previously started message was completed,
+ *  at which point it can be processed further.
+ */
+PMH_EXTERN hStatus_t httpish_msg_process_network_msg(httpishMsg_t *msg,
+                                                     char *nmsg,
+                                                     uint32_t *bytes_read);
+
+/*
+ * Utility to get the class of status codes in http like responses.
+ * For eg., the class for a code 480 is 4, as currently defined.
+ */
+PMH_EXTERN httpishStatusCodeClass_t httpish_msg_get_code_class(uint16_t statusCode);
+
+
+#endif /* _HTTPISH_H_ */
diff --git a/libs/sipcc/core/sipstack/h/httpish_protocol.h b/libs/sipcc/core/sipstack/h/httpish_protocol.h
new file mode 100644 (file)
index 0000000..3a1751d
--- /dev/null
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _HTTPISH_PROTOCOL_H_
+#define _HTTPISH_PROTOCOL_H_
+
+/*
+ * Some common headers only. There does not seem a whole lot of
+ * sense in defining error codes since they tend to have different
+ * semantics
+ */
+
+#define HTTPISH_HEADER_CONTENT_LENGTH "Content-Length"
+#define HTTPISH_C_HEADER_CONTENT_LENGTH "l"
+#define HTTPISH_HEADER_CONTENT_TYPE "Content-Type"
+#define HTTPISH_HEADER_CONTENT_ENCODING "Content-Encoding"
+#define HTTPISH_HEADER_CONTENT_ID   "Content-Id"
+
+#define HTTPISH_HEADER_USER_AGENT "User-Agent"
+#define HTTPISH_HEADER_SERVER "Server"
+#define HTTPISH_HEADER_DATE "Date"
+
+#define HTTPISH_HEADER_VIA "Via"
+#define HTTPISH_HEADER_MAX_FORWARDS "Max-Forwards"
+#define HTTPISH_HEADER_EXPIRES "Expires"
+#define HTTPISH_HEADER_LOCATION "Location"
+
+#define HTTPISH_HEADER_MIME_VERSION "Mime-Version"
+#define uniqueBoundary "uniqueBoundary"
+
+#endif /* _HTTPISH_PROTOCOL_H_ */
diff --git a/libs/sipcc/core/sipstack/h/pmhdefs.h b/libs/sipcc/core/sipstack/h/pmhdefs.h
new file mode 100644 (file)
index 0000000..5e1338b
--- /dev/null
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PMH_DEFS_H_
+#define _PMH_DEFS_H_
+
+#define UTILFREE(x)  cpr_free(x)
+
+#define PMH_EXTERN extern
+
+#endif /* _PMH_DEFS_H_ */
diff --git a/libs/sipcc/core/sipstack/h/pmhutils.h b/libs/sipcc/core/sipstack/h/pmhutils.h
new file mode 100644 (file)
index 0000000..0ffe148
--- /dev/null
@@ -0,0 +1,163 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PMH_UTILS_H_
+#define _PMH_UTILS_H_
+
+#include "cpr_types.h"
+#include "pmhdefs.h"
+
+/*
+ * Define a "stream" created from an input packet. We have to do this
+ * since SIP * messages may come in over TCP or UDP, so we cannot use
+ * TCP stream semantics. It is expected that a client interfaces to
+ * these streams via the API functions defined below, rather than
+ * directly.
+ */
+typedef struct
+{
+    char   *buff;
+    char   *loc;  /* This points to the next byte that will be read */
+    int32_t nbytes;
+    int32_t bytes_read;
+    boolean eof;
+    boolean error;
+} pmhRstream_t;
+
+
+/*
+ * Defines a write stream to write things to and finally create an
+ * "output packet". It is expected that a client interfaces to
+ * these streams via the API functions defined below, rather than
+ * directly.
+ */
+typedef struct
+{
+    char *buff;
+    int32_t nbytes;
+    int32_t total_bytes;
+    boolean growable;
+} pmhWstream_t;
+
+typedef struct
+{
+    uint16_t num_tokens;
+    char **tokens;
+} pmhTokens_t;
+
+/*
+ * Buf is the input buffer holding data that you want to use a "stream".
+ * The stream uses buf as is, so dont free it. Free it only when done
+ * ie. by calling pmhutils_rstream_delete. Returns NULL on failure.
+ * buf = Input buffer containing data.
+ * nbytes = Number of bytes in buf.
+ */
+PMH_EXTERN pmhRstream_t *pmhutils_rstream_create(char *buf, uint32_t nbytes);
+
+
+/*
+ * Frees internal memory. If freebuf is TRUE, it frees the
+ * buffer it was created with. If not TRUE, the internal buffer
+ * should be held by the user and freed when appropriate.
+ */
+PMH_EXTERN void pmhutils_rstream_delete(pmhRstream_t *rs, boolean freebuf);
+
+
+/*
+ * Returns a character from the stream, and advances internal pointer.
+ * If no characters are left, returns '\0' and sets the eof to TRUE.
+ */
+PMH_EXTERN char pmhutils_rstream_read_byte(pmhRstream_t *);
+
+
+/*
+ * Returns a character buffer  from the stream,
+ * and advances internal pointer.
+ * If no characters are left, returns NULL  and sets the eof to TRUE.
+ * This routine allocates memory which should be freed by the user.
+ * nbytes = Number of bytes asked for.
+ */
+PMH_EXTERN char *pmhutils_rstream_read_bytes(pmhRstream_t *rs, int32_t nbytes);
+
+/*
+ * Returns a string created from a line(as terminated by \r or \n or \r\n)
+ * in the stream. This will actually allocate memory for the string,
+ * which should be freed by the user when done. Empty lines are returned
+ * as empty strings - ie. first char is \0.
+ * Updates internal pointer in the stream.
+ * Returns NULL if eof has been reached on the stream
+ */
+PMH_EXTERN char *pmhutils_rstream_read_line(pmhRstream_t *);
+
+/* Creates a write stream with an internal buffer of default size */
+PMH_EXTERN pmhWstream_t *pmhutils_wstream_create(void);
+
+/* Creates a write stream with a user provided buffer */
+PMH_EXTERN pmhWstream_t *pmhutils_wstream_create_with_buf(char *buf,
+                                                          uint32_t nbytes);
+
+/*
+ * Grows the write streams internal buffer by a default step.
+ * Mostly used internally by the write functions
+ */
+PMH_EXTERN boolean pmhutils_wstream_grow(pmhWstream_t *);
+
+/*
+ * Frees the internal elements of the write stream,
+ * including its internal buffer if freebuf is TRUE. If freebuf
+ * is FALSE, the buffer should be held by the user and freed
+ * when appropriate.
+ */
+PMH_EXTERN void pmhutils_wstream_delete(pmhWstream_t *ws, boolean freebuf);
+
+
+/*
+ * Writes the input string, followed by a SIP New line
+ * ie. \r\n
+ * Returns FALSE on failure.
+ * Expects a NULL terminated string in "line".
+ * Returns FALSE on failure(ie no memory etc.)
+ * May result in creation of memory, which will
+ * be freed when pmhutils_wstream_free_internal is called.
+ */
+PMH_EXTERN boolean pmhutils_wstream_write_line(pmhWstream_t *ws, char *line);
+
+/*
+ * Writes a single character to the output stream.
+ * Returns FALSE on failure, TRUE on success.
+ */
+PMH_EXTERN boolean pmhutils_wstream_write_byte(pmhWstream_t *, char);
+
+/*
+ * Writes a buffer(of length len pointed to by buf) to
+ * the stream. May result in creation of memory, which will
+ * be freed when pmhutils_wstream_free_internal is called.
+ * Returns FALSE on failure(ie no memory etc.)
+ */
+PMH_EXTERN boolean pmhutils_wstream_write_bytes(pmhWstream_t *ws, char *buf,
+                                                uint32_t len);
+
+/*
+ * Returns the internal buffer, and fills in its length in nbytes.
+ * This would be used just before wstream_delete(.., FALSE) in order
+ * to hold the buffer.
+ */
+PMH_EXTERN char *pmhutils_wstream_getbuf(pmhWstream_t *, uint32_t *nbytes);
+
+PMH_EXTERN uint32_t pmhutils_wstream_get_length(pmhWstream_t *ws);
+
+/*
+ * A tokenizer like strtok, but creates memory for every token.
+ * Use only when you have to create the memory anyway.
+ * Returns NULL if no tokens are found or incase memory allocation
+ * fails.
+ */
+PMH_EXTERN pmhTokens_t *pmh_tokenize(const char *str, const char *tokens);
+
+/*
+ * Util to free all the tokens
+ */
+PMH_EXTERN void pmh_tokens_free(pmhTokens_t *tokens);
+
+#endif /*_PMH_UTILS_H_*/
diff --git a/libs/sipcc/core/sipstack/h/regmgrapi.h b/libs/sipcc/core/sipstack/h/regmgrapi.h
new file mode 100755 (executable)
index 0000000..b4a7e51
--- /dev/null
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _REGMGRAPI_H_
+#define _REGMGRAPI_H_
+
+#include "sessionConstants.h"
+
+typedef enum reg_mode_t_ {
+    REG_MODE_CCM = CC_MODE_CCM,
+    REG_MODE_NON_CCM = CC_MODE_NONCCM,
+} reg_mode_t;
+
+reg_mode_t sip_regmgr_get_cc_mode(line_t line);
+
+#endif /* _REGMGRAPI_H_ */
diff --git a/libs/sipcc/core/sipstack/h/sip_ccm_transport.h b/libs/sipcc/core/sipstack/h/sip_ccm_transport.h
new file mode 100644 (file)
index 0000000..e200cdc
--- /dev/null
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __SIP_CCM_TRANSPORT_H__
+#define __SIP_CCM_TRANSPORT_H__
+
+#include "cpr_types.h"
+#include "cpr_socket.h"
+#include "phone_types.h"
+
+#define CCM_ID_PRINT(arg) \
+        (arg == PRIMARY_CCM ?  "PRIMARY_CCM" : \
+        arg == SECONDARY_CCM ?  "SECONDARY_CCM" : \
+        arg == TERTIARY_CCM ?  "TERTIARY_CCM" : \
+        arg == MAX_CCM ?  "MAX_CCM" : \
+        arg == UNUSED_PARAM ?  "UNUSED_PARAM" : "Unknown")\
+
+
+#endif /* __SIP_CCM_TRANSPORT_H__ */
diff --git a/libs/sipcc/core/sipstack/h/sip_common_regmgr.h b/libs/sipcc/core/sipstack/h/sip_common_regmgr.h
new file mode 100644 (file)
index 0000000..cb88fe7
--- /dev/null
@@ -0,0 +1,157 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __SIP_COMMON_REGMGR_H__
+#define __SIP_COMMON_REGMGR_H__
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_timers.h"
+#include "cpr_string.h"
+#include "cpr_memory.h"
+#include "ccsip_core.h"
+#include "singly_link_list.h"
+#include "ccsip_platform.h"
+#include "sip_common_transport.h"
+
+#define LINE1  0
+#define LINE2  1
+#define LINE3  2
+#define LINE4  3
+#define LINE5  4
+#define LINE6  5
+#define LINE7  6
+#define LINE8  7
+#define LINE9  8
+#define LINE10 9
+#define LINE11 10
+#define LINE12 11
+#define LINE13 12
+#define LINE14 13
+#define LINE15 14
+#define LINE16 15
+#define LINE17 16
+#define LINE18 17
+#define LINE19 18
+#define LINE20 19
+
+#define RSP_START 1
+#define RSP_COMPLETE 2
+#define FAILOVER_RSP 0
+#define MAX_FALLBACK_MONITOR_PERIOD 300
+#define TLS_CONNECT_TIME 8
+
+#define TOKEN_REFER_TO  "<urn:X-cisco-remotecc:token-registration>"
+
+typedef enum {
+    ACTIVE_FD = 0,
+    STANDBY_FD,
+    MAX_FALLBACK_FDs
+} CC_FDs;
+
+typedef enum {
+    RET_SUCCESS = 0,
+    RET_NO_STANDBY,
+    RET_START_FALLBACK,
+    RET_INIT_REBOOT
+} RET_CODE;
+
+typedef struct fallback_ccb_t_ {
+    ccsipCCB_t *ccb;
+    sipPlatformUIExpiresTimer_t WaitTimer;
+    sipPlatformUIExpiresTimer_t RetryTimer;
+    uint32_t StabilityMsgCount;
+    boolean tls_socket_waiting;
+} fallback_ccb_t;
+
+typedef struct ccm_fd_table_t_ {
+    ti_config_table_t *active_ccm_entry;
+    ti_config_table_t *standby_ccm_entry;
+} ccm_act_stdby_table_t;
+
+//ccm_act_stdby_table_t CCM_Active_Standby_Table[MAX_TEL_LINES+1];
+
+typedef struct ccm_failover_table_t_ {
+    ti_config_table_t *failover_ccm_entry;
+    ti_config_table_t *fallback_ccm_entry;
+    boolean prime_registered;
+    boolean failover_started;
+} ccm_failover_table_t;
+
+typedef struct ccm_fallback_table_t_ {
+    ti_config_table_t *fallback_ccm_entry;
+    boolean is_idle;
+    boolean is_resp;
+    ccsipCCB_t *ccb;
+} ccm_fallback_table_t;
+
+typedef struct {
+    uint32_t   ccb_index;   /* ccb index for which this msg is intended for */
+    CCM_ID     ccm_id;      /* cucm id */
+} ccsip_registration_msg_t;
+
+void notify_register_update(int availableLine);
+fallback_ccb_t *sip_regmgr_get_fallback_ccb_by_index(line_t index);
+int sip_regmgr_destroy_cc_conns(void);
+int sip_regmgr_init(void);
+void sip_regmgr_register_lines(boolean prime_only, boolean skip_prime);
+void sipRegmgrSendRegisterMsg(uint8_t line, ccsipCCB_t *ccb);
+void sip_regmgr_ev_cancel(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_in_fallback_any_response(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_failure_response(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_tmr_ack_retry(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_tmr_expire_standby(ccsipCCB_t *ccb, sipSMEvent_t *event);
+
+void sip_regmgr_ev_unreg_tmr_ack(ccsipCCB_t *cb);
+void sip_regmgr_trigger_fallback_monitor(void);
+void sip_regmgr_setup_new_standby_ccb(CCM_ID ccm_id);
+void sip_regmgr_free_fallback_ccb(ccsipCCB_t *ccb);
+void sip_regmgr_retry_timeout_expire(void *data);
+void sip_regmgr_stability_timeout_expire(void *data);
+void sip_regmgr_find_fallback_ccb_by_callid(const char *callid,
+                                            ccsipCCB_t **ccb_ret);
+boolean sip_regmgr_find_fallback_ccb_by_addr_port(cpr_ip_addr_t *ipaddr,
+                                                  uint16_t port,
+                                                  ccsipCCB_t **ccb_ret);
+sll_match_e sip_regmgr_fallback_ccb_find(void *find_by_p, void *data_p);
+void sip_regmgr_retry_timer_start(fallback_ccb_t *fallback_ccb);
+ti_config_table_t *sip_regmgr_ccm_get_conn(line_t dn,
+                                           ti_config_table_t *ccm_entry);
+void sip_regmgr_check_and_transition(ccsipCCB_t *ccb);
+void sip_regmgr_ev_default(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_fallback_retry(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_wait_timeout_expire(void *data);
+void sip_regmgr_ev_in_fallback_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_stability_check_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_stability_check_tmr_stable(ccsipCCB_t *ccb,
+                                              sipSMEvent_t *event);
+void sip_regmgr_ev_stability_check_tmr_wait(ccsipCCB_t *ccb,
+                                            sipSMEvent_t *event);
+void sip_regmgr_ev_token_wait_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_cleanup(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_token_wait_4xx_n_5xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_ev_token_wait_tmr_wait(ccsipCCB_t *ccb, sipSMEvent_t *event);
+void sip_regmgr_shutdown(void);
+
+void sip_regmgr_rsp(int rsp_id, int rsp_type, boolean waited);
+
+boolean sip_regmgr_check_config_change(void);
+
+void sip_regmgr_process_config_change(void);
+void sip_regmgr_send_refer(ccsipCCB_t *ccb);
+void sip_regmgr_ccm_restarted(ccsipCCB_t *new_reg_ccb);
+void sip_regmgr_notify_timer_callback(void *data);
+ccsipCCB_t *sip_regmgr_get_fallback_ccb_list(uint32_t *previous_data_p);
+void sip_regmgr_replace_standby(ccsipCCB_t *ccb);
+void sip_regmgr_regallfail_timer_callback(void *data);
+void sip_regmgr_handle_reg_all_fail(void);
+void sip_regmgr_get_config_addr(int ccm_id, char *add_str);
+
+extern boolean g_disable_mass_reg_debug_print;
+
+void regmgr_handle_register_update(line_t last_available_line);
+
+
+#endif /* __SIP_COMMON_REGMGR_H__ */
diff --git a/libs/sipcc/core/sipstack/h/sip_common_transport.h b/libs/sipcc/core/sipstack/h/sip_common_transport.h
new file mode 100644 (file)
index 0000000..e6b4bf0
--- /dev/null
@@ -0,0 +1,223 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __SIP_COMMON_TRANSPORT_H__
+#define __SIP_COMMON_TRANSPORT_H__
+
+#include "cpr_types.h"
+#include "cpr_socket.h"
+#include "ccsip_pmh.h"
+#include "sip_csps_transport.h"
+#include "sip_ccm_transport.h"
+#include "singly_link_list.h"
+
+
+#define MAX_CONNECTIONS 5
+
+/* Macro definition for chacking a valid connid (TCP/UDP) */
+#define VALID_CONNID(connid) \
+       (connid >= 0 && connid < MAX_CONNECTIONS)
+
+typedef enum {
+    NONE_CC = 0,
+    ACTIVE_CC = 1,
+    STANDBY_CC
+} CC_POSITION;
+
+/*
+ * Define the types of connections
+ */
+/*
+ * NOTE: Need to match the values in the config matrix
+ */
+typedef enum {
+    CONN_NONE = 0,
+    CONN_TCP,
+    CONN_UDP,
+    CONN_TLS,
+    CONN_TCP_TMP,
+    CONN_MAX_TYPES
+} CONN_TYPE;
+
+// Device Security Modes
+typedef enum sec_level_t_ {
+    NON_SECURE = 0,  // Normal, no security
+    AUTHENTICATED,   // Use TLS, server will use NULL encryption
+    ENCRYPTED,       // Use TLS, server will use AES encryption
+    NOT_IN_CTL       // Not in CTL should not be seen by SIP.
+} sec_level_t;
+
+typedef enum conn_create_status_t_ {
+    CONN_INVALID = -1,
+    CONN_SUCCESS,
+    CONN_FAILURE
+} conn_create_status_t;
+
+typedef struct ti_common_t_ {
+    uint16_t     listen_port;
+    char         addr_str[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t     addr;
+    uint16_t     port;
+    uint16_t     sec_port;
+    CONN_TYPE    conn_type;
+    CONN_TYPE    configured_conn_type;
+    cpr_socket_t handle;
+} ti_common_t;
+
+typedef struct ti_ccm_t_ {
+    CCM_ID  ccm_id;
+    int32_t sec_level;
+    int32_t is_valid;
+} ti_ccm_t;
+
+typedef struct ti_csps_t_ {
+    char     bkup_pxy_addr_str[MAX_IPADDR_STR_LEN];
+    cpr_ip_addr_t bkup_pxy_addr;
+    uint16_t bkup_pxy_port;
+    char     emer_pxy_addr_str[MAX_IPADDR_STR_LEN];
+    uint16_t emer_pxy_port;
+    char     outb_pxy_addr_str[MAX_IPADDR_STR_LEN];
+    uint16_t outb_pxy_port;
+} ti_csps_t;
+
+typedef struct ti_config_table_t_ {
+    CC_ID       cc_type;
+    ti_common_t ti_common;
+    union {
+        ti_ccm_t   ti_ccm;
+        ti_csps_t *ti_csps;
+    } ti_specific;
+} ti_config_table_t;
+
+extern ti_config_table_t *CCM_Config_Table[MAX_REG_LINES + 1][MAX_CCM];
+extern ti_config_table_t CCM_Dummy_Entry;
+extern ti_config_table_t CSPS_Config_Table[MAX_REG_LINES];
+typedef struct cc_config_table_t_ {
+    CC_ID cc_type;
+    void *cc_table_entry; // Needs to deferenced as
+    // ti_config_table_t*
+} cc_config_table_t;
+
+typedef long sipSPIConnId_t;
+
+typedef enum {
+    SOCKET_NO_ERROR = -1,
+    SOCKET_SEND_ERROR,
+    SOCKET_RECV_ERROR,
+    SOCKET_OPEN_ERROR,
+    SOCKET_OPT_ERROR,
+    SOCKET_CONNECT_ERROR,
+    SOCKET_CONN_REFUSED_ERROR,
+    SOCKET_BIND_ERROR,
+    SOCKET_REMOTE_CLOSURE,
+    SOCKET_ADMIN_CLOSURE,
+    SIP_TCP_CONN_TABLE_FULL
+} ccsipSockErrCodes_e;
+
+/* All the possible state of the TCP/UDP sockets */
+typedef enum {
+    SOCK_IDLE,            /* not inuse */
+    SOCK_LISTENING,       /* fd is listening on the well-known port */
+    SOCK_ACCEPTED,        /* connection accepted          */
+    SOCK_CONNECTED,       /* connection made              */
+    SOCK_CONNECT_PENDING, /* connection is pending */
+    SOCK_FAILED           /* failed, will be closed when all calls fail */
+} sock_state_t;
+
+typedef struct {
+    cpr_ip_addr_t   addr;
+    uint16_t        port;
+    CONN_TYPE       transport;  /* Specifies UDP, Multicast UDP, TCP */
+    uint8_t         ip_sig_tos;
+    uint16_t        local_listener_port;
+} sipSPICreateConnection_t;
+
+typedef struct {
+    void *context;
+    sipSPICreateConnection_t createConnMsg;
+} sipSPIMessage_t;
+
+
+void sipTransportSetServerHandleAndPort(cpr_socket_t socket_handle,
+                                        uint16_t listen_port,
+                                        ti_config_table_t *ccm_table_entry);
+int sip_dns_gethostbysrv(char *domain,
+                         cpr_ip_addr_t *ipaddr_ptr,
+                         uint16_t *port,
+                         srv_handle_t *srv_order,
+                         boolean retried_addr);
+int sip_dns_gethostbysrvorname(char *hname,
+                               cpr_ip_addr_t *ipaddr_ptr,
+                               uint16_t *port);
+
+conn_create_status_t sip_transport_setup_cc_conn(line_t dn, CCM_ID ccm_id);
+int sip_transport_destroy_cc_conn(line_t dn, CCM_ID ccm_id);
+
+int16_t SIPTaskGetProxyPortByDN(line_t dn);
+uint32_t SIPTaskGetProxyAddressByDN(line_t dn);
+cpr_socket_t SIPTaskGetProxyHandleByDN(line_t dn);
+
+int sipTransportCreateSendMessage(ccsipCCB_t *ccb,
+                                  sipMessage_t *pSIPMessage,
+                                  sipMethod_t message_type,
+                                  cpr_ip_addr_t *cc_remote_ipaddr,
+                                  uint16_t cc_remote_port,
+                                  boolean isRegister,
+                                  boolean reTx,
+                                  int timeout, void *scbp,
+                                  int reldev_stored_msg);
+#define sipTransportChannelCreateSend(a, b, m, c, d, e, f) \
+sipTransportCreateSendMessage(a, b, m, c, d, FALSE, TRUE, e, NULL, f)
+
+int sipTransportSendMessage(ccsipCCB_t *ccb,
+                            char *pOutMessageBuf,
+                            uint32_t nbytes,
+                            sipMethod_t message_type,
+                            cpr_ip_addr_t *cc_remote_ipaddr,
+                            uint16_t cc_remote_port,
+                            boolean isRegister,
+                            boolean reTx,
+                            int timeout,
+                            void *scbp);
+#define sipTransportChannelSend(a, b, c, m, d, e, f) \
+sipTransportSendMessage(a, b, c, m, d, e, FALSE, TRUE, f, NULL)
+
+int sipTransportGetServerAddrPort(char *domain,
+                                  cpr_ip_addr_t *ipaddr_ptr,
+                                  uint16_t *port,
+                                  srv_handle_t *psrv_order,
+                                  boolean retried_addr);
+int      sipTransportGetPrimServerPort(line_t line);
+int      sipTransportGetBkupServerPort(line_t line);
+int      sipTransportGetEmerServerPort(line_t line);
+int      sipTransportGetOutbProxyPort(line_t line);
+cpr_ip_type     sipTransportGetPrimServerAddress(line_t line, char *buffer);
+uint16_t  sipTransportGetBkupServerAddress(cpr_ip_addr_t *pip_addr,
+                                            line_t line, char *buffer);
+void     sipTransportGetEmerServerAddress(line_t line, char *buffer);
+void     sipTransportGetOutbProxyAddress(line_t line, char *buffer);
+void sipTransportGetServerIPAddr(cpr_ip_addr_t *pip_addr, line_t line);
+int      sipTransportInit(void);
+uint16_t sipTransportGetServerAddress(cpr_ip_addr_t *pip_addr, line_t dn, line_t index);
+short    sipTransportGetServerPort(line_t dn, line_t index);
+
+int      sipTransportGetCCType(int line, void *cc_table_entry);
+void     sip_regmgr_set_cc_info(line_t line, line_t dn_line,
+                                CC_ID *cc_type, void *cc_table_entry);
+uint16_t sipTransportGetListenPort(line_t line, ccsipCCB_t *ccb);
+const char *sipTransportGetTransportType(line_t line, boolean upper_case,
+                                         ccsipCCB_t *ccb);
+
+void     sipTransportShutdown(void);
+extern void ccsip_dump_send_msg_info(char *msg, sipMessage_t *pSIPMessage,
+                               cpr_ip_addr_t *cc_remote_ipaddr,
+                               uint16_t cc_remote_port);
+void SIPTaskProcessTCPMessage(sipMessage_t *pSipMessage,
+                              cpr_sockaddr_storage from);
+
+void
+sipTransportSetSIPServer();
+
+
+#endif /* __SIP_COMMON_TRANSPORT_H__ */
diff --git a/libs/sipcc/core/sipstack/h/sip_csps_transport.h b/libs/sipcc/core/sipstack/h/sip_csps_transport.h
new file mode 100644 (file)
index 0000000..0797985
--- /dev/null
@@ -0,0 +1,47 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __SIP_CSPS_TRANSPORT_H__
+#define __SIP_CSPS_TRANSPORT_H__
+
+#include "cpr_types.h"
+#include "phone_types.h"
+#include "phone_debug.h"
+#include "cfgfile_utils.h"
+#include "configmgr.h"
+#include "ccsip_protocol.h"
+#include "ccsip_pmh.h"
+#include "ccsip_platform_timers.h"
+#include "ccsip_platform_udp.h"
+#include "ccsip_messaging.h"
+
+/*
+ * Defines for Primary, Secondary and Tertiary CC
+ */
+typedef enum {
+    PRIMARY_CSPS = 0,
+    MAX_CSPS
+} CSPS_ID;
+
+//extern csps_config_info_t CSPS_Config_Table;
+
+cpr_socket_t sipTransportCSPSGetProxyHandleByDN(line_t dn);
+short    sipTransportCSPSGetProxyPortByDN(line_t dn);
+uint16_t sipTransportCSPSGetProxyAddressByDN(cpr_ip_addr_t *pip_addr,
+                                             line_t dn);
+
+uint16_t sip_config_get_proxy_port(line_t line);
+uint16_t sip_config_get_backup_proxy_port(void);
+void     sip_config_get_proxy_addr(line_t line, char *buffer, int buffer_len);
+uint16_t sip_config_get_backup_proxy_addr(cpr_ip_addr_t *IPAddress,
+                                           char *buffer, int buffer_len);
+
+extern sipPlatformUITimer_t sipPlatformUISMTimers[];
+extern ccsipGlobInfo_t gGlobInfo;
+
+extern int dns_error_code; // DNS errror code global
+void sipTransportCSPSClearProxyHandle(cpr_ip_addr_t *ipaddr, uint16_t port,
+                                      cpr_socket_t this_fd);
+
+#endif /* __SIP_CSPS_TRANSPORT_H__ */
diff --git a/libs/sipcc/core/sipstack/h/sip_interface_regmgr.h b/libs/sipcc/core/sipstack/h/sip_interface_regmgr.h
new file mode 100644 (file)
index 0000000..a12d179
--- /dev/null
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SIP_INTERFACE_REGMGR_H_
+#define _SIP_INTERFACE_REGMGR_H_
+
+#include "cpr_types.h"
+#include "phone_types.h"
+#include "sip_common_transport.h"
+#include "regmgrapi.h"
+
+typedef enum reg_rcs_t_ {
+    REG_RC_SUCCESS,
+    REG_RC_ERROR,
+    REG_RC_MAX
+} reg_rcs_t;
+
+typedef enum reg_srcs_t_ {
+    REG_SRC_GSM,
+    REG_SRC_SIP,
+    REG_SRC_MAX
+} reg_srcs_t;
+
+typedef enum reg_status_t_ {
+    REG_FAIL,
+    REG_ALL_FAIL
+} reg_status_t;
+
+typedef struct reg_status_msg_t_ {
+    reg_srcs_t src_id;
+    reg_status_t msg_id;
+} reg_status_msg_t;
+
+typedef enum {
+    CCM_STATUS_NONE = 0,
+    CCM_STATUS_STANDBY,
+    CCM_STATUS_ACTIVE
+} reg_ccm_status;
+
+void sip_regmgr_send_status(reg_srcs_t src_id, reg_status_t msg_id);
+sec_level_t sip_regmgr_get_sec_level(line_t line);
+boolean sip_regmgr_srtp_fallback_enabled(line_t line);
+void sip_platform_set_ccm_status();
+void sip_platform_cc_mode_notify(void);
+void sip_platform_failover_ind(CCM_ID ccm_id);
+void sip_platform_fallback_ind(CCM_ID ccm_id);
+extern CCM_ID sip_regmgr_get_ccm_id(ccsipCCB_t *ccb);
+boolean sip_platform_is_phone_idle(void);
+extern void platform_reg_failover_ind(void *to_id);
+extern void platform_reg_fallback_ind(void *from_id);
+extern void sip_regmgr_phone_idle(boolean waited);
+extern void sip_regmgr_fallback_rsp();
+extern void sip_regmgr_failover_rsp_start();
+extern void sip_regmgr_failover_rsp_complete();
+extern void ui_set_ccm_conn_status(const char *addr_str, int status);
+extern void platform_cc_mode_notify(int mode);
+extern void ui_reg_all_failed(void);
+extern void platform_reg_fallback_cfm(void);
+extern void platform_regallfail_ind(void *);
+extern void sip_platform_logout_reset_req(void);
+extern void platform_logout_reset_req (void);
+
+#endif /* _SIP_INTERFACE_REGMGR_H_ */
diff --git a/libs/sipcc/core/sipstack/h/sip_platform_task.h b/libs/sipcc/core/sipstack/h/sip_platform_task.h
new file mode 100644 (file)
index 0000000..2521906
--- /dev/null
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SIP_PLATFORM_TASK_H_
+#define _SIP_PLATFORM_TASK_H_
+
+#include "cpr_socket.h"
+
+/*
+ * Prototypes
+ */
+void sip_platform_task_loop(void *arg);
+void sip_platform_task_set_listen_socket(cpr_socket_t s);
+void sip_platform_task_set_read_socket(cpr_socket_t s);
+void sip_platform_task_clr_read_socket(cpr_socket_t s);
+
+void sip_platform_task_reset_listen_socket(cpr_socket_t s);
+
+#endif
diff --git a/libs/sipcc/core/sipstack/httpish.c b/libs/sipcc/core/sipstack/httpish.c
new file mode 100644 (file)
index 0000000..f909a7b
--- /dev/null
@@ -0,0 +1,1642 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *  Functions that parse and create HTTP/1.1-like messages(RFC 2068). Basically
+ *  code that converts from network(ie text) form to a usable structure
+ *  and vice-versa.
+ */
+
+#include <errno.h>
+#include <limits.h>
+
+#include "plstr.h"
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "httpish.h"
+#include "ccsip_protocol.h"
+#include "phone_debug.h"
+#include "ccsip_core.h"
+
+//#define HTTPISH_DEBUG if (1)
+#define MSG_DELIMIT_SIZE   80
+#define TMP_BODY_BUF_SIZE 200
+#define CMPC_HEADER_SIZE  256
+
+extern sip_header_t sip_cached_headers[];
+httpishMsg_t *
+httpish_msg_create (void)
+{
+    int i;
+    httpishMsg_t *msg;
+
+    msg = (httpishMsg_t *) cpr_calloc(1, sizeof(httpishMsg_t));
+    if (!msg) {
+        return NULL;
+    }
+
+    msg->headers = (queuetype *) cpr_calloc(1, sizeof(queuetype));
+
+    if (!msg->headers) {
+        cpr_free(msg);
+        return NULL;
+    }
+
+    msg->retain_flag = FALSE;
+    msg->mesg_line = NULL;
+    msg->content_length = 0;
+    msg->is_complete = FALSE;
+    msg->headers_read = FALSE;
+
+    for (i = 0; i < HTTPISH_MAX_BODY_PARTS; i++) {
+        msg->mesg_body[i].msgContentType = NULL;
+        msg->mesg_body[i].msgBody = NULL;
+        msg->mesg_body[i].msgLength = 0;
+        msg->mesg_body[i].msgContentId = NULL;
+        msg->mesg_body[i].msgContentEnc = SIP_CONTENT_ENCODING_IDENTITY_VALUE;
+        msg->mesg_body[i].msgContentDisp = SIP_CONTENT_DISPOSITION_SESSION_VALUE;
+        msg->mesg_body[i].msgRequiredHandling = TRUE;
+        msg->mesg_body[i].msgContentTypeValue = SIP_CONTENT_TYPE_UNKNOWN_VALUE;
+    }
+
+    msg->num_body_parts = 0;
+    msg->raw_body = NULL;
+
+    queue_init(msg->headers, 0);
+
+    return msg;
+}
+
+int
+httpish_strncasecmp(const char *s1, const char* s2, size_t len)
+{
+  /*This routine is an enhanced version of strncasecmp().
+   *It ensures that the two strings being compared for size "len"
+   *don't have trailing characters beyond "len" chars.
+   *The trailing whitespaces beyond "len" chars is ignored.
+   */
+    const unsigned char *us1 = (const unsigned char *) s1;
+    const unsigned char *us2 = (const unsigned char *) s2;
+
+    /* No match if only one ptr is NULL */
+    if ((!s1 && s2) || (s1 && !s2))
+        return ((int) (s1 - s2));
+
+    if ((len == 0) || (s1 == s2))
+        return 0;
+
+    while (len-- > 0 && toupper(*us1) == toupper(*us2)) {
+        if (len == 0 || *us1 == '\0' || *us2 == '\0')
+            break;
+        us1++;
+        us2++;
+    }
+
+   if (len == 0 && toupper(*us1) == toupper(*us2)) {
+       //all "len" chars are compared, need to look for trailing
+       //chars beyond "len" string size. Ignore white spaces.
+      while (*(++us1) != '\0') {
+          if (*us1 != ' ' && *us1 != '\t') {
+               break;
+          }
+      }
+
+      while (*(++us2) != '\0') {
+          if (*us2 != ' ' && *us2 != '\t') {
+               break;
+          }
+      }
+   }
+
+
+   return (toupper(*us1) - toupper(*us2));
+}
+
+void
+httpish_msg_free (httpishMsg_t *msg)
+{
+    int i;
+
+    if ((!msg) || (msg->retain_flag == TRUE)) {
+        return;
+    }
+
+    UTILFREE(msg->mesg_line);
+
+    // Free all body parts
+    for (i = 0; i < HTTPISH_MAX_BODY_PARTS; i++) {
+        UTILFREE(msg->mesg_body[i].msgContentType);
+        UTILFREE(msg->mesg_body[i].msgBody);
+        UTILFREE(msg->mesg_body[i].msgContentId);
+    }
+    UTILFREE(msg->raw_body);
+
+    if (msg->headers) {
+        httpish_header *this_header;
+
+        this_header = (httpish_header *) dequeue(msg->headers);
+        while (this_header != NULL) {
+            UTILFREE(this_header->header);
+            UTILFREE(this_header);
+            this_header = (httpish_header *) dequeue(msg->headers);
+        }
+    }
+
+    UTILFREE(msg->headers);
+    msg->headers = NULL;
+
+    /* Free the header cache */
+    for (i = 0; i < HTTPISH_HEADER_CACHE_SIZE; ++i) {
+        if (msg->hdr_cache[i].hdr_start) {
+            cpr_free(msg->hdr_cache[i].hdr_start);
+        }
+    }
+
+    /* Free the httpishMsg_t struct itself */
+    cpr_free(msg);
+}
+
+boolean
+httpish_msg_is_request (httpishMsg_t *msg,
+                        const char *schema,
+                        int schema_len)
+{
+    char *loc;
+
+    loc = msg->mesg_line;
+
+    if (!msg->is_complete || !msg->mesg_line) {
+        return FALSE;
+    }
+
+    /*
+     *  There might be a couple of leading spaces. Not allowed,
+     *  but still be friendly
+     */
+    while ((*loc == ' ') && (*loc != '\0')) {
+        loc++;
+    }
+
+    if (strncmp(loc, schema, schema_len)) {
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
+
+boolean
+httpish_msg_is_complete (httpishMsg_t *msg)
+{
+    return msg->is_complete;
+}
+
+
+hStatus_t
+httpish_msg_add_reqline (httpishMsg_t *msg,
+                         const char *method,
+                         const char *url,
+                         const char *version)
+{
+
+    uint32_t linesize = 0;
+
+    if (!msg || !method || !url || !version) {
+        return HSTATUS_FAILURE;
+    }
+
+    if (msg->mesg_line) {
+        cpr_free(msg->mesg_line);
+    }
+
+    linesize = strlen(method) + 1 + strlen(url) + 1 + strlen(version) + 1;
+
+    msg->mesg_line = (char *) cpr_malloc(linesize * sizeof(char));
+    if (!msg->mesg_line) {
+        return HSTATUS_FAILURE;
+    }
+
+    snprintf(msg->mesg_line, linesize, "%s %s %s", method, url, version);
+
+    return HSTATUS_SUCCESS;
+}
+
+hStatus_t
+httpish_msg_add_respline (httpishMsg_t *msg,
+                          const char *version,
+                          uint16_t status_code,
+                          const char *reason_phrase)
+{
+    uint32_t linesize = 0;
+
+    if (!msg || !reason_phrase || !version ||
+        (status_code < HTTPISH_MIN_STATUS_CODE)) {
+        return HSTATUS_FAILURE;
+    }
+
+    if (msg->mesg_line) {
+        cpr_free(msg->mesg_line);
+    }
+
+    /* Assumes status codes are max 6 characters long */
+    linesize = strlen(version) + 1 + 6 + 1 + strlen(reason_phrase) + 1;
+
+    msg->mesg_line = (char *) cpr_malloc(linesize * sizeof(char));
+    if (!msg->mesg_line) {
+        return HSTATUS_FAILURE;
+    }
+
+    snprintf(msg->mesg_line, linesize, "%s %d %s",
+             version, status_code, reason_phrase);
+
+    return HSTATUS_SUCCESS;
+}
+
+
+httpishReqLine_t *
+httpish_msg_get_reqline (httpishMsg_t *msg)
+{
+    char *this_token;
+    char *msgline;
+    httpishReqLine_t *hreq = NULL;
+    char *strtok_state;
+
+    if (!msg || !msg->mesg_line || !(msgline = cpr_strdup(msg->mesg_line))) {
+        return NULL;
+    }
+
+    hreq = (httpishReqLine_t *) cpr_malloc(sizeof(httpishReqLine_t));
+    if (!hreq) {
+        cpr_free(msgline);
+        return NULL;
+    }
+
+    this_token = PL_strtok_r(msgline, " ", &strtok_state);
+
+    if (!this_token) {
+        cpr_free(hreq);
+        cpr_free(msgline);
+        return NULL;
+    }
+
+    hreq->method = cpr_strdup(this_token);
+
+    this_token = PL_strtok_r(NULL, " ", &strtok_state);
+
+    if (!this_token) {
+        cpr_free(hreq->method);
+        cpr_free(hreq);
+        cpr_free(msgline);
+        return NULL;
+    }
+
+    hreq->url = cpr_strdup(this_token);
+
+    this_token = PL_strtok_r(NULL, " ", &strtok_state);
+
+    if (!this_token) {
+        cpr_free(hreq->method);
+        cpr_free(hreq->url);
+        cpr_free(hreq);
+        cpr_free(msgline);
+        return NULL;
+    }
+
+    hreq->version = cpr_strdup(this_token);
+    cpr_free(msgline);
+    return hreq;
+}
+
+
+httpishRespLine_t *
+httpish_msg_get_respline (httpishMsg_t *msg)
+{
+    char *this_token;
+    char *msgline;
+    httpishRespLine_t *hrsp = NULL;
+    char *strtok_state;
+    unsigned long strtoul_result;
+    char *strtoul_end;
+
+    if (!msg || !msg->mesg_line) {
+        return NULL;
+    }
+
+    msgline = cpr_strdup(msg->mesg_line);
+    if (!msgline) {
+        return NULL;
+    }
+
+    hrsp = (httpishRespLine_t *) cpr_malloc(sizeof(httpishRespLine_t));
+
+    if (!hrsp) {
+        cpr_free(msgline);
+        return NULL;
+    }
+
+    this_token = PL_strtok_r(msgline, " ", &strtok_state);
+
+    if (!this_token) {
+        cpr_free(hrsp);
+        cpr_free(msgline);
+        return NULL;
+    }
+
+    hrsp->version = cpr_strdup(this_token);
+
+    this_token = PL_strtok_r(NULL, " ", &strtok_state);
+
+    if (!this_token) {
+        cpr_free(hrsp->version);
+        cpr_free(hrsp);
+        cpr_free(msgline);
+        return NULL;
+    }
+
+    errno = 0;
+    strtoul_result = strtoul(this_token, &strtoul_end, 10);
+
+    if (errno || this_token == strtoul_end || strtoul_result > USHRT_MAX) {
+        cpr_free(hrsp->version);
+        cpr_free(hrsp);
+        cpr_free(msgline);
+        return NULL;
+    }
+
+    hrsp->status_code = (uint16_t) strtoul_result;
+
+    this_token = PL_strtok_r(NULL, " ", &strtok_state);
+
+    /* reason phrase is optional */
+    if (this_token) {
+        hrsp->reason_phrase = cpr_strdup(this_token);
+    } else {
+        hrsp->reason_phrase = NULL;
+    }
+
+    cpr_free(msgline);
+    return (hrsp);
+}
+
+
+
+void
+httpish_msg_free_reqline (httpishReqLine_t *rqline)
+{
+    if (!rqline) {
+        return;
+    }
+
+    UTILFREE(rqline->method);
+    UTILFREE(rqline->url);
+    UTILFREE(rqline->version);
+}
+
+
+void
+httpish_msg_free_respline (httpishRespLine_t *rspline)
+{
+    if (!rspline) {
+        return;
+    }
+
+    UTILFREE(rspline->reason_phrase);
+    UTILFREE(rspline->version);
+}
+
+hStatus_t
+httpish_msg_add_text_header (httpishMsg_t *msg,
+                             const char *hname,
+                             const char *hval)
+{
+    uint32_t        linesize = 0;
+    httpish_header *this_header = NULL;
+    char           *header_line = NULL;
+
+    if (!msg || !hname || !hval) {
+        return HSTATUS_FAILURE;
+    }
+
+    linesize = strlen(hname) + 2 + strlen(hval) + 1;
+
+    header_line = (char *) cpr_malloc(linesize * sizeof(char));
+    if (!header_line)
+        return HSTATUS_FAILURE;
+
+    this_header = (httpish_header *) cpr_malloc(sizeof(httpish_header));
+    if (!this_header) {
+        cpr_free(header_line);
+        return HSTATUS_FAILURE;
+    }
+
+    snprintf(header_line, linesize, "%s: %s", hname, hval);
+
+    this_header->header = header_line;
+    this_header->next = NULL;
+
+    enqueue(msg->headers, (void *) this_header);
+
+    return HSTATUS_SUCCESS;
+}
+
+
+hStatus_t
+httpish_msg_add_int_header (httpishMsg_t *msg,
+                            const char *hname,
+                            int32_t hval)
+{
+    uint32_t        linesize = 0;
+    char           *header_line = NULL;
+    httpish_header *this_header = NULL;
+
+    if (!msg || !hname) {
+        return HSTATUS_FAILURE;
+    }
+
+    /* Assumes the int is less than 10 characters */
+    linesize = strlen(hname) + 2 + 10 + 1;
+
+    header_line = (char *) cpr_malloc(linesize * sizeof(char));
+    if (!header_line) {
+        return HSTATUS_FAILURE;
+    }
+
+    this_header = (httpish_header *) cpr_malloc(sizeof(httpish_header));
+    if (!this_header) {
+        cpr_free(header_line);
+        return HSTATUS_FAILURE;
+    }
+
+    snprintf(header_line, linesize, "%s: %d", hname, hval);
+
+    this_header->header = header_line;
+    this_header->next = NULL;
+
+    enqueue(msg->headers, (void *) this_header);
+
+    return HSTATUS_SUCCESS;
+}
+
+const char *
+httpish_msg_get_cached_header_val (httpishMsg_t *msg,
+                                   int cache_index)
+{
+    return msg->hdr_cache[cache_index].val_start;
+}
+
+int
+compact_hdr_cmp (char *this_line,
+                 const char *c_hname)
+{
+    char cmpct_hdr[CMPC_HEADER_SIZE];
+
+    if (c_hname) {
+        sstrncpy(cmpct_hdr, c_hname, CMPC_HEADER_SIZE);
+        return cpr_strcasecmp(this_line, cmpct_hdr);
+    }
+    return -1;
+}
+
+int
+httpish_header_name_val (char *sipHeaderName,  char *this_line)
+{
+    unsigned int x = 0;
+    boolean  nameFound = FALSE;
+
+    if (!sipHeaderName || !this_line) {
+       return (SIP_ERROR);
+    }
+
+    sipHeaderName[0] = '\0';
+
+    /* Remove the leading white spaces  eg: ......From:  or .....From....: */
+    while ((*this_line==' ' || *this_line=='\t') ) {
+        this_line++;
+    }
+
+     /* Copy the allowed characters for header field name */
+    while ((*this_line > 32) && (*this_line < 127) && (x < HTTPISH_HEADER_NAME_SIZE)) {
+        if (*this_line == ':') {
+            nameFound = TRUE;
+            sipHeaderName[x] = '\0';
+            break;
+        }
+        sipHeaderName[x] = *this_line;
+        this_line++;
+        x++;
+    }
+
+    /* Remove trailing white spaces */
+     if (nameFound == FALSE && x < HTTPISH_HEADER_NAME_SIZE) {
+         while ((*this_line == ' ' || *this_line=='\t') ){
+             this_line++;
+             if (*this_line == ':') {
+                 nameFound = TRUE;
+                 sipHeaderName[x] = '\0';
+                 break;
+             }
+         }
+     }
+     sipHeaderName[HTTPISH_HEADER_NAME_SIZE-1] = '\0';
+
+     if (nameFound) {
+         return (SIP_OK);
+     } else {
+         return (SIP_ERROR);
+     }
+}
+
+boolean
+httpish_msg_header_present (httpishMsg_t *msg,
+                            const char *hname)
+{
+    nexthelper *p;
+    char       *this_line = NULL;
+
+    /*
+     * To allow case-insensitive compact headers, we need to compare 2
+     * characters before we can decide, what the header name is.
+     * e.g. Call-ID and Content-Type both start with C.
+     * For now assume there is no white space between header name and ":"
+     */
+
+    if (!msg || !hname || (msg->headers->count == 0)) {
+        return FALSE;
+    }
+
+    p = (nexthelper *) msg->headers->qhead;
+    while (p) {
+        this_line = ((httpish_header *)p)->header;
+        if (this_line) {
+            /* Remove leading spaces */
+            while ((*this_line == ' ') && (*this_line != '\0'))
+                this_line++;
+            if ((strlen(this_line) >= strlen(hname)) &&
+                (cpr_strncasecmp(this_line, hname, strlen(hname))) == 0) {
+                return TRUE;
+            }
+        }
+        p = p->next;
+    }
+
+    return FALSE;
+}
+const char *
+httpish_msg_get_header_val (httpishMsg_t *msg,
+                            const char *hname,
+                            const char *c_hname)
+{
+    static const char fname[] = "httpish_msg_get_header_val";
+    nexthelper *p;
+    char       *this_line = NULL;
+    char       headerName[HTTPISH_HEADER_NAME_SIZE];
+
+    headerName[0] = '\0';
+
+    /*
+     * To allow case-insensitive compact headers, we need to compare 2
+     * characters before we can decide, what the header name is.
+     * e.g. Call-ID and Content-Type both start with C.
+     * For now assume there is no white space between header name and ":"
+     */
+
+    if (!msg || !hname || (msg->headers->count == 0)) {
+        return NULL;
+    }
+
+    p = (nexthelper *) msg->headers->qhead;
+    while (p) {
+        this_line = ((httpish_header *)p)->header;
+
+        if (httpish_header_name_val(headerName, this_line)) {
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Invalid Header Passed %s\n", DEB_F_PREFIX_ARGS(HTTPISH, fname), this_line);
+            return (NULL);
+        }
+
+        if (this_line) {
+            if ((cpr_strcasecmp(headerName, hname) == 0 ||
+                 compact_hdr_cmp(headerName, c_hname) == 0)) {
+                this_line = strchr(this_line, ':');
+                if (this_line) {
+                    this_line++;
+                    /* Remove leading spaces */
+                    while ((*this_line == ' ') && (*this_line != '\0'))
+                        this_line++;
+                    if (*this_line == '\0')
+                        return (NULL);
+                    else
+                        return ((const char *) this_line);
+                }
+            }
+        }
+        p = p->next;
+    }
+    return NULL;
+}
+
+int32_t
+httpish_msg_get_content_length (httpishMsg_t *msg)
+{
+    return msg->content_length;
+}
+
+static boolean
+httpish_msg_to_wstream (pmhWstream_t *ws,
+                        httpishMsg_t *msg)
+{
+    nexthelper *p;
+    char        tmp_body_buf[TMP_BODY_BUF_SIZE];
+    int         buf_len, total_length = 0, i, boundary_size;
+
+    if (!pmhutils_wstream_write_line(ws, msg->mesg_line)) {
+        return (FALSE);
+    }
+
+    p = (nexthelper *) msg->headers->qhead;
+    while (p) {
+        if (!pmhutils_wstream_write_line(ws,
+                (char *) (((httpish_header *)p)->header))) {
+            return (FALSE);
+        }
+        p = p->next;
+    }
+    if (msg->num_body_parts > 0) {
+        if (msg->num_body_parts > 1) {
+            // Write out the special Content-Type header and the
+            // Mime-Version header and the aggregate Content-Length header
+            // followed by the unique boundary
+            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE,
+                               "%s: multipart/mixed; boundary=%s\r\n",
+                               HTTPISH_HEADER_CONTENT_TYPE, uniqueBoundary);
+
+            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
+                return (FALSE);
+            }
+
+            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "%s: 1.0\r\n",
+                               HTTPISH_HEADER_MIME_VERSION);
+            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
+                return (FALSE);
+            }
+
+            // Traverse the list and calculate the total size of the
+            // body
+            boundary_size = strlen("\r\n--\r\n") + strlen(uniqueBoundary);
+            for (i = 0; i < msg->num_body_parts; i++) {
+                total_length += boundary_size;
+                total_length += msg->mesg_body[i].msgLength;
+                total_length += sizeof(HTTPISH_HEADER_CONTENT_TYPE) + 1;
+                switch (msg->mesg_body[i].msgContentTypeValue) {
+                default:
+                case SIP_CONTENT_TYPE_UNKNOWN_VALUE:
+                    total_length += strlen(msg->mesg_body[i].msgContentType) + 2;
+                    break;
+                case SIP_CONTENT_TYPE_SDP_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_SDP) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_SIPFRAG_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_SIPFRAG) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_DIALOG_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_DIALOG) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_KPML_REQUEST_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_KPML_REQUEST) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_KPML_RESPONSE_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_KPML_RESPONSE) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_REMOTECC_REQUEST_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_REMOTECC_REQUEST) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_REMOTECC_RESPONSE_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_REMOTECC_RESPONSE) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_CTI_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_CTI) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_CMXML_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_CMXML) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_TEXT_PLAIN_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_TEXT_PLAIN) + 1;
+                    break;
+                case SIP_CONTENT_TYPE_PRESENCE_VALUE:
+                    total_length += sizeof(SIP_CONTENT_TYPE_PRESENCE) + 1;
+                    break;
+                }
+                // Now estimate size of Content-Disposition header
+                total_length += sizeof(SIP_HEADER_CONTENT_DISP) + 1;
+                switch (msg->mesg_body[i].msgContentDisp) {
+                case SIP_CONTENT_DISPOSITION_RENDER_VALUE:
+                    total_length += sizeof(SIP_CONTENT_DISPOSITION_RENDER) - 1;
+                    break;
+                case SIP_CONTENT_DISPOSITION_SESSION_VALUE:
+                default:
+                    total_length += sizeof(SIP_CONTENT_DISPOSITION_SESSION) - 1;
+                    break;
+                case SIP_CONTENT_DISPOSITION_ICON_VALUE:
+                    total_length += sizeof(SIP_CONTENT_DISPOSITION_ICON) - 1;
+                    break;
+                case SIP_CONTENT_DISPOSITION_ALERT_VALUE:
+                    total_length += sizeof(SIP_CONTENT_DISPOSITION_ALERT) - 1;
+                    break;
+                case SIP_CONTENT_DISPOSITION_PRECONDITION_VALUE:
+                    total_length += sizeof(SIP_CONTENT_DISPOSITION_PRECONDITION) - 1;
+                    break;
+                }
+                // Now the handling attribute
+                total_length += sizeof(";handling=") - 1;
+                if (msg->mesg_body[i].msgRequiredHandling) {
+                    total_length += sizeof("required") + 1;
+                } else {
+                    total_length += sizeof("optional") + 1;
+                }
+                // Now the content id
+                if (msg->mesg_body[i].msgContentId) {
+                    total_length += sizeof(HTTPISH_HEADER_CONTENT_ID) + 1 +
+                        strlen(msg->mesg_body[i].msgContentId) + 2;
+
+                }
+            }
+            // Now account for the closing boundary which is 2+boundary_size
+            total_length += 2 + boundary_size + 2;
+
+            // Now write it out
+            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "%s: %d\r\n",
+                               HTTPISH_HEADER_CONTENT_LENGTH, total_length);
+
+            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
+                return (FALSE);
+            }
+
+            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "\r\n--%s\r\n", uniqueBoundary);
+
+            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
+                return (FALSE);
+            }
+
+        } else {
+            // Write out Content-Length for the first body
+            total_length = msg->mesg_body[0].msgLength;
+            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "%s: %d\r\n",
+                               HTTPISH_HEADER_CONTENT_LENGTH, total_length);
+
+            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
+                return (FALSE);
+            }
+        }
+        for (i = 0; i < msg->num_body_parts; i++) {
+            if (i > 0) {
+                // If there is another body to come, write the unique boundary
+                buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "\r\n--%s\r\n", uniqueBoundary);
+                if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
+                    return (FALSE);
+                }
+            }
+            snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "Content-Type: %s\r\n",
+                    msg->mesg_body[i].msgContentType);
+            sstrncat(tmp_body_buf, "Content-Disposition: ",
+                    sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+
+            switch (msg->mesg_body[i].msgContentDisp) {
+            case SIP_CONTENT_DISPOSITION_RENDER_VALUE:
+                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_RENDER,
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+                break;
+            case SIP_CONTENT_DISPOSITION_SESSION_VALUE:
+            default:
+                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_SESSION,
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+                break;
+            case SIP_CONTENT_DISPOSITION_ICON_VALUE:
+                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_ICON,
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+                break;
+            case SIP_CONTENT_DISPOSITION_ALERT_VALUE:
+                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_ALERT,
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+                break;
+            case SIP_CONTENT_DISPOSITION_PRECONDITION_VALUE:
+                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_PRECONDITION,
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+                break;
+            }
+            if (msg->mesg_body[i].msgRequiredHandling) {
+                sstrncat(tmp_body_buf, ";handling=required\r\n",
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+            } else {
+                sstrncat(tmp_body_buf, ";handling=optional\r\n",
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+            }
+            if (msg->mesg_body[i].msgContentId) {
+                sstrncat(tmp_body_buf, "Content-Id: ",
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+                sstrncat(tmp_body_buf, msg->mesg_body[i].msgContentId,
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+                sstrncat(tmp_body_buf, "\r\n",
+                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+            }
+            sstrncat(tmp_body_buf, "\r\n",
+                     sizeof(tmp_body_buf) - strlen(tmp_body_buf));
+            buf_len = strlen(tmp_body_buf);
+            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
+                return (FALSE);
+            }
+
+            // Now write the body
+            if (!pmhutils_wstream_write_bytes(ws, msg->mesg_body[i].msgBody,
+                                              msg->mesg_body[i].msgLength)) {
+                return (FALSE);
+            }
+        }
+        // After writing out the last body part, write out the last unique
+        // boundary line
+        if (msg->num_body_parts > 1) {
+            snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "\r\n--%s--\r\n", uniqueBoundary);
+            buf_len = strlen(tmp_body_buf);
+            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
+                return (FALSE);
+            }
+        }
+    } else {
+        if (!pmhutils_wstream_write_byte(ws, '\r')) {
+            return (FALSE);
+        }
+        if (!pmhutils_wstream_write_byte(ws, '\n')) {
+            return (FALSE);
+        }
+    }
+
+    return (TRUE);
+}
+
+hStatus_t
+httpish_msg_write (httpishMsg_t *msg,
+                   char *buf,
+                   uint32_t *nbytes)
+{
+    pmhWstream_t *ws = NULL;
+
+    ws = pmhutils_wstream_create_with_buf(buf, *nbytes);
+    if (!ws) {
+        return (HSTATUS_FAILURE);
+    }
+
+    if (!httpish_msg_to_wstream(ws, msg)) {
+        pmhutils_wstream_delete(ws, FALSE);
+        cpr_free(ws);
+        return (HSTATUS_FAILURE);
+    }
+
+    *nbytes = pmhutils_wstream_get_length(ws);
+    pmhutils_wstream_delete(ws, FALSE);
+    cpr_free(ws);
+    return HSTATUS_SUCCESS;
+}
+
+int
+httpish_cache_header_val (httpishMsg_t *hmsg,
+                          char *this_line)
+{
+    static const char fname[] = "httpish_cache_header_val";
+    char *hdr_start;
+    httpish_cache_t *hdr_cache;
+    int i;
+    char headerName[HTTPISH_HEADER_NAME_SIZE];
+
+    headerName[0] = '\0';
+
+    hdr_cache = hmsg->hdr_cache;
+    hdr_start = this_line;
+
+    if (httpish_header_name_val(headerName, this_line)) {
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Invalid Header %s\n", DEB_F_PREFIX_ARGS(HTTPISH, fname), this_line);
+        return (SIP_ERROR);
+    }
+
+    for (i = 0; i < HTTPISH_HEADER_CACHE_SIZE; ++i) {
+        sip_header_t *tmp = sip_cached_headers + i;
+
+        if (cpr_strcasecmp(headerName, tmp->hname) == 0 ||
+            compact_hdr_cmp(headerName, tmp->c_hname) == 0) {
+            this_line = strchr(this_line, ':');
+            if (this_line) {
+                this_line++; /* Skip the ':' */
+
+                /* Remove leading spaces */
+                while (*this_line == ' ' || *this_line == '\t') {
+                    this_line++;
+                }
+                if (*this_line) {
+                    if (hdr_cache[i].hdr_start) {
+                        int org_len, offset;
+                        int size;
+                        char *newbuf;
+
+                        /* Multiple instances of a header, concatenate the
+                         * header values with a ','
+                         */
+                        org_len = strlen(hdr_cache[i].hdr_start);
+                        offset = hdr_cache[i].val_start - hdr_cache[i].hdr_start;
+                        size = org_len + 2 + strlen(this_line);
+                        newbuf = (char *) cpr_realloc(hdr_cache[i].hdr_start,
+                                                      size);
+                        if (newbuf == NULL) {
+                            cpr_free(hdr_cache[i].hdr_start);
+                            hdr_cache[i].hdr_start = NULL;
+                            break;
+                        }
+                        hdr_cache[i].hdr_start = newbuf;
+                        hdr_cache[i].val_start = hdr_cache[i].hdr_start + offset;
+                        hdr_cache[i].hdr_start[org_len] = ',';
+                        sstrncpy(hdr_cache[i].hdr_start + org_len + 1, this_line,
+                                size - org_len - 1);
+                        cpr_free(hdr_start);
+                    } else {
+                        hdr_cache[i].hdr_start = hdr_start;
+                        hdr_cache[i].val_start = this_line;
+                    }
+                } else { // this line is blank
+                    cpr_free(hdr_start);
+                }
+            } else { // this line does not have a ':'
+                cpr_free(hdr_start);
+            }
+            return 0;
+        }
+    }
+    return -1;
+}
+
+/*
+ * Return -1 for invalid/empty content-length value
+ * else return the content length
+ */
+int32_t
+get_content_length (httpishMsg_t *hmsg)
+{
+    int i;
+    const char *hdr_val;
+    long strtol_result;
+    char *strtol_end;
+
+    hdr_val = httpish_msg_get_cached_header_val(hmsg, CONTENT_LENGTH);
+    if (hdr_val == NULL) {
+        return -1;
+    }
+
+    for (i = 0; hdr_val[i]; ++i) {
+        if (!isdigit((int) hdr_val[i])) {
+            return -1;
+        }
+    }
+
+    /* If the string was empty then the content length is still invalid */
+    if (!i) {
+        return -1;
+    }
+
+    errno = 0;
+    strtol_result = strtol(hdr_val, &strtol_end, 10);
+
+    if (errno || hdr_val == strtol_end || strtol_result > INT_MAX) {
+        return -1;
+    } else {
+        return (int) strtol_result;
+    }
+}
+
+uint8_t
+get_content_type_value (const char *content_type)
+{
+    if (!content_type) {
+        return SIP_CONTENT_TYPE_UNKNOWN_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_SDP,
+                                sizeof(SIP_CONTENT_TYPE_SDP) - 1)) {
+        return SIP_CONTENT_TYPE_SDP_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_SIPFRAG,
+                                sizeof(SIP_CONTENT_TYPE_SIPFRAG) - 1)) {
+        return SIP_CONTENT_TYPE_SIPFRAG_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_TEXT_PLAIN,
+                                sizeof(SIP_CONTENT_TYPE_TEXT_PLAIN) - 1)) {
+        return SIP_CONTENT_TYPE_TEXT_PLAIN_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_SIP,
+                                sizeof(SIP_CONTENT_TYPE_SIP) - 1)) {
+        return SIP_CONTENT_TYPE_SIP_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MWI,
+                                sizeof(SIP_CONTENT_TYPE_MWI) - 1)) {
+        return SIP_CONTENT_TYPE_MWI_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MULTIPART_MIXED,
+                                sizeof(SIP_CONTENT_TYPE_MULTIPART_MIXED) - 1)) {
+        return SIP_CONTENT_TYPE_MULTIPART_MIXED_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE,
+                                sizeof(SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE) - 1)) {
+        return SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE_VALUE;
+    }
+    else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_DIALOG,
+                              sizeof(SIP_CONTENT_TYPE_DIALOG) - 1)) {
+        return SIP_CONTENT_TYPE_DIALOG_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_KPML_REQUEST,
+                                sizeof(SIP_CONTENT_TYPE_KPML_REQUEST) - 1)) {
+        return SIP_CONTENT_TYPE_KPML_REQUEST_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_KPML_RESPONSE,
+                                sizeof(SIP_CONTENT_TYPE_KPML_RESPONSE) - 1)) {
+        return SIP_CONTENT_TYPE_KPML_RESPONSE_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_REMOTECC_REQUEST,
+                                sizeof(SIP_CONTENT_TYPE_REMOTECC_REQUEST) - 1)) {
+        return SIP_CONTENT_TYPE_REMOTECC_REQUEST_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_CONFIGAPP,
+                                sizeof(SIP_CONTENT_TYPE_CONFIGAPP) - 1)) {
+        return SIP_CONTENT_TYPE_CONFIGAPP_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_REMOTECC_RESPONSE,
+                                sizeof(SIP_CONTENT_TYPE_REMOTECC_RESPONSE) - 1)) {
+        return SIP_CONTENT_TYPE_REMOTECC_RESPONSE_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_PRESENCE,
+                                sizeof(SIP_CONTENT_TYPE_PRESENCE) - 1)) {
+        return SIP_CONTENT_TYPE_PRESENCE_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_CMXML,
+                                sizeof(SIP_CONTENT_TYPE_CMXML) - 1)) {
+        return SIP_CONTENT_TYPE_CMXML_VALUE;
+    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_CTI,
+                                sizeof(SIP_CONTENT_TYPE_CTI) - 1)) {
+        return SIP_CONTENT_TYPE_CTI_VALUE;
+    }
+    return SIP_CONTENT_TYPE_UNKNOWN_VALUE;
+}
+
+/******************************************************************
+ * msg_process_one_body
+ * This function will process one of the body parts of the whole body
+ * Headers not understood will be ignored
+ * - msg_start should be pointing at the first header of the body part
+ * - msg_end at its last character
+ ******************************************************************/
+int
+msg_process_one_body (httpishMsg_t *hmsg,
+                      char *msg_start,
+                      char *msg_end,
+                      int current_body_part)
+{
+    static const char fname[] = "msg_process_one_body";
+    pmhRstream_t *rstream = NULL;
+    char         *content_type = NULL, *content_disp = NULL;
+    char         *line = NULL, *body = NULL;
+    char         *content_enc = NULL;
+    char         *content_id = NULL;  int nbytes = 0;
+    boolean       body_read = FALSE;
+
+    // Adjust msg_start to point to the first valid character
+    while (*msg_start == '\r' || *msg_start == '\n') {
+        msg_start++;
+    }
+
+    // Convert this chunk of data into a more parse-able format
+    rstream = pmhutils_rstream_create(msg_start, (uint32_t)(msg_end - msg_start));
+
+    while (!body_read) {
+        if (rstream) {
+            line = pmhutils_rstream_read_line(rstream);
+        }
+
+        if (line) {
+            // Look for the kind of line it is
+            if (!cpr_strncasecmp(line, HTTPISH_HEADER_CONTENT_TYPE,
+                                 sizeof(HTTPISH_HEADER_CONTENT_TYPE) - 1)) {
+                // Its Content-Type, read in the value
+                // XXX what if the line looks like "Content-Type-Garbage: some-value"?
+                content_type = line + sizeof(HTTPISH_HEADER_CONTENT_TYPE);
+                // XXX what if the line looks like "Content-Type :  some-value"?
+                while (*content_type == ' ') {
+                    content_type++;
+                }
+                hmsg->mesg_body[current_body_part].msgContentTypeValue = get_content_type_value(content_type);
+                nbytes = strlen(content_type) + 1;
+                hmsg->mesg_body[current_body_part].msgContentType =
+                             (char *) cpr_malloc((nbytes)*sizeof(char));
+                if (hmsg->mesg_body[current_body_part].msgContentType == NULL) {
+                   CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed\n", fname);
+                } else {
+                    memcpy(hmsg->mesg_body[current_body_part].msgContentType,
+                           content_type, nbytes);
+                }
+            } else if (!cpr_strncasecmp(line, HTTPISH_HEADER_CONTENT_ID,
+                                        sizeof(HTTPISH_HEADER_CONTENT_ID) - 1)) {
+                //Its Content-Id, read the value
+                content_id = line + sizeof(HTTPISH_HEADER_CONTENT_ID);
+                while(*content_id == ' ') {
+                    content_id++;
+                }
+                nbytes = strlen(content_id) + 1;
+                hmsg->mesg_body[current_body_part].msgContentId =
+                             (char *) cpr_malloc((nbytes)*sizeof(char));
+                if (hmsg->mesg_body[current_body_part].msgContentId == NULL) {
+                   CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed\n", fname);
+                }
+                memcpy(hmsg->mesg_body[current_body_part].msgContentId,
+                          content_id, nbytes);
+            } else if (!cpr_strncasecmp(line, SIP_HEADER_CONTENT_DISP,
+                                        sizeof(SIP_HEADER_CONTENT_DISP) - 1)) {
+                // Its Content-Disposition, read in the value
+                content_disp = line + sizeof(SIP_HEADER_CONTENT_DISP);
+                while (*content_disp == ' ') {
+                    content_disp++;
+                }
+                if (!cpr_strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_SESSION,
+                                     sizeof(SIP_CONTENT_DISPOSITION_SESSION) - 1)) {
+                    hmsg->mesg_body[current_body_part].msgContentDisp =
+                        SIP_CONTENT_DISPOSITION_SESSION_VALUE;
+                    content_disp += sizeof(SIP_CONTENT_DISPOSITION_SESSION) - 1;
+                } else {
+                    hmsg->mesg_body[current_body_part].msgContentDisp =
+                        SIP_CONTENT_DISPOSITION_UNKNOWN_VALUE;
+                    content_disp = strchr(line, ';');
+                }
+                if (content_disp && *content_disp == ';') {
+                    content_disp++;
+                    if (!cpr_strncasecmp(content_disp, "handling", 8)) {
+                        content_disp += 9;
+                        if (!cpr_strncasecmp(content_disp, "required", 8)) {
+                            hmsg->mesg_body[current_body_part].msgRequiredHandling = TRUE;
+                        } else if (!cpr_strncasecmp(content_disp, "optional", 8)) {
+                            hmsg->mesg_body[current_body_part].msgRequiredHandling = FALSE;
+                        }
+                    }
+                }
+            } else if (!cpr_strncasecmp(line, HTTPISH_HEADER_CONTENT_ENCODING,
+                                        sizeof(HTTPISH_HEADER_CONTENT_ENCODING) - 1)) {
+                content_enc = line + sizeof(HTTPISH_HEADER_CONTENT_ENCODING);
+                while (*content_enc == ' ') {
+                    content_enc++;
+                }
+                if (!cpr_strcasecmp(content_enc, SIP_CONTENT_ENCODING_IDENTITY)) {
+                    hmsg->mesg_body[current_body_part].msgContentEnc =
+                        SIP_CONTENT_ENCODING_IDENTITY_VALUE;
+                } else {
+                    hmsg->mesg_body[current_body_part].msgContentEnc =
+                        SIP_CONTENT_ENCODING_UNKNOWN_VALUE;
+                }
+            } else if (!(*line)) {
+                body_read = TRUE;
+                // The rest of the bytes are the body
+                hmsg->mesg_body[current_body_part].msgLength =
+                    rstream->nbytes - rstream->bytes_read;
+                body = pmhutils_rstream_read_bytes(rstream, rstream->nbytes - rstream->bytes_read);
+                if (body) {
+                    hmsg->mesg_body[current_body_part].msgBody = body;
+                }
+            } else {
+                // Unhandled header type
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unrecognized header in body\n", DEB_F_PREFIX_ARGS(HTTPISH, fname));
+            }
+            // Free the read line
+            cpr_free(line);
+            line = NULL;
+        } else {
+            body_read = TRUE;
+        }
+    }
+    // Free the created stream structure
+    pmhutils_rstream_delete(rstream, FALSE);
+    cpr_free(rstream);
+    return 0;
+}
+
+/******************************************************************
+ * msg_process_multiple_bodies
+ * This function will process multiple body parts of the whole body
+ * boundary should point to the delimiter string, raw body points to
+ * the beginning of the whole body structure in the message
+ *
+ * The algorithm here is to find the beginning and the end of each
+ * body part and send it off for further header and body extraction
+ ******************************************************************/
+int
+msg_process_multiple_bodies (httpishMsg_t *hmsg,
+                             char *boundary,
+                             char *raw_body)
+{
+    char    msg_delimit[MSG_DELIMIT_SIZE];
+    int     i, body_part = 0;
+    boolean end_of_proc = FALSE;
+    char   *msg_start, *msg_end;
+
+    // First copy the boundary in an array
+    msg_delimit[0] = '-';
+    msg_delimit[1] = '-';
+    for (i = 0; boundary[i] != '\r' && boundary[i] != '\n' &&
+         boundary[i] != ';' && boundary[i] != '\0'; i++) {
+         if (i + 2 >= MSG_DELIMIT_SIZE) {
+             return body_part;
+         }
+        msg_delimit[i + 2] = boundary[i];
+    }
+    msg_delimit[i + 2] = '\0';
+
+    // Loop through the body segments
+    while (!end_of_proc && body_part < HTTPISH_MAX_BODY_PARTS) {
+        msg_start = strstr(raw_body, msg_delimit);
+        if (msg_start) {
+            msg_start += strlen(msg_delimit) + 1;
+            msg_end = strstr(msg_start, msg_delimit);
+            if (msg_end) {
+                (void) msg_process_one_body(hmsg, msg_start, msg_end - 1,
+                                            body_part);
+            } else {
+                // No boundary found for message end, return error
+                end_of_proc = TRUE;
+                continue;
+            }
+        } else {
+            // No boundary found for message start, return error
+            end_of_proc = TRUE;
+            continue;
+        }
+        raw_body = msg_end;
+        // Check if this is the last boundary
+        if (*(raw_body + strlen(msg_delimit)) == '-') {
+            end_of_proc = TRUE;
+        }
+        body_part++;
+    }
+    return body_part;
+}
+
+hStatus_t
+httpish_msg_process_network_msg (httpishMsg_t *hmsg,
+                                 char *nmsg,
+                                 uint32_t *nbytes)
+{
+    static const char fname[] = "httpish_msg_process_network_msg";
+    pmhRstream_t *rs = NULL;
+    int32_t       bytes_remaining, delta;
+    char         *mline;
+    hStatus_t     retval;
+    char         *raw_body = NULL;
+    const char   *content_type = NULL;
+    const char   *content_id = NULL;
+    int32_t      contentid_len;
+    int32_t      contenttype_len;
+
+    if (!hmsg || !nmsg || (*nbytes <= 0)) {
+        return HSTATUS_FAILURE;
+    }
+
+    if (hmsg->is_complete == TRUE) {
+        *nbytes = 0;
+        return HSTATUS_SUCCESS;
+    }
+
+    if ((rs = pmhutils_rstream_create(nmsg, *nbytes)) == NULL)
+        return HSTATUS_FAILURE;
+
+    /* Try to read message line */
+    while (!hmsg->mesg_line) {
+        mline = pmhutils_rstream_read_line(rs);
+        if (!mline) {
+            *nbytes = rs->bytes_read;
+            if (rs->eof == TRUE) {
+                retval = HSTATUS_SUCCESS;
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Msg line read failure due to RS->EOF\n", fname);
+            } else {
+                retval = HSTATUS_FAILURE;
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Msg line read failure\n", fname);
+            }
+            pmhutils_rstream_delete(rs, FALSE);
+            cpr_free(rs);
+            return retval;
+        }
+        if (!(*mline)) {
+            cpr_free(mline);
+        } else {
+            hmsg->mesg_line = mline;
+        }
+    }
+
+    /* There is a message line. We could have a header or a message body */
+    while (!hmsg->headers_read) {
+        char *this_header;
+
+        this_header = pmhutils_rstream_read_line(rs);
+        if (!this_header) {
+            *nbytes = rs->bytes_read;
+            if (rs->eof == TRUE) {
+                retval = HSTATUS_SUCCESS;
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Header line read failure due to RS->EOF\n", DEB_F_PREFIX_ARGS(HTTPISH, fname));
+            } else {
+                retval = HSTATUS_FAILURE;
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Header line read failure\n", fname);
+            }
+            pmhutils_rstream_delete(rs, FALSE);
+            cpr_free(rs);
+            return retval;
+        }
+        if (!(*this_header)) {
+            cpr_free(this_header);
+            hmsg->headers_read = TRUE;
+        } else {
+            httpish_header *h;
+
+            if (httpish_cache_header_val(hmsg, this_header) == -1) {
+                /*
+                 * For a non-cacheable header or error in caching routine,
+                 * use the header linked list.
+                 */
+                h = (httpish_header *) cpr_malloc(sizeof(httpish_header));
+                if (!h) {
+                    *nbytes = rs->bytes_read;
+                    pmhutils_rstream_delete(rs, FALSE);
+                    cpr_free(rs);
+                    cpr_free(this_header);
+                    return HSTATUS_FAILURE;
+                }
+
+                h->next = NULL;
+                h->header = this_header;
+                enqueue(hmsg->headers, (void *)h);
+            }
+
+        }
+    }
+
+    /* Calculate the bytes remaining in the read stream */
+    bytes_remaining = rs->nbytes - rs->bytes_read;
+
+    /* Now get the content length header value */
+    hmsg->content_length = get_content_length(hmsg);
+    if (hmsg->content_length == -1) {
+        /* Bad or missing content-length header
+         * For UDP, assume remaining msg is message body.
+         * For TCP, message is not complete without content-length header
+         * but we will ignore this possibility for now (assume content-length will always be there)
+         * since we don't know if we have a complete message or a fragmented one
+         */
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Content-Length header not received\n", fname);
+        hmsg->content_length = bytes_remaining;
+    }
+
+    delta = bytes_remaining - hmsg->content_length;
+    if (delta) {
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX "Content Length %d, Bytes Remaining %d.\n", DEB_F_PREFIX_ARGS(HTTPISH, fname),
+                            hmsg->content_length, bytes_remaining);
+    }
+    if (delta < 0) {
+        /* We have fewer bytes than specified by Content-Length header */
+        hmsg->content_length = bytes_remaining;
+        hmsg->is_complete = FALSE;
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Partial body received\n", DEB_F_PREFIX_ARGS(HTTPISH, fname));
+    } else {
+        hmsg->is_complete = TRUE;
+    }
+
+    if (hmsg->content_length > 0) {
+        raw_body = pmhutils_rstream_read_bytes(rs, hmsg->content_length);
+        if (!raw_body) {
+            pmhutils_rstream_delete(rs, FALSE);
+            cpr_free(rs);
+            return HSTATUS_FAILURE;
+        }
+    }
+
+    // If message is not complete we should not parse the message any more
+    if (!hmsg->is_complete) {
+        *nbytes = rs->bytes_read;
+        pmhutils_rstream_delete(rs, FALSE);
+        cpr_free(rs);
+        UTILFREE(raw_body);
+        return HSTATUS_SUCCESS;
+    }
+
+    // Figure out whether more parsing of the received body is necessary
+    content_type = httpish_msg_get_cached_header_val(hmsg, CONTENT_TYPE);
+    if (content_type && (hmsg->content_length > 0)) {
+        if (!cpr_strncasecmp(content_type, "multipart/mixed", 15)) {
+
+            char *boundary = NULL;
+            int num_bodies = 0;
+
+            // find the boundary tag
+            boundary = strchr(content_type, '=');
+            if (boundary) {
+                boundary++;
+                if (raw_body) {
+                    num_bodies = msg_process_multiple_bodies(hmsg, boundary, raw_body);
+                }
+
+                if (num_bodies == 0) {
+                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in decoding multipart messages\n", fname);
+                } else {
+                    hmsg->num_body_parts = (uint8_t) num_bodies;
+                }
+            } else {
+                // No boundary specified!
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in decoding multipart messages: No body delimiter\n", fname);
+            }
+            hmsg->raw_body = raw_body;
+
+        } else {
+            // All of the body is of a single type
+            // hmsg->mesg_body[0].msgBody = raw_body;
+            hmsg->mesg_body[0].msgBody = (char *)
+                cpr_malloc(hmsg->content_length + 1);
+            if (hmsg->mesg_body[0].msgBody == NULL) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
+                pmhutils_rstream_delete(rs, FALSE);
+                cpr_free(rs);
+                cpr_free(raw_body);
+                return HSTATUS_FAILURE;
+            }
+
+            if (raw_body) {
+                memcpy(hmsg->mesg_body[0].msgBody, raw_body,
+                       hmsg->content_length + 1);
+            }
+
+            content_id = httpish_msg_get_header_val(hmsg, HTTPISH_HEADER_CONTENT_ID, NULL);
+            if (content_id) {
+                contentid_len = strlen(content_id) + 1;
+                hmsg->mesg_body[0].msgContentId = (char *)
+                       cpr_malloc(contentid_len * sizeof(char));
+                if (hmsg->mesg_body[0].msgContentId == NULL) {
+                   CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
+                    pmhutils_rstream_delete(rs, FALSE);
+                    cpr_free(rs);
+                    cpr_free(raw_body);
+                    return HSTATUS_FAILURE;
+                }
+               memcpy(hmsg->mesg_body[0].msgContentId,
+                       content_id, contentid_len);
+            }
+
+            hmsg->mesg_body[0].msgContentTypeValue = get_content_type_value(content_type);
+            contenttype_len = strlen(content_type) + 1;
+            hmsg->mesg_body[0].msgContentType = (char *)
+                   cpr_malloc(contenttype_len * sizeof(char));
+            if (hmsg->mesg_body[0].msgContentType == NULL) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
+                pmhutils_rstream_delete(rs, FALSE);
+                cpr_free(rs);
+                cpr_free(raw_body);
+                return HSTATUS_FAILURE;
+            }
+            memcpy(hmsg->mesg_body[0].msgContentType,
+                   content_type, contenttype_len);
+
+            hmsg->mesg_body[0].msgContentDisp =
+                SIP_CONTENT_DISPOSITION_SESSION_VALUE;
+            hmsg->mesg_body[0].msgRequiredHandling = TRUE;
+            hmsg->mesg_body[0].msgLength = hmsg->content_length;
+            hmsg->num_body_parts = 1;
+            hmsg->raw_body = raw_body;
+        }
+    } else if (hmsg->content_length > 0) {
+        // No content-type specified but there is a body present. Treat this
+        // as an error
+        hmsg->is_complete = FALSE;
+        hmsg->content_length = -1;
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Body found without content-type\n", fname);
+        UTILFREE(raw_body);
+        pmhutils_rstream_delete(rs, FALSE);
+        cpr_free(rs);
+        return HSTATUS_SUCCESS;
+    }
+
+    *nbytes = rs->bytes_read;
+    pmhutils_rstream_delete(rs, FALSE);
+    cpr_free(rs);
+    return HSTATUS_SUCCESS;
+}
+
+
+hStatus_t
+httpish_msg_add_body (httpishMsg_t *msg,
+                      char *body,
+                      uint32_t nbytes,
+                      const char *content_type,
+                      uint8_t msg_disposition,
+                      boolean required,
+                      char *content_id)
+{
+    static const char fname[] = "httpish_msg_add_body";
+    uint8_t current_body_part;
+    uint32_t contenttype_len;
+
+    if (!msg || !body || (nbytes == 0)) {
+        return HSTATUS_FAILURE;
+    }
+
+    if (msg->num_body_parts == HTTPISH_MAX_BODY_PARTS) {
+        return HSTATUS_FAILURE;
+    }
+
+    // Add body at the next available index
+    current_body_part = msg->num_body_parts;
+
+    contenttype_len = strlen(content_type) + 1;
+    msg->mesg_body[current_body_part].msgContentType = (char *)
+            cpr_malloc(contenttype_len * sizeof(char));
+    if (msg->mesg_body[current_body_part].msgContentType == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
+        return HSTATUS_FAILURE;
+    }
+
+    msg->mesg_body[current_body_part].msgBody = body;
+    memcpy(msg->mesg_body[current_body_part].msgContentType,
+           content_type, contenttype_len);
+    msg->mesg_body[current_body_part].msgContentTypeValue = get_content_type_value(content_type);
+    msg->mesg_body[current_body_part].msgContentDisp = msg_disposition;
+    msg->mesg_body[current_body_part].msgRequiredHandling = required;
+    msg->mesg_body[current_body_part].msgLength = nbytes;
+    msg->mesg_body[current_body_part].msgContentId = content_id;
+
+    msg->num_body_parts++;
+
+    return HSTATUS_SUCCESS;
+}
+
+
+httpishStatusCodeClass_t
+httpish_msg_get_code_class (uint16_t statusCode)
+{
+    httpishStatusCodeClass_t retval;
+    int fc = statusCode / 100;
+
+    switch (fc) {
+    case 1:
+        retval = codeClass1xx;
+        break;
+    case 2:
+        retval = codeClass2xx;
+        break;
+    case 3:
+        retval = codeClass3xx;
+        break;
+    case 4:
+        retval = codeClass4xx;
+        break;
+    case 5:
+        retval = codeClass5xx;
+        break;
+    case 6:
+        retval = codeClass6xx;
+        break;
+    default:
+        retval = codeClassInvalid;
+        break;
+    }
+
+    return retval;
+}
+
+
+uint16_t
+httpish_msg_get_num_particular_headers (httpishMsg_t *msg,
+                                        const char *hname,
+                                        const char *c_hname,
+                                        char *header_val[],
+                                        uint16_t max_headers)
+{
+    nexthelper *p;
+    char       *this_line = NULL;
+    uint16_t    found = 0;
+
+    if (!msg || !hname) {
+        return 0;
+    }
+
+    p = (nexthelper *) msg->headers->qhead;
+
+    while (p && found < max_headers) {
+        this_line = ((httpish_header *)p)->header;
+        if (this_line) {
+            /* Remove leading spaces */
+            while ((*this_line == ' ') && (*this_line != '\0'))
+                this_line++;
+            if ((strlen(this_line) > strlen(hname) + 1) &&
+                (cpr_strncasecmp(this_line, hname, strlen(hname)) == 0 ||
+                 compact_hdr_cmp(this_line, c_hname) == 0)) {
+                this_line = strchr(this_line, ':');
+                if (this_line) {
+                    this_line++;
+                    /* Remove leading spaces */
+                    while ((*this_line == ' ') && (*this_line != '\0'))
+                        this_line++;
+                    if (*this_line == '\0') {
+                        p = p->next;
+                        continue;
+                    } else {
+                        header_val[found] = this_line;
+                        found++;
+                    }
+                }
+            }
+        }
+        p = p->next;
+    }
+    return found;
+}
+
+
diff --git a/libs/sipcc/core/sipstack/pmhutils.c b/libs/sipcc/core/sipstack/pmhutils.c
new file mode 100644 (file)
index 0000000..57c83d7
--- /dev/null
@@ -0,0 +1,308 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "pmhutils.h"
+#include "phone.h"
+
+
+#define WSTREAM_START_SIZE         1024
+
+pmhRstream_t *
+pmhutils_rstream_create (char *buf, uint32_t nbytes)
+{
+    pmhRstream_t *pmhRstream;
+
+    if (!buf || (nbytes == 0)) {
+        return NULL;
+    }
+
+    pmhRstream = (pmhRstream_t *) cpr_malloc(sizeof(pmhRstream_t));
+    if (!pmhRstream) {
+        return NULL;
+    }
+
+    pmhRstream->eof = pmhRstream->error = FALSE;
+    pmhRstream->buff = pmhRstream->loc = buf;
+    pmhRstream->nbytes = nbytes;
+    pmhRstream->bytes_read = 0;
+
+    return pmhRstream;
+}
+
+void
+pmhutils_rstream_delete (pmhRstream_t *pmhRstream, boolean freebuf)
+{
+    if (!pmhRstream) {
+        return;
+    }
+
+    pmhRstream->error = TRUE;
+
+    if (freebuf && pmhRstream->buff) {
+        cpr_free(pmhRstream->buff);
+    }
+}
+
+char *
+pmhutils_rstream_read_bytes (pmhRstream_t *pmhRstream, int32_t nbytes)
+{
+    char *ret;
+
+    if (!pmhRstream || !pmhRstream->loc || (pmhRstream->eof == TRUE)) {
+        return NULL;
+    }
+
+    if (pmhRstream->bytes_read >= pmhRstream->nbytes) {
+        pmhRstream->eof = TRUE;
+        return NULL;
+    }
+
+    if ((pmhRstream->nbytes - pmhRstream->bytes_read) < (int32_t) nbytes) {
+        return NULL;
+    }
+    ret = (char *) cpr_malloc((nbytes + 1) * sizeof(char));
+    if (ret == NULL) {
+        // Addition of 1 byte (NULL) will save us from strlen errr
+        return NULL;
+    }
+    memcpy(ret, pmhRstream->loc, nbytes);
+    ret[nbytes] = 0;              /* ensure null terminating character */
+    pmhRstream->bytes_read += nbytes;
+    pmhRstream->loc += nbytes;
+
+    return (ret);
+}
+
+
+#define SANITY_LINE_SIZE PKTBUF_SIZ
+
+unsigned short linesize = 80;
+unsigned short incr_size = 32;
+
+char *
+pmhutils_rstream_read_line (pmhRstream_t *pmhRstream)
+{
+    char *ret_line;
+    char *new_loc = NULL;
+    int offset, bytes_allocated;
+    boolean line_break;
+
+    if (!pmhRstream || !pmhRstream->loc || (pmhRstream->eof == TRUE)) {
+        return NULL;
+    }
+
+    if (pmhRstream->bytes_read >= pmhRstream->nbytes) {
+        pmhRstream->eof = TRUE;
+        return NULL;
+    }
+
+    new_loc = pmhRstream->loc;
+
+    ret_line = (char *) cpr_malloc(linesize);
+    if (ret_line == NULL) {
+        return NULL;
+    }
+    offset = 0;
+    bytes_allocated = linesize;
+    /* Search for the first occurrence of a line break */
+    while (1) {
+        line_break = FALSE;
+        if (*new_loc == '\r') {
+            line_break = TRUE;
+            new_loc++;
+        }
+        if (*new_loc == '\n') {
+            line_break = TRUE;
+            new_loc++;
+        }
+        if (line_break) {
+            if (*new_loc != ' ' && *new_loc != '\t') {
+                break;
+            }
+        }
+
+        if (offset == (bytes_allocated - 1)) {
+            char *newbuf;
+
+            bytes_allocated += incr_size;
+            newbuf = (char *) cpr_realloc(ret_line, bytes_allocated);
+            if (newbuf == NULL) {
+                if (bytes_allocated != 0) {
+                    cpr_free(ret_line);
+                }
+                return NULL;
+            }
+            ret_line = newbuf;
+        }
+        ret_line[offset++] = *new_loc++;
+        if (*new_loc == '\0') {
+            /* We hit the end of buffer before hitting a line break */
+            pmhRstream->eof = TRUE;
+            pmhRstream->bytes_read += (new_loc - pmhRstream->loc);
+            pmhRstream->loc = new_loc;
+            cpr_free(ret_line);
+            return NULL;
+        }
+    }
+
+    pmhRstream->bytes_read += (new_loc - pmhRstream->loc);
+    /* Update the internal location pointer and bytes read */
+
+    if (pmhRstream->bytes_read >= pmhRstream->nbytes) {
+        pmhRstream->loc = pmhRstream->buff + pmhRstream->nbytes;
+        pmhRstream->eof = TRUE;
+    } else {
+        pmhRstream->loc = pmhRstream->buff + pmhRstream->bytes_read;
+    }
+
+    ret_line[offset] = 0;
+    return ret_line;
+}
+
+pmhWstream_t *
+pmhutils_wstream_create_with_buf (char *buf, uint32_t nbytes)
+{
+    pmhWstream_t *pmhWstream = NULL;
+
+    if (buf && nbytes && (nbytes > 0)) {
+        pmhWstream = (pmhWstream_t *) cpr_malloc(sizeof(pmhWstream_t));
+        if (!pmhWstream) {
+            return (NULL);
+        }
+        pmhWstream->buff = buf;
+        pmhWstream->nbytes = 0;
+        pmhWstream->total_bytes = nbytes;
+        pmhWstream->growable = FALSE;
+    }
+    return (pmhWstream);
+}
+
+
+uint32_t
+pmhutils_wstream_get_length (pmhWstream_t *ws)
+{
+    if (ws) {
+        return (ws->nbytes);
+    } else {
+        return (0);
+    }
+}
+
+
+void
+pmhutils_wstream_delete (pmhWstream_t *pmhWstream, boolean freebuf)
+{
+    if (pmhWstream && freebuf && pmhWstream->buff) {
+        cpr_free(pmhWstream->buff);
+    }
+}
+
+
+boolean
+pmhutils_wstream_write_line (pmhWstream_t *pmhWstream, char *this_line)
+{
+
+    /* Sanity check */
+    if (this_line == NULL || strlen(this_line) > SANITY_LINE_SIZE) {
+        return FALSE;
+    }
+
+    if (!(pmhWstream && this_line)) {
+        return FALSE;
+    }
+
+    while ((pmhWstream->nbytes + (int32_t)strlen(this_line)) >
+            pmhWstream->total_bytes) {
+        if (!pmhWstream->growable ||
+            (FALSE == pmhutils_wstream_grow(pmhWstream))) {
+            return FALSE;
+        }
+    }
+
+    memcpy(&pmhWstream->buff[pmhWstream->nbytes], this_line,
+           strlen(this_line));
+
+    pmhWstream->nbytes += strlen(this_line);
+    if (!pmhutils_wstream_write_byte(pmhWstream, '\r')) {
+        return FALSE;
+    }
+
+    if (!pmhutils_wstream_write_byte(pmhWstream, '\n')) {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+boolean
+pmhutils_wstream_write_byte (pmhWstream_t *pmhWstream, char c)
+{
+    if (!pmhWstream) {
+        return FALSE;
+    }
+
+    if ((pmhWstream->nbytes + 1) > pmhWstream->total_bytes) {
+        if (!pmhWstream->growable ||
+            (FALSE == pmhutils_wstream_grow(pmhWstream))) {
+            return FALSE;
+        }
+    }
+
+    pmhWstream->buff[pmhWstream->nbytes] = c;
+    pmhWstream->nbytes++;
+
+    return TRUE;
+}
+
+boolean
+pmhutils_wstream_write_bytes (pmhWstream_t *pmhWstream,
+                              char *inbuf, uint32_t nbytes)
+{
+
+    if (!pmhWstream) {
+        return FALSE;
+    }
+
+    while ((pmhWstream->nbytes + (int32_t) nbytes) > pmhWstream->total_bytes) {
+        if (!pmhWstream->growable ||
+            (FALSE == pmhutils_wstream_grow(pmhWstream))) {
+            return FALSE;
+        }
+    }
+
+    memcpy(&pmhWstream->buff[pmhWstream->nbytes], inbuf, nbytes);
+
+    pmhWstream->nbytes += nbytes;
+
+    return TRUE;
+}
+
+boolean
+pmhutils_wstream_grow (pmhWstream_t *pmhWstream)
+{
+    char *newbuf;
+
+    if (!pmhWstream || !pmhWstream->buff || !pmhWstream->growable) {
+        return FALSE;
+    }
+
+    newbuf = (char *) cpr_realloc((void *) pmhWstream->buff,
+                                  pmhWstream->total_bytes + WSTREAM_START_SIZE);
+    if (newbuf == NULL) {
+        if ((pmhWstream->total_bytes + WSTREAM_START_SIZE) != 0) {
+            cpr_free(pmhWstream->buff);
+        }
+        pmhWstream->buff = NULL;
+        return FALSE;
+    }
+
+    pmhWstream->buff = newbuf;
+
+    pmhWstream->total_bytes += WSTREAM_START_SIZE;
+
+    return TRUE;
+}
diff --git a/libs/sipcc/core/sipstack/sip_common_regmgr.c b/libs/sipcc/core/sipstack/sip_common_regmgr.c
new file mode 100644 (file)
index 0000000..72c14fb
--- /dev/null
@@ -0,0 +1,3471 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "phntask.h"
+#include "phone_types.h"
+#include "phone_debug.h"
+#include "util_string.h"
+#include "dns_utils.h"
+#include "cpr_socket.h"
+#include "util_string.h"
+#include "sip_common_transport.h"
+#include "sip_ccm_transport.h"
+#include "ccsip_messaging.h"
+#include "ccsip_register.h"
+#include "sip_common_transport.h"
+#include "sip_common_regmgr.h"
+#include "singly_link_list.h"
+#include "uiapi.h"
+#include "text_strings.h"
+#include "ccsip_callinfo.h"
+#include "sip_interface_regmgr.h"
+#include "ccsip_task.h"
+#include "cpr_rand.h"
+#include "ccsip_platform_tcp.h"
+#include "ccsip_subsmanager.h"
+#include "ccsip_publish.h"
+#include "ccsip_platform_tls.h"
+
+#define REGALL_FAIL_TIME       100
+boolean regall_fail_attempt = FALSE;
+boolean registration_reject = FALSE;
+
+int retry_times = 0;
+
+boolean config_update_required = FALSE;
+void *new_standby_available = NULL;
+sll_handle_t fallback_ccb_list;
+static boolean wan_failure = FALSE;
+extern ti_config_table_t CCM_Device_Specific_Config_Table[MAX_CCM];
+extern ccm_act_stdby_table_t CCM_Active_Standby_Table;
+extern uint16_t ccm_config_id_addr_str[MAX_CCM];
+#ifdef IPV6_STACK_ENABLED
+
+extern uint16_t ccm_config_id_ipv6_addr_str[MAX_CCM];
+#endif
+
+extern boolean sip_reg_all_failed;
+extern void sip_platform_handle_service_control_notify(sipServiceControl_t * scp);
+extern void ui_update_registration_state_all_lines(boolean registered);
+extern int phone_local_tcp_port[UNUSED_PARAM];
+
+ccm_failover_table_t CCM_Failover_Table;
+ccm_fallback_table_t CCM_Fallback_Table;
+cc_config_table_t CC_Config_Table[MAX_REG_LINES + 1];
+typedef struct fallback_line_num_t_ {
+    line_t line;
+    boolean available;
+} fallback_line_num_t;
+
+static fallback_line_num_t fallback_lines_available[MAX_CCM - 1] =
+{
+    {MAX_CCBS,     TRUE},
+    {MAX_CCBS + 1, TRUE},
+};
+
+static void sip_regmgr_update_call_ccb(void);
+void sip_regmgr_fallback_generic_timer_stop(cprTimer_t timer);
+boolean sip_regmgr_find_fallback_ccb_by_ccmid (CCM_ID ccm_id, ccsipCCB_t **ccb_ret);
+
+void sip_regmgr_clean_standby_ccb(ccsipCCB_t *ccb);
+static void sip_regmgr_tls_retry_timer_start (fallback_ccb_t *fallback_ccb);
+static void
+sip_regmgr_generic_timer_start_failure (fallback_ccb_t *fallback_ccb,
+                                        uint32_t event)
+{
+
+}
+
+static void
+sip_regmgr_generic_message_post_failure (fallback_ccb_t *fallback_ccb,
+                                         uint32_t event)
+{
+
+}
+
+/** sip_regmgr_get_fallback_ccb_list
+ *
+ * PARAMETERS: NULL, or fallback_ccb
+ *
+ * DESCRIPTION: Returns the ccb and fallback_ccb from linked list
+ *              The first call to this function should pass in NULL
+ *              Subsequent calls should pass the opaque (for the caller)
+ *              token so that the next ccb is returned
+ *
+ * RETURNS:     fallback_ccb as the opaque token, ccb as the return value
+ *
+ */
+ccsipCCB_t *
+sip_regmgr_get_fallback_ccb_list (uint32_t *previous_data_p)
+{
+    fallback_ccb_t *this_fallback_ccb = NULL;
+    uint32_t data;
+
+    data = (uint32_t) (*previous_data_p);
+    this_fallback_ccb = (fallback_ccb_t *)
+        sll_next(fallback_ccb_list, (void *)(long) (data));
+    if (this_fallback_ccb) {
+        *previous_data_p = (long) (this_fallback_ccb);
+        return (this_fallback_ccb->ccb);
+    }
+    return NULL;
+}
+
+/*
+ ** sip_regmgr_get_fallback_ccb_by_index
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: line number
+ *
+ *  DESCRIPTION: Searches the linked list of fallback ccb's and
+ *               returns the ccb that matches the line number.
+ *
+ *  RETURNS: fallback_ccb if match, NULL if not.
+ *
+ */
+fallback_ccb_t *
+sip_regmgr_get_fallback_ccb_by_index (line_t ndx)
+{
+    return (fallback_ccb_t *)(sll_find(fallback_ccb_list, (void *)(long)ndx));
+}
+
+/*
+ ** sip_regmgr_find_fallback_ccb
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: find_by_p is the dn line number that is the used to
+ *              match with the fallback ccb's. data_p is the ccb that
+ *              from the linked list that is checked to see if it has
+ *              the dn number that is being searched for.
+ *
+ *  DESCRIPTION: finds the fallback ccb that matched a dn line value
+ *               This is the match routine provided while the singly
+ *               linked list for fallback ccb's is created. sll_find()
+ *               used this routine to perform the find.
+ *
+ *  RETURNS: Indicates if match was found or not.
+ *
+ */
+sll_match_e
+sip_regmgr_find_fallback_ccb (void *find_by_p, void *data_p)
+{
+    int to_find_ccb_index = (long) find_by_p;
+    fallback_ccb_t *fallback_ccb = (fallback_ccb_t *) data_p;
+    ccsipCCB_t *list_ccb = (ccsipCCB_t *) fallback_ccb->ccb;
+
+    if (to_find_ccb_index == list_ccb->index) {
+        return (SLL_MATCH_FOUND);
+    } else {
+        return (SLL_MATCH_NOT_FOUND);
+    }
+}
+
+/*
+ ** sip_regmgr_find_fallback_ccb_by_callid
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: Callid string and the container for the matching
+ *              ccb, if found.
+ *
+ *  DESCRIPTION: Searches the linked list of fallback ccb's and
+ *               returns the ccb that matches the callid.
+ *
+ *  RETURNS: void
+ *
+ */
+void
+sip_regmgr_find_fallback_ccb_by_callid (const char *callid,
+                                        ccsipCCB_t **ccb_ret)
+{
+    const char fname[] = "sip_regmgr_find_fallback_ccb_by_callid";
+    fallback_ccb_t *fallback_ccb = NULL;
+    ccsipCCB_t *list_ccb = NULL;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Trying to find match for %s\n",
+                          DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), callid);
+    while ((fallback_ccb = (fallback_ccb_t *)sll_next(fallback_ccb_list,
+                                                      fallback_ccb)) != NULL) {
+        list_ccb = (ccsipCCB_t *) fallback_ccb->ccb;
+        if (strcmp(callid, list_ccb->sipCallID) == 0) {
+            *ccb_ret = list_ccb;
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Found ccb to match callid"
+                                  " line %d/%d\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname),
+                                  list_ccb->index,
+                                  list_ccb->dn_line);
+            break;
+        }
+    }
+}
+
+/*
+ ** sip_regmgr_find_fallback_ccb_by_addr_port
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: ipaddr and port to match and container for the ccb
+ *              if found.
+ *
+ *  DESCRIPTION: finds the fallback ccb that matched a addr:port value
+ *
+ *  RETURNS: Indicates if match was found or not.
+ *
+ */
+boolean
+sip_regmgr_find_fallback_ccb_by_addr_port (cpr_ip_addr_t *ipaddr, uint16_t port,
+                                           ccsipCCB_t **ccb_ret)
+{
+    fallback_ccb_t *fallback_ccb = NULL;
+    ccsipCCB_t *list_ccb = NULL;
+    ti_config_table_t *cfg_table_entry;
+    ti_common_t *ti_common;
+    boolean found_ccb = FALSE;
+
+    while ((fallback_ccb = (fallback_ccb_t *)sll_next(fallback_ccb_list,
+                                                      fallback_ccb)) != NULL) {
+        list_ccb = (ccsipCCB_t *) fallback_ccb->ccb;
+        cfg_table_entry = (ti_config_table_t *)
+            list_ccb->cc_cfg_table_entry;
+        ti_common = &cfg_table_entry->ti_common;
+        if (util_compare_ip(&(ti_common->addr), ipaddr) && ti_common->port == port) {
+            *ccb_ret = list_ccb;
+            found_ccb = TRUE;
+            break;
+        }
+    }
+    return (found_ccb);
+}
+
+/**
+ ** sip_regmgr_find_fallback_ccb_by_ccmid
+ *  finds the fallback ccb that matched a ccm id
+ *
+ *  @param  ccm_id ccm id to match and ccb_ret - ccb if found
+ *
+ *  @return TRUE if found; else  FALSE
+ *
+ */
+boolean
+sip_regmgr_find_fallback_ccb_by_ccmid (CCM_ID ccm_id, ccsipCCB_t **ccb_ret)
+{
+    fallback_ccb_t *fallback_ccb = NULL;
+    ccsipCCB_t *list_ccb = NULL;
+    ti_config_table_t *cfg_table_entry;
+    boolean found_ccb = FALSE;
+
+    while ((fallback_ccb = (fallback_ccb_t *)sll_next(fallback_ccb_list,
+                                                      fallback_ccb)) != NULL) {
+        list_ccb = (ccsipCCB_t *) fallback_ccb->ccb;
+        if (list_ccb) {
+            cfg_table_entry = (ti_config_table_t *)
+                              list_ccb->cc_cfg_table_entry;
+            if (cfg_table_entry &&
+               (cfg_table_entry->ti_specific.ti_ccm.ccm_id == ccm_id)) {
+                               if(ccb_ret != NULL){
+                                       *ccb_ret = list_ccb;
+                               }
+                found_ccb = TRUE;
+                break;
+            }
+        }
+    }
+    return (found_ccb);
+}
+
+/*
+ ** sip_regmgr_get_fallback_line_num
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: void
+ *
+ *  DESCRIPTION: returns the fallback line number to use during
+ *               failover while creating fallback ccb's
+ *
+ *  RETURNS: line number to use.
+ *
+ */
+line_t
+sip_regmgr_get_fallback_line_num ()
+{
+    const char fname[] = "sip_regmgr_get_fallback_line_num";
+    int ndx;
+    line_t line = 0;
+
+    for (ndx = 0; ndx < (MAX_CCM - 1); ndx++) {
+        if (fallback_lines_available[ndx].available) {
+            fallback_lines_available[ndx].available = FALSE;
+            line = fallback_lines_available[ndx].line;
+            break;
+        }
+    }
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Allocated fallback line %d at index %d\n",
+                          DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), line, (int) (line - MAX_CCBS));
+    return (line);
+}
+
+/*
+ ** sip_regmgr_return_fallback_line_num
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: void
+ *
+ *  DESCRIPTION: returns the fallback line number for reuse during
+ *               failover while creating fallback ccb's
+ *
+ *  RETURNS: line number to use.
+ *
+ */
+void
+sip_regmgr_return_fallback_line_num (line_t num)
+{
+    const char fname[] = "sip_regmgr_return_fallback_line_num";
+
+    if (((num - MAX_CCBS) > -1) &&
+        ((num - MAX_CCBS) < (MAX_CCM - 1))) {
+        fallback_lines_available[(int)(num - MAX_CCBS)].available = TRUE;
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Returned fallback line %d at index %d\n",
+                              DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), num, (int) (num - MAX_CCBS));
+    } else {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Invalid index for fallback_lines_available %d",
+                              DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), (int) (num - MAX_CCBS));
+    }
+}
+
+/*
+ ** sip_regmgr_clean_fallback_ccb
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: pointer to the fallback ccb
+ *
+ *  DESCRIPTION: Clean fallback CCBs
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_regmgr_clean_fallback_ccb (fallback_ccb_t *fallback_ccb)
+{
+    if (fallback_ccb == NULL) {
+        return;
+    }
+
+    if (fallback_ccb->ccb) {
+        sip_regmgr_return_fallback_line_num(fallback_ccb->ccb->index);
+    }
+    (void) cprCancelTimer(fallback_ccb->WaitTimer.timer);
+    (void) cprDestroyTimer(fallback_ccb->WaitTimer.timer);
+    fallback_ccb->WaitTimer.timer = NULL;
+    fallback_ccb->tls_socket_waiting = FALSE;
+
+    (void) cprCancelTimer(fallback_ccb->RetryTimer.timer);
+    (void) cprDestroyTimer(fallback_ccb->RetryTimer.timer);
+    fallback_ccb->RetryTimer.timer = NULL;
+
+    if (fallback_ccb->ccb) {
+        sip_sm_call_cleanup(fallback_ccb->ccb);
+        cpr_free(fallback_ccb->ccb);
+        fallback_ccb->ccb = NULL;
+    }
+}
+
+/*
+ ** sip_regmgr_free_fallback_ccb
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: The fallback ccb to be deleted.
+ *
+ *  DESCRIPTION: Delete a fallback ccb and its associated timers
+ *               and removes it from the linked list
+ *
+ *  RETURNS: none
+ */
+void
+sip_regmgr_free_fallback_ccb (ccsipCCB_t *ccb)
+{
+    const char fname[] = "sip_regmgr_free_fallback_ccb";
+    fallback_ccb_t *fallback_ccb;
+
+    fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+
+    if (!fallback_ccb) {
+        return;
+    }
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Freed fallback ccb for %s:%d\n",
+                          DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), ccb->reg.proxy, ccb->reg.port);
+    sip_regmgr_clean_fallback_ccb(fallback_ccb);
+    if (sll_remove(fallback_ccb_list, fallback_ccb) != SLL_RET_SUCCESS) {
+        CCSIP_DEBUG_ERROR("%s: sll_remove error for fallback_ccb\n", fname);
+    }
+    cpr_free(fallback_ccb);
+}
+
+/*
+ ** sip_regmgr_cleanup_fallback_ccb_list
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: none
+ *
+ *  DESCRIPTION: Delete fallback ccbs and its associated timers
+ *               and removes it from the linked list
+ *
+ *  RETURNS: none
+ */
+void
+sip_regmgr_free_fallback_ccb_list ()
+{
+    fallback_ccb_t *fallback_ccb = NULL;
+
+    while ((fallback_ccb = (fallback_ccb_t *)sll_next(fallback_ccb_list, NULL))
+           != NULL) {
+        sip_regmgr_clean_fallback_ccb(fallback_ccb);
+        (void) sll_remove(fallback_ccb_list, fallback_ccb);
+        cpr_free(fallback_ccb);
+    }
+    sll_destroy(fallback_ccb_list);
+    fallback_ccb_list = NULL;
+}
+
+/*
+ ** sip_regmgr_create_fallback_ccb
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: ccm_id, dn_line of the new fallback
+ *              ccb to be created.
+ *
+ *  DESCRIPTION: Creates a new fallback ccb and its associated timers
+ *               and adds it to the linked list.
+ *
+ *  RETURNS: Indicates if the fallback ccb was created successfully
+ *           or not.
+ *
+ */
+boolean
+sip_regmgr_create_fallback_ccb (CCM_ID ccm_id, line_t dn_line)
+{
+    const char fname[] = "sip_regmgr_create_fallback_ccb";
+    ccsipCCB_t *ccb = NULL;
+    boolean ccb_created = FALSE;
+    fallback_ccb_t *fallback_ccb;
+    static const char sipWaitTimerName[] = "sipWait";
+    static const char sipRegRetryTimerName[] = "sipRetry";
+    line_t fallback_line;
+
+    if (((int)dn_line < 1) || ((int)dn_line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, dn_line);
+        return(FALSE);
+    }
+
+    if (ccm_id >= MAX_CCM) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"ccm id <%d> out of bounds.\n",
+                          fname, ccm_id);
+        return(FALSE);
+    }
+    /* Check if fallback ccb exists. If so, return */
+    if (sip_regmgr_find_fallback_ccb_by_ccmid(ccm_id, NULL)) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"fallback ccb exists  for ccmid %d\n",
+                              DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), ccm_id);
+        return(TRUE);
+    }
+    fallback_line = sip_regmgr_get_fallback_line_num();
+    if (!fallback_line) {
+        /* Couldn't get fallback line number */
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"couldn't get fallback line for ccmid %d\n",
+                              DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), ccm_id);
+        return(FALSE);
+    }
+    fallback_ccb = (fallback_ccb_t *) cpr_calloc(1, sizeof(fallback_ccb_t));
+    if (fallback_ccb) {
+        fallback_ccb->WaitTimer.timer = cprCreateTimer(sipWaitTimerName,
+                                                       SIP_WAIT_TIMER,
+                                                       TIMER_EXPIRATION,
+                                                       sip_msgq);
+
+        fallback_ccb->RetryTimer.timer = cprCreateTimer(sipRegRetryTimerName,
+                                                        SIP_RETRY_TIMER,
+                                                        TIMER_EXPIRATION,
+                                                        sip_msgq);
+        fallback_ccb->tls_socket_waiting = FALSE;
+        if (!fallback_ccb->WaitTimer.timer || !fallback_ccb->RetryTimer.timer) {
+            CCSIP_DEBUG_ERROR("%s: failed to create one or more"
+                              " UISM timers\n", fname);
+            if (fallback_ccb->WaitTimer.timer) {
+                (void) cprCancelTimer(fallback_ccb->WaitTimer.timer);
+                (void) cprDestroyTimer(fallback_ccb->WaitTimer.timer);
+                fallback_ccb->WaitTimer.timer = NULL;
+            }
+            if (fallback_ccb->RetryTimer.timer) {
+                (void) cprCancelTimer(fallback_ccb->RetryTimer.timer);
+                (void) cprDestroyTimer(fallback_ccb->RetryTimer.timer);
+                fallback_ccb->RetryTimer.timer = NULL;
+            }
+            ccb_created = FALSE;
+        }
+        ccb = (ccsipCCB_t *) cpr_calloc(1, sizeof(ccsipCCB_t));
+        if (ccb != NULL) {
+            (void) sip_sm_ccb_init(ccb, fallback_line, dn_line,
+                                   SIP_REG_STATE_IN_FALLBACK);
+            ccb->cc_type = CC_CCM;
+            ccb->cc_cfg_table_entry = CCM_Config_Table[dn_line - 1][ccm_id];
+            sstrncpy(ccb->reg.proxy,
+                     CCM_Config_Table[dn_line - 1][ccm_id]->ti_common.addr_str,
+                     MAX_IPADDR_STR_LEN);
+            ccb->reg.addr = CCM_Config_Table[dn_line - 1][ccm_id]->ti_common.addr;
+            ccb->reg.port = (uint16_t)
+                CCM_Config_Table[dn_line - 1][ccm_id]->ti_common.port;
+            ccb->dest_sip_addr =
+                CCM_Config_Table[dn_line - 1][ccm_id]->ti_common.addr;
+            ccb->dest_sip_port =
+                CCM_Config_Table[dn_line - 1][ccm_id]->ti_common.port;
+            ccb->local_port = CCM_Config_Table[dn_line - 1][ccm_id]->ti_common.listen_port;
+            fallback_ccb->ccb = ccb;
+            (void) sll_append(fallback_ccb_list, fallback_ccb);
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Created fallback ccb for %s:%d with line %d\n",
+                                  DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), ccb->reg.proxy, ccb->reg.port,
+                                  fallback_line);
+            ccb_created = TRUE;
+        } else {
+            CCSIP_DEBUG_ERROR(DEB_F_PREFIX"Memalloc failed for ccb for CCM-id %d\n",
+                                  DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), ccm_id);
+            sip_regmgr_clean_fallback_ccb(fallback_ccb);
+            if (fallback_ccb) {
+                cpr_free(fallback_ccb);
+            }
+        }
+    } else {
+        CCSIP_DEBUG_ERROR(DEB_F_PREFIX"Memalloc failed for fallback ccb for CCM-id %d\n",
+                              DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), ccm_id);
+        sip_regmgr_return_fallback_line_num(fallback_line);
+    }
+    return (ccb_created);
+}
+
+/*
+ ** sip_regmgr_trigger_fallback_monitor
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:void
+ *
+ *  DESCRIPTION: Walks through the linked list of fallback ccbs
+ *               and starts the retry timer on them and starts
+ *               sending keepalive messages to monitor the failed
+ *               ccms.
+ *
+ *  RETURNS: void.
+ *
+ */
+void
+sip_regmgr_trigger_fallback_monitor (void)
+{
+    const char fname[] = "sip_regmgr_trigger_fallback_monitor";
+    fallback_ccb_t *fallback_ccb = NULL;
+    ccsipCCB_t *ccb = NULL;
+
+    /*
+     * Go through the linked list of failed ccm's and
+     * start monitoring them.
+     */
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Looking to trigger fallback "
+                          "if any available\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+    do {
+        fallback_ccb = (fallback_ccb_t *) sll_next(fallback_ccb_list,
+                                                   (void *)fallback_ccb);
+        if (fallback_ccb) {
+            ti_config_table_t *ccm_table_entry;
+            ccb = fallback_ccb->ccb;
+            if (ccb->state == (int) SIP_REG_PRE_FALLBACK) {
+                char user[MAX_LINE_NAME_SIZE];
+
+                /*
+                 * If state is TokenWait,
+                 * Then transition back into InFallback state.
+                 * Else send out a keepalive message to the registration server.
+                 */
+                sip_util_get_new_call_id(ccb);
+                ccb->authen.cred_type = 0;
+                ccb->retx_counter     = 0;
+                ccb->reg.tmr_expire   = 0;
+                ccb->reg.act_time     = 0;
+                config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line, sizeof(user));
+                sip_reg_sm_change_state(ccb, SIP_REG_STATE_IN_FALLBACK);
+                ccm_table_entry = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+                if (ccm_table_entry->ti_common.handle != INVALID_SOCKET) {
+                    (void) sipSPISendRegister(ccb, 0, user, 0);
+                }
+
+                /*
+                 * Start the ack, retry timer
+                 */
+                sip_regmgr_retry_timer_start(fallback_ccb);
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Started monitoring %s:%d\n",
+                                      DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname),
+                                      ccb->reg.proxy, ccb->reg.port);
+            } else {
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"fallback is in progress ccb idx=%d",
+                                      DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname),ccb->index);
+            }
+        }
+    } while (fallback_ccb);
+}
+
+void set_active_ccm(ti_config_table_t *cfg_table_entry) {
+    CCM_Active_Standby_Table.active_ccm_entry = cfg_table_entry;
+    if (cfg_table_entry != NULL) {
+        DEF_DEBUG("set_active_ccm: ccm=%s  port=%d",
+        CCM_ID_PRINT(cfg_table_entry->ti_specific.ti_ccm.ccm_id),
+        phone_local_tcp_port[cfg_table_entry->ti_specific.ti_ccm.ccm_id]);
+    } else {
+        DEF_DEBUG("set_active_ccm: ccm=PRIMARY  port=-1");
+    }
+}
+
+
+/*
+ ** sip_regmgr_setup_new_active_ccb
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: pointer to the cfg table entry of the call manager
+ *              that is going to become the new active ccm.
+ *
+ *  DESCRIPTION: Sets up the reg ccb's for all the lines to point
+ *               to the new call manager during failover/fallback.
+ *
+ *  RETURNS: void.
+ *
+ */
+void
+sip_regmgr_setup_new_active_ccb (ti_config_table_t *cfg_table_entry)
+{
+    const char fname[] = "sip_regmgr_setup_new_active_ccb";
+    line_t ndx;
+    ccsipCCB_t *line_ccb;
+
+    for (ndx = REG_CCB_START; ndx < REG_CCB_END; ndx++) {
+        line_ccb = sip_sm_get_ccb_by_index(ndx);
+        ui_set_sip_registration_state(line_ccb->dn_line, FALSE);
+
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cancelling timers, line= %d\n",
+            DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), line_ccb->index);
+        (void) sip_platform_register_expires_timer_stop(line_ccb->index);
+        sip_stop_ack_timer(line_ccb);
+        line_ccb->reg.registered = 0;
+        sip_reg_sm_change_state(line_ccb, SIP_REG_STATE_IDLE);
+        sip_sm_call_cleanup(line_ccb);
+        // Since we are going to point this CCB to a new CCM, get a new call-id
+        line_ccb->sipCallID[0] = '\0';
+        sip_util_get_new_call_id(line_ccb);
+        line_ccb->cc_cfg_table_entry = (void *) cfg_table_entry;
+
+        if (cfg_table_entry == NULL) {
+            CCSIP_DEBUG_REG_STATE("%s: param cfg_table_entry is NULL!!!\n", fname);
+            continue;
+        }
+        else {
+            sstrncpy(line_ccb->reg.proxy, cfg_table_entry->ti_common.addr_str,
+                 MAX_IPADDR_STR_LEN);
+            line_ccb->dest_sip_addr = cfg_table_entry->ti_common.addr;
+            line_ccb->dest_sip_port = (uint16_t) cfg_table_entry->ti_common.port;
+            line_ccb->reg.addr = cfg_table_entry->ti_common.addr;
+            line_ccb->reg.port = (uint16_t) cfg_table_entry->ti_common.port;
+            /*
+             * Modify destination fields in call back timer struct
+             */
+            (void) sip_platform_msg_timer_update_destination(line_ccb->index,
+                                                         &(line_ccb->reg.addr),
+                                                         line_ccb->reg.port);
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Updated active to %s:%d\n",
+                              DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname),
+                              line_ccb->reg.proxy, line_ccb->reg.port);
+        }
+    }
+    set_active_ccm(cfg_table_entry);
+}
+
+/*
+ ** sip_regmgr_setup_new_standby_ccb
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: pointer to the cfg table entry of the call manager
+ *              that is going to become the new standby ccm.
+ *
+ *  DESCRIPTION: Sets up the reg backup ccb for all the lines to point
+ *               to the new call manager during failover/fallback.
+ *
+ *  RETURNS: void.
+ *
+ */
+void
+sip_regmgr_setup_new_standby_ccb (CCM_ID ccm_index)
+{
+    const char fname[] = "sip_regmgr_setup_new_standby_ccb";
+    ccsipCCB_t *ccb;
+    ti_config_table_t *cfg_table_entry;
+
+
+    ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+    if (((int)ccb->dn_line < 1) || ((int)ccb->dn_line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, ccb->dn_line);
+        return;
+    }
+    if (ccm_index >= MAX_CCM) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"ccm id <%d> out of bounds.\n",
+                          fname, ccm_index);
+        return;
+    }
+    cfg_table_entry = CCM_Config_Table[ccb->dn_line - 1][ccm_index];
+
+    ccsip_register_cleanup(ccb, FALSE);
+    // Since we are going to point this CCB to a new CCM, get a new call-id
+    ccb->sipCallID[0] = '\0';
+    sip_util_get_new_call_id(ccb);
+    sip_reg_sm_change_state(ccb, SIP_REG_STATE_UNREGISTERING);
+    ccb->cc_cfg_table_entry = (void *) cfg_table_entry;
+    sstrncpy(ccb->reg.proxy, cfg_table_entry->ti_common.addr_str,
+             MAX_IPADDR_STR_LEN);
+    ccb->reg.addr = cfg_table_entry->ti_common.addr;
+    ccb->reg.port = (uint16_t)
+        cfg_table_entry->ti_common.port;
+    ccb->dest_sip_addr = cfg_table_entry->ti_common.addr;
+    ccb->dest_sip_port = cfg_table_entry->ti_common.port;
+    ccb->local_port = cfg_table_entry->ti_common.listen_port;
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"For ccb_index=REG_BACKUP_CCB=%d, updated standby to %s:%d\n",
+                          DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname),
+                         REG_BACKUP_CCB, ccb->reg.proxy, ccb->reg.port);
+    CCM_Active_Standby_Table.standby_ccm_entry = cfg_table_entry;
+}
+
+/*
+ ** sip_regmgr_ccm_get_next
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: ccb pointer
+ *
+ *  DESCRIPTION: Gets the next ccm in the list for failover.
+ *
+ *  RETURNS: pointer to the config table of the next ccm in line.
+ *
+ */
+ti_config_table_t *
+sip_regmgr_ccm_get_next (ccsipCCB_t *ccb, CC_POSITION from_cc)
+{
+    /*
+     * If from_cc is ACTIVE_CC, then check to see if the current
+     * standby is valid. If it is then set that to be the ACTIVE_CC,
+     * and return that. If there is no current standby, then see if
+     * we have WAN failure.
+     * Add the failed units in the fallback ccm list and trigger
+     * fallback algorithm on them.
+     */
+    const char fname[] = "sip_regmgr_ccm_get_next";
+    ti_config_table_t *ccm_table_ptr = NULL,
+                      *ccm_table_active_entry = NULL,
+                      *ccm_table_standby_entry = NULL;
+    CCM_ID ccm_id, ccm_index;
+    CC_POSITION from_cc_save = NONE_CC;
+
+
+    ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+
+    if (!ccm_table_ptr) {
+        return (NULL);
+    }
+    ccm_id = (ccm_table_ptr->ti_specific.ti_ccm.ccm_id);
+
+    /*
+     * Update the cfg table to invalidate the current ccm that
+     * has gone down in the case of tcp and create the fallback
+     * ccb.
+     * Already taken care of in the ccsip_platform_tcp.c function
+     * sip_tcp_createconnfailed_to_spi()
+     */
+    if (ccm_table_ptr->ti_common.conn_type != CONN_UDP) {
+        int connid;
+
+        connid = sip_tcp_fd_to_connid(ccm_table_ptr->ti_common.handle);
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"clear the socket and port for "
+            "current active cucm_id=%d connid=%d\n",
+             DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname),ccm_id, connid );
+        sip_tcp_purge_entry(connid);
+        sipTransportSetServerHandleAndPort(INVALID_SOCKET, 0,
+                                           ccm_table_ptr);
+    }
+
+    // Update CCM status
+
+    ui_set_ccm_conn_status(ccm_table_ptr->ti_common.addr_str, CCM_STATUS_NONE);
+
+
+    (void) sip_regmgr_create_fallback_ccb(ccm_id, ccb->dn_line);
+
+    if (from_cc == ACTIVE_CC) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Came here from cucm\n", DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname));
+        if (CCM_Active_Standby_Table.standby_ccm_entry) {
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"old ccm_id=%d new_ccm_id=%d Standby=NULL\n",
+                DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname), ccm_id,
+                CCM_Active_Standby_Table.standby_ccm_entry->ti_specific.ti_ccm.ccm_id);
+            ccm_table_ptr = CCM_Active_Standby_Table.standby_ccm_entry;
+            ccm_id = (ccm_table_ptr->ti_specific.ti_ccm.ccm_id);
+            /*
+             * The current standby is going to be tried for the ACTIVE CC
+             * role, so fill the standby_ccm_entry with the next valid cc
+             * info.
+             */
+            set_active_ccm(ccm_table_ptr);
+            CCM_Active_Standby_Table.standby_ccm_entry = NULL;
+            /*
+             * Save the ccm_table_ptr to return the correct value
+             */
+            ccm_table_active_entry = ccm_table_ptr;
+            /*
+             * We have used the entry of the current standby for our
+             * active cc. So go ahead and find the new standby entry
+             */
+            ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+
+            ccm_table_ptr = NULL;
+            from_cc_save = ACTIVE_CC;
+        } else {
+            /*
+             * There is no Standby available and the Acitve has
+             * failed. No CC available. Initiate Reboot !
+             */
+            ccsip_register_set_register_state(SIP_REG_NO_CC);
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"NO CC AVAILABLE. NEED TO REBOOT !\n",
+                                  DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname));
+            set_active_ccm(NULL);
+
+            return (NULL);
+        }
+    }
+
+    for (ccm_index = (CCM_ID) (ccm_id + 1); ccm_index < MAX_CCM; ccm_index++) {
+        ti_ccm_t *ti_ccm = &CCM_Config_Table[ccb->dn_line - 1][ccm_index]->
+                               ti_specific.ti_ccm;
+
+        if (ti_ccm->is_valid) {
+            ccm_table_standby_entry = sip_regmgr_ccm_get_conn(ccb->dn_line,
+                (ti_config_table_t *) CCM_Config_Table[ccb->dn_line - 1][ccm_index]);
+            if (ccm_table_standby_entry == NULL) {
+                /*
+                 * Create a fallback_ccm ccb and put it in a queue to use
+                 * later (POST_FAILOVER)
+                 */
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Could not set transport"
+                    "connection to this standby. Ignore this. Continue search \n",
+                    DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname) );
+
+                (void) sip_regmgr_create_fallback_ccb(ccm_index, ccb->dn_line);
+            } else {
+                /*
+                 * Will always be REG_BACKUP_CCB
+                 * Found our 'next' ccm
+                 */
+                CCM_Active_Standby_Table.standby_ccm_entry =
+                    ccm_table_standby_entry;
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"new standby ccm id %d ip=%s ccm_id=%d\n",
+                    DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname), ccm_index,
+                    ccm_table_standby_entry->ti_common.addr_str, ti_ccm->ccm_id);
+                break;
+            }
+        }
+    }
+
+    /*
+     * We can wait until the active line re-registers to trigger
+     * this but doing it as soon as we find there is no backup
+     * available
+     */
+    if (ccm_table_standby_entry == NULL) {
+        /*
+         * regmgr - FAILOVER Trigger fallback monitoring
+         */
+        ccsip_register_set_register_state(SIP_REG_NO_STANDBY);
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Setting register state to SIP_REG_NO_STANDBY \n",
+            DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname) );
+
+        ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+
+        sip_sm_call_cleanup(ccb);
+        // No new standby ccm. Cleanup standby ccm address, port and timers
+        sip_regmgr_clean_standby_ccb(ccb);
+    }
+    if (from_cc_save == ACTIVE_CC) {
+        ccm_table_ptr = ccm_table_active_entry;
+    } else {
+        ccm_table_ptr = ccm_table_standby_entry;
+    }
+    return (ccm_table_ptr);
+}
+
+/*
+ ** sip_regmgr_tls_retry_timer_start
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: fallback ccb pointer
+ *
+ *  DESCRIPTION: Starts the tls retry timer on the fallback ccb
+ *  Normally secd takes 3-6 secs to establish a connection. Start
+ *  timer to check the status after 8s. Restart keep alive timer for the
+ *  remaining time if socket is invalid after 8s
+ *
+ *  RETURNS: void
+ *
+ */
+static void
+sip_regmgr_tls_retry_timer_start (fallback_ccb_t *fallback_ccb)
+{
+    const char fname[] = "sip_regmgr_tls_retry_timer_start";
+    ccsipCCB_t *ccb = NULL;
+    int timeout;
+
+    if (!fallback_ccb) {
+        return;
+    }
+    ccb = fallback_ccb->ccb;
+    if (fallback_ccb->tls_socket_waiting) {
+        /* socket invalid after TLS_CONNECT_TIME(8s)
+         * Restart timer for the remaining time(eg:120-8 s)
+         */
+        timeout = sip_config_get_keepalive_expires();
+
+        if (timeout > MAX_FALLBACK_MONITOR_PERIOD) {
+            timeout = MAX_FALLBACK_MONITOR_PERIOD;
+        }
+        if (timeout > TLS_CONNECT_TIME) {
+            timeout -= TLS_CONNECT_TIME;
+        }
+        fallback_ccb->tls_socket_waiting = FALSE;
+    } else {
+        /* start timer to monitor the socket status */
+        timeout = TLS_CONNECT_TIME;
+        fallback_ccb->tls_socket_waiting = TRUE;;
+    }
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Starting TLS timer (%d sec)\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_FALLBACK, ccb->index, ccb->dn_line, fname), timeout);
+    if (cprStartTimer(fallback_ccb->RetryTimer.timer, timeout * 1000,
+                fallback_ccb) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, 0, fname, "cprStartTimer");
+        sip_regmgr_generic_timer_start_failure(fallback_ccb, SIP_TMR_REG_RETRY);
+    }
+}
+
+/*
+ ** sip_regmgr_retry_timer_start
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: fallback ccb pointer
+ *
+ *  DESCRIPTION: Starts the retry timer on the fallback ccb
+ *
+ *  RETURNS: void
+ *
+ */
+void
+sip_regmgr_retry_timer_start (fallback_ccb_t *fallback_ccb)
+{
+    const char fname[] = "sip_regmgr_retry_timer_start";
+    ccsipCCB_t *ccb = NULL;
+    int timeout;
+
+    if (!fallback_ccb) {
+        return;
+    }
+
+    ccb = fallback_ccb->ccb;
+
+    timeout = sip_config_get_keepalive_expires();
+
+    if (timeout > MAX_FALLBACK_MONITOR_PERIOD) {
+       timeout = MAX_FALLBACK_MONITOR_PERIOD;
+    }
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Starting fallback timer (%d sec)\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_FALLBACK, ccb->index, ccb->dn_line, fname), timeout);
+
+    ccb->retx_flag = TRUE;
+    if (cprStartTimer(fallback_ccb->RetryTimer.timer, timeout * 1000,
+                fallback_ccb) == CPR_FAILURE) {
+        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb->index, 0, fname, "cprStartTimer");
+        sip_regmgr_generic_timer_start_failure(fallback_ccb, SIP_TMR_REG_RETRY);
+        ccb->retx_flag = FALSE;
+    }
+}
+
+/*
+ ** sip_regmgr_retry_timeout_expire
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: void *data (fallback ccb)
+ *
+ *  DESCRIPTION: Is the callback function for retry timer
+ *
+ *  RETURNS: void
+ *
+ */
+void
+sip_regmgr_retry_timeout_expire (void *data)
+{
+    static const char fname[] = "sip_regmgr_retry_timeout_expire";
+    fallback_ccb_t *fallback_ccb;
+    ccsipCCB_t *ccb;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+    fallback_ccb = (fallback_ccb_t *) data;
+    if (!fallback_ccb) {
+        return;
+    }
+    ccb = (ccsipCCB_t *) fallback_ccb->ccb;
+    if (ccsip_register_send_msg(SIP_TMR_REG_RETRY, ccb->index) != SIP_REG_OK) {
+        sip_regmgr_generic_message_post_failure(fallback_ccb,
+                                                SIP_TMR_REG_RETRY);
+    }
+}
+
+/*
+ ** sip_regmgr_set_stability_total_msgs
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: fallback ccb pointer and a boolean indicating if
+ *              it is in a state where we are waiting for the
+ *              phone to get idle before transitioning into token
+ *              wait state.
+ *
+ *  DESCRIPTION: Starts the stability timer on the fallback ccb
+ *
+ *  RETURNS: void
+ *
+ */
+void
+sip_regmgr_set_stability_total_msgs (fallback_ccb_t *fallback_ccb)
+{
+    const char fname[] = "sip_regmgr_set_stability_total_msgs";
+    ccsipCCB_t *ccb = NULL;
+    int timer_keepalive_expires;
+    int connection_mode_duration;
+
+    if (!fallback_ccb) {
+        return;
+    }
+    ccb = fallback_ccb->ccb;
+
+    config_get_value(CFGID_CONN_MONITOR_DURATION,
+                     &connection_mode_duration, sizeof(connection_mode_duration));
+
+    timer_keepalive_expires = sip_config_get_keepalive_expires();
+
+    /* Stability count is used to wait to make sure that wan is not flapping */
+    fallback_ccb->StabilityMsgCount = connection_mode_duration / timer_keepalive_expires;
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Starting stability msg count as %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_FALLBACK, ccb->index, ccb->dn_line, fname),
+                          fallback_ccb->StabilityMsgCount);
+}
+
+/*
+ ** sip_regmgr_wait_timer_start
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: fallback ccb pointer
+ *
+ *  DESCRIPTION: Starts the wait timer on the fallback ccb
+ *
+ *  RETURNS: void
+ *
+ */
+void
+sip_regmgr_wait_timer_start (fallback_ccb_t *fallback_ccb)
+{
+    const char fname[] = "sip_regmgr_wait_timer_start";
+    ccsipCCB_t *ccb = NULL;
+    int timeout;
+
+    if (!fallback_ccb) {
+        return;
+    }
+    ccb = fallback_ccb->ccb;
+
+    timeout = sip_config_get_keepalive_expires();
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Starting wait timer (%d sec)\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_FALLBACK, ccb->index, ccb->dn_line, fname), timeout);
+
+    if (cprStartTimer(fallback_ccb->WaitTimer.timer, timeout * 1000,
+                fallback_ccb) == CPR_FAILURE) {
+        CCSIP_DEBUG_REG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                              ccb->index, 0, fname, "cprStartTimer");
+        sip_regmgr_generic_timer_start_failure(fallback_ccb, SIP_TMR_REG_WAIT);
+    }
+}
+
+/*
+ ** sip_regmgr_wait_timeout_expire
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: void *data (fallback ccb)
+ *
+ *  DESCRIPTION: Is the callback function for wait timer
+ *
+ *  RETURNS: void
+ *
+ */
+void
+sip_regmgr_wait_timeout_expire (void *data)
+{
+    static const char fname[] = "sip_regmgr_wait_timeout_expire";
+    fallback_ccb_t *fallback_ccb;
+    ccsipCCB_t *ccb;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+    fallback_ccb = (fallback_ccb_t *) data;
+    ccb = (ccsipCCB_t *) fallback_ccb->ccb;
+
+    sip_regmgr_fallback_generic_timer_stop(fallback_ccb->WaitTimer.timer);
+
+    if (ccsip_register_send_msg(SIP_TMR_REG_WAIT, ccb->index) != SIP_REG_OK) {
+        sip_regmgr_generic_message_post_failure(fallback_ccb, SIP_TMR_REG_WAIT);
+    }
+}
+
+void
+sip_regmgr_fallback_generic_timer_stop (cprTimer_t timer)
+{
+    static const char fname[] = "sip_regmgr_fallback_generic_timer_stop";
+
+    if (cprCancelTimer(timer) == CPR_FAILURE) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"%s failed!\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), "cprCancelTimer");
+    }
+
+    return;
+}
+
+
+/*
+ ** sip_regmgr_ev_fallback_retry
+ *
+ *
+ *  PARAMETERS: ccb and event
+ *
+ *  DESCRIPTION: Event handler for 4xx, 5xx, 6xx responses in fallback state
+ *               Start retry timer to monitor the fallback ccm
+ *               Wait for the timer to pop to send the next keepalive.
+ *
+ *
+ *  RETURNS: void
+ *
+ */
+void
+sip_regmgr_ev_fallback_retry (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "sip_regmgr_ev_fallback_retry";
+    fallback_ccb_t *fallback_ccb = NULL;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Recd retry event for LINE %d/%d in state %d\n",
+                          DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), ccb->index, ccb->dn_line, ccb->state);
+
+    sip_stop_ack_timer(ccb);
+    fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+    if (fallback_ccb) {
+        sip_regmgr_retry_timer_start(fallback_ccb);
+    }
+    free_sip_message(event->u.pSipMessage);
+}
+
+void
+sip_regmgr_ev_default (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "sip_regmgr_ev_default";
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received a default event in state %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->index, ccb->dn_line, fname), ccb->state);
+
+    sip_reg_sm_change_state(ccb, SIP_REG_STATE_IN_FALLBACK);
+    sip_regmgr_ev_tmr_ack_retry(ccb, event);
+    /* only free SIP messages, timeouts are internal */
+    if (event->type < (int) E_SIP_REG_TMR_ACK) {
+        free_sip_message(event->u.pSipMessage);
+    }
+}
+
+/*
+ ** sip_regmgr_ev_tmr_ack_retry
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: ccb and event
+ *
+ *  DESCRIPTION: Event handler for ack and retry timers. Please
+ *               see inline comments for the description.
+ *
+ *  RETURNS: void
+ *
+ */
+void
+sip_regmgr_ev_tmr_ack_retry (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "sip_regmgr_ev_tmr_ack_retry";
+    ti_config_table_t *ccm_table_ptr;
+    fallback_ccb_t *fallback_ccb;
+    CCM_ID current_standby_ccm_id, fallback_ccm_id;
+
+    /*
+     * If state is REGISTERING, set state to IN_FAILOVER.
+     * Mark all lines as unregistered and IDLE state.
+     * Create fallback ccb for the failed ccm and put it in
+     * a queue to be triggered post failover.
+     * Find the "next" ccm and set the values in the ccb.
+     * Need change state to IDLE and post REG_REQ, make sure
+     * we set the no_dns_lookup parameter to 1.
+     * If state is IN_FALLBACK, send register message
+     * If state is STABILITY_CHECK, transition back to IN_FALLBACK
+     * and send register with expire 0.
+     * If state is TOKEN_WAIT, transition back to IN_FALLBACK
+     * and send register with expire 0.
+     */
+    ccb->retx_counter = 0;
+    switch ((sipRegSMStateType_t) ccb->state) {
+    case SIP_REG_STATE_REGISTERED:
+        /*
+         * A send failure on a tcp for active cc could have caused
+         * this message.
+         * Fall through and do the same as what is done when in
+         * REGISTERING State.
+         */
+    case SIP_REG_STATE_REGISTERING:
+        /*
+         * Get the next ccm to use as active.
+         */
+        ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+        if (ccm_table_ptr != CCM_Active_Standby_Table.active_ccm_entry) {
+            break;
+        }
+        ccm_table_ptr = NULL;
+
+        ccm_table_ptr = sip_regmgr_ccm_get_next(ccb, ACTIVE_CC);
+
+        if (ccm_table_ptr == NULL) {
+                               /*
+                * Send indication of REG_ALLFAIL so platform can
+                * initiate a reboot.
+                */
+               CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Unable to get next ccm!\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+               // Cleanup fallback CCB list
+               sip_regmgr_free_fallback_ccb_list();
+               sip_reg_all_failed = TRUE;
+               sip_regmgr_handle_reg_all_fail();
+               break;
+        }
+
+               CCSIP_DEBUG_REG_STATE("%s: ccb information: ccb->dn_line=%d, ccb->index=%d, retry_times=%d\n",
+                                                          fname, ccb->dn_line, ccb->index, retry_times);
+
+               CCM_Failover_Table.failover_ccm_entry = ccm_table_ptr;
+        CCM_Failover_Table.failover_started = TRUE;
+        CCM_Failover_Table.prime_registered = FALSE;
+        CCM_Fallback_Table.fallback_ccm_entry = NULL;
+        (void) sip_platform_register_expires_timer_stop(ccb->index);
+        sip_stop_ack_timer(ccb);
+        sip_platform_msg_timer_stop(ccb->index);
+        sip_platform_failover_ind(ccm_table_ptr->ti_specific.ti_ccm.ccm_id);
+
+        /*
+        *   Notes for case: (CSCsx60672)
+        *   We should change the info in REG_BACKUP_CCB for the case that:
+        *   Active CCB failover
+        *   previous standby CCM is set as CCM_Active_Standby_Table.active_ccm_entry (#1 IPAddr A)
+        *   Third CCM would be set as  CCM_Active_Standby_Table.standby_ccm_entry (#2 IPAddr B)
+        *
+        *   if (#2) failover before Jphone callback,
+        *   The REG_BACKUP_CCB would still store the #1's info
+        *   So the call flows would prompt the wrong info(#1 IPAddr A) to Jphone, in this case, (#2 IPAddr B)
+        *      should be prompted
+        *
+        *   We need to update the REG_BACKUP_CCB's info here
+        */
+        if (CCM_Active_Standby_Table.standby_ccm_entry)
+        {
+            ti_ccm_t *ti_ccm = &(CCM_Active_Standby_Table.standby_ccm_entry->ti_specific.ti_ccm);
+            sip_regmgr_setup_new_standby_ccb(ti_ccm->ccm_id);
+            CCSIP_DEBUG_REG_STATE("%s: Setup new standby ccb, ccm_id is %d\n", fname, ti_ccm->ccm_id);
+        }
+
+        break;
+    case SIP_REG_STATE_UNREGISTERING:
+
+        if (ccb->index == REG_BACKUP_CCB) {
+            sip_stop_ack_timer(ccb);
+            sip_platform_msg_timer_stop(ccb->index);
+            ccm_table_ptr = sip_regmgr_ccm_get_next(ccb, STANDBY_CC);
+            if (ccm_table_ptr) {
+                /*
+                 * New standby, start monitoring it.
+                 * Change the ip addr and port to point to the new ccm
+                 * Set the CC_CONFIG_TABLE entry for all the lines to
+                 * point to the new ccm.
+                 * Set the ccb->cc_cfg_table_entry with the new entry.
+                 */
+                ti_ccm_t *ti_ccm;
+
+                ti_ccm = &ccm_table_ptr->ti_specific.ti_ccm;
+                sip_regmgr_setup_new_standby_ccb(ti_ccm->ccm_id);
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Start monitoring new standby cc\n",
+                                      DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+                (void) ccsip_register_send_msg(SIP_REG_CANCEL, ccb->index);
+
+                ui_set_ccm_conn_status(ccm_table_ptr->ti_common.addr_str,
+                                       CCM_STATUS_STANDBY);
+
+            } else {
+                CCM_Active_Standby_Table.standby_ccm_entry = NULL;
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Unable to get next standby ccm !\n",
+                                      DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+            }
+            sip_regmgr_trigger_fallback_monitor();
+        } else {
+            /*
+             * Received a timeout when the active tried to unregister
+             * after the expires timer.
+             */
+            sip_reg_sm_change_state(ccb, SIP_REG_STATE_IDLE);
+
+            if (ccsip_register_all_unregistered() == TRUE) {
+                ccsip_register_set_register_state(SIP_REG_IDLE);
+            }
+
+            /*
+             * Always check how sip_sm_call_cleanup is implemented.
+             * The function has a tendency to change.
+             */
+            sip_sm_call_cleanup(ccb);
+        }
+
+        break;
+    case SIP_REG_STATE_STABILITY_CHECK:
+        /*
+         * We getting here means that the wan link could have gone
+         * down again. We stop the timers associated with the
+         * carefree algorithm and transition back to the
+         * IN_FALLBACK state.
+         */
+        fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+        if (fallback_ccb) {
+            sip_regmgr_fallback_generic_timer_stop(fallback_ccb->WaitTimer.timer);
+        }
+        /*
+         * FALL THROUGH ALERT to the next state.
+         */
+        /*sa_ignore FALL_THROUGH*/
+    case SIP_REG_STATE_TOKEN_WAIT:
+        sip_reg_sm_change_state(ccb, SIP_REG_STATE_IN_FALLBACK);
+        /*
+         * FALL THROUGH ALERT ! get into the next case and
+         * start sending of keepalives to the newly failed
+         * ccm.
+         */
+         /*sa_ignore FALL_THROUGH*/
+    case SIP_REG_STATE_IN_FALLBACK:
+        /*
+         * Send Register msg with expiry set to zero (poll)
+         */
+        {
+            char user[MAX_LINE_NAME_SIZE];
+            fallback_ccb_t *fallback_ccb2;
+            ti_config_table_t *ccm_table_entry;
+
+            fallback_ccb2 = (fallback_ccb_t *) sll_find(fallback_ccb_list,
+                                                       (void *)(long)ccb->index);
+            ccm_table_entry = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+            /*
+             * Check here to see if we still need this fallback ccb
+             * to be polling its ccm. The reason is that one of the
+             * ccms that has a higher precedence to this ccm could have
+             * come back online, in which case we can stop monitoring
+             * this ccm.
+             */
+            if (CCM_Active_Standby_Table.standby_ccm_entry) {
+                current_standby_ccm_id = CCM_Active_Standby_Table.
+                    standby_ccm_entry->ti_specific.ti_ccm.ccm_id;
+                fallback_ccm_id = ccm_table_entry->ti_specific.ti_ccm.ccm_id;
+
+                if (current_standby_ccm_id < fallback_ccm_id) {
+                    /*
+                     * Clean the current fallback ccb and free it
+                     */
+                    DEF_DEBUG(DEB_F_PREFIX"Freeing the fallback ccb for %d ccm as current standby"
+                                " is %d ccm!\n",
+                                DEB_F_PREFIX_ARGS(SIP_REG_FREE_FALLBACK, fname),
+                                fallback_ccm_id, current_standby_ccm_id);
+                    sip_regmgr_free_fallback_ccb(ccb);
+                    break;
+                }
+            }
+
+            /*
+             * For the TCP case where a connection has gone down,
+             * We need to first try and setup the connection as
+             * part of the recovery process. After that is when we
+             * can start sending the keepalive messages. The following
+             * 'if' block handles that case. The socket handle of the
+             * config table is inited to INVALID_SOCKET when the connection
+             * goes down.
+             */
+            if ((ccb->cc_type == CC_CCM)
+                && (ccm_table_entry->ti_common.handle == INVALID_SOCKET)) {
+                ti_common_t *ti_common;
+                sipSPIMessage_t sip_msg;
+                uint16_t listener_port;
+                ti_ccm_t *ti_ccm;
+                CONN_TYPE conn_type = CONN_NONE;
+                cpr_socket_t server_conn_handle = INVALID_SOCKET;
+
+                ti_common = &ccm_table_entry->ti_common;
+                sip_msg.createConnMsg.addr = ti_common->addr;
+                sip_msg.createConnMsg.port = ti_common->port;
+                sip_msg.context = NULL;
+                ti_ccm = &ccm_table_entry->ti_specific.ti_ccm;
+
+                if (((ti_ccm->sec_level == AUTHENTICATED) ||
+                     (ti_ccm->sec_level == ENCRYPTED)) &&
+                    (ti_common->conn_type == CONN_TLS)) {
+                    sip_msg.context = NULL;
+                    if (fallback_ccb2 && (fallback_ccb2->tls_socket_waiting)) {
+                        /* socket invalid after TLS_CONN_TIME
+                         * Restart timer for the remaining time
+                         */
+                        sip_regmgr_tls_retry_timer_start(fallback_ccb2);
+                        break;
+                    }
+                    server_conn_handle = sip_tls_create_connection(&sip_msg,
+                                                                   FALSE,
+                                                                   ti_ccm->sec_level);
+                    conn_type = CONN_TLS;
+                } else {
+                    server_conn_handle = sip_tcp_create_connection(&sip_msg);
+                }
+                phone_local_tcp_port[ccm_table_entry->ti_specific.ti_ccm.ccm_id] =
+                    sip_msg.createConnMsg.local_listener_port;
+                if (server_conn_handle != INVALID_SOCKET) {
+                    listener_port = sip_msg.createConnMsg.local_listener_port;
+                    sipTransportSetServerHandleAndPort(server_conn_handle,
+                                                       listener_port,
+                                                       ccm_table_entry);
+                    ccb->local_port = listener_port;
+                    if ((conn_type == CONN_TLS) && fallback_ccb2) {
+                        /* start timer to monitor the tls connect status */
+                        sip_regmgr_tls_retry_timer_start(fallback_ccb2);
+                        break;
+                    }
+                } else {
+                    CCSIP_DEBUG_ERROR("%s: Error: "
+                                      "sip_platform_tcp_channel_create("
+                                      "server addr=%s, server port=%d) "
+                                      "failed.\n", fname, ti_common->addr_str,
+                                      ti_common->port);
+                    if (fallback_ccb2) {
+                        sip_regmgr_retry_timer_start(fallback_ccb2);
+                    }
+                    break;
+                }
+            }
+            /*
+             * Send a keepalive message and hope to get a reply back
+             */
+            clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+            sip_util_get_new_call_id(ccb);
+            ccb->authen.cred_type = 0;
+            ccb->retx_counter     = 0;
+            ccb->reg.tmr_expire   = 0;
+            ccb->reg.act_time     = 0;
+            config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line, sizeof(user));
+            /*
+             * Start the retry timer
+             */
+            (void) sipSPISendRegister(ccb, 0, user, 0);
+            if (fallback_ccb2) {
+                sip_regmgr_retry_timer_start(fallback_ccb2);
+            }
+            break;
+        }
+    default:
+        break;
+    }
+}
+
+/*
+ ** sip_regmgr_ev_in_fallback_2xx
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Event handler for the all responses
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_regmgr_ev_in_fallback_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "sip_regmgr_ev_in_fallback_2xx";
+    sipMessage_t *response = event->u.pSipMessage;
+    int status_code = 0;
+    fallback_ccb_t *fallback_ccb;
+
+    clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+
+    if (sipGetResponseCode(response, &status_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE), ccb->index,
+                          ccb->dn_line, fname);
+        free_sip_message(response);
+        return;
+    }
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received a %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_FALLBACK, ccb->index, ccb->dn_line, fname), status_code);
+
+    sip_stop_ack_timer(ccb);
+    fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+    if (fallback_ccb) {
+        sip_regmgr_fallback_generic_timer_stop(fallback_ccb->RetryTimer.timer);
+    }
+    sip_regmgr_check_and_transition(ccb);
+    free_sip_message(response);
+}
+
+/*
+ ** sip_regmgr_ev_stability_check_2xx
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Event handler for the all responses
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_regmgr_ev_stability_check_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "sip_regmgr_ev_stability_check_2xx";
+    fallback_ccb_t *fallback_ccb;
+    sipMessage_t *response = event->u.pSipMessage;
+    int status_code = 0;
+
+    clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+
+    if (sipGetResponseCode(response, &status_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE), ccb->index,
+                          ccb->dn_line, fname);
+        free_sip_message(response);
+        return;
+    }
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received a %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_FALLBACK, ccb->index, ccb->dn_line, fname), status_code);
+
+    fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+    if (!fallback_ccb) {
+        free_sip_message(response);
+        return;
+    }
+    sip_regmgr_fallback_generic_timer_stop(fallback_ccb->RetryTimer.timer);
+    /*
+     * Update stats here ?
+     */
+    if (fallback_ccb->StabilityMsgCount > 0) {
+        fallback_ccb->StabilityMsgCount--;
+    }
+    if (fallback_ccb->StabilityMsgCount) {
+        sip_regmgr_wait_timer_start(fallback_ccb);
+    } else {
+        wan_failure = FALSE;
+        sip_regmgr_check_and_transition(ccb);
+    }
+    free_sip_message(response);
+}
+
+/*
+ ** sip_regmgr_ev_stability_check_tmr_wait
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_regmgr_ev_stability_check_tmr_wait (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "sip_regmgr_ev_stability_check_tmr_wait";
+    fallback_ccb_t *fallback_ccb;
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received event\n",
+             DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->index, ccb->dn_line, fname));
+    fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+    (void) ccsip_register_send_msg(SIP_REG_CANCEL, ccb->index);
+    if (fallback_ccb) {
+        sip_regmgr_retry_timer_start(fallback_ccb);
+    }
+}
+
+/*
+ ** sip_regmgr_ev_token_wait_2xx
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: ccb and event
+ *
+ *  DESCRIPTION: TOKEN_WAIT state, set expire timer and transition
+ *               to IDLE state.
+ *
+ *  RETURNS: none
+ */
+void
+sip_regmgr_ev_token_wait_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "sip_regmgr_ev_token_wait_2xx";
+    fallback_ccb_t *fallback_ccb;
+    sipMessage_t *response;
+    int status_code = 0;
+    CCM_ID fallback_ccm_id;
+    ti_config_table_t *fallback_ccb_entry;
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received event\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->index, ccb->dn_line, fname));
+    response = event->u.pSipMessage;
+    clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+
+    if (sipGetResponseCode(response, &status_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE), ccb->index,
+                          ccb->dn_line, fname);
+        free_sip_message(response);
+        return;
+    }
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received a %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->index, ccb->dn_line, fname), status_code);
+
+    sip_stop_ack_timer(ccb);
+    fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+    if (fallback_ccb) {
+        sip_regmgr_fallback_generic_timer_stop(fallback_ccb->WaitTimer.timer);
+
+    }
+    fallback_ccb_entry = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+    fallback_ccm_id    = fallback_ccb_entry->ti_specific.ti_ccm.ccm_id;
+
+    if (CCM_Fallback_Table.fallback_ccm_entry == NULL) {
+        CCM_Fallback_Table.fallback_ccm_entry = (ti_config_table_t *)
+            ccb->cc_cfg_table_entry;
+        CCM_Fallback_Table.is_idle = FALSE;
+        CCM_Fallback_Table.is_resp = FALSE;
+        CCM_Fallback_Table.ccb = ccb;
+        sip_platform_fallback_ind(fallback_ccm_id);
+
+    } else {
+        sip_reg_sm_change_state(ccb, SIP_REG_STATE_IN_FALLBACK);
+        if (fallback_ccb) {
+            sip_regmgr_retry_timer_start(fallback_ccb);
+        }
+    }
+
+    free_sip_message(response);
+}
+
+/*
+ ** sip_regmgr_ev_cleanup
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: ccb and event
+ *
+ *  DESCRIPTION: cleanup primary
+ *
+ *  RETURNS: none
+ */
+void
+sip_regmgr_ev_cleanup (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "sip_regmgr_ev_cleanup";
+    CCM_ID ccm_id;
+    ti_config_table_t *cfg_table_entry;
+    ccsipCCB_t *line_ccb = NULL;
+    ti_common_t *ti_common;
+
+    CCSIP_DEBUG_REG_STATE("%s:\n", fname);
+    line_ccb = sip_sm_get_ccb_by_index(REG_CCB_START);
+    if (ccb == NULL || line_ccb == NULL) {
+        CCSIP_DEBUG_REG_STATE("%s: invalid ccb or line_ccb\n", fname);
+        return;
+    }
+
+    cfg_table_entry = (ti_config_table_t *) line_ccb->cc_cfg_table_entry;
+    /*
+     * Setup the new active cc
+     */
+    sip_regmgr_setup_new_active_ccb((ti_config_table_t *)
+                                    ccb->cc_cfg_table_entry);
+    sip_regmgr_free_fallback_ccb(ccb);
+    /*
+     * Initiate registering with the new active cc
+     */
+    if (CCM_Fallback_Table.is_resp) {
+        CCSIP_DEBUG_REG_STATE("%s: Register all lines\n", fname);
+        ccsip_register_all_lines();
+        CCM_Fallback_Table.fallback_ccm_entry = NULL;
+    } else {
+        CCSIP_DEBUG_REG_STATE("%s: Register prime line\n", fname);
+        sip_regmgr_register_lines(TRUE, FALSE);
+        CCM_Failover_Table.prime_registered = TRUE;
+    }
+    //set status of  current standby to NONE
+    if (CCM_Active_Standby_Table.standby_ccm_entry) {
+        ti_config_table_t *standby_table_entry;
+
+        standby_table_entry = CCM_Active_Standby_Table.standby_ccm_entry;
+
+        ui_set_ccm_conn_status(standby_table_entry->ti_common.addr_str,
+                               CCM_STATUS_NONE);
+
+    }
+    /*
+     * Setup the new standby.
+     * CCM will close the TCP connection after unregistering from the
+     * current active ccm if connection type is TCP/TLS
+     * Close the TCP connection if one exists and open a new one
+     */
+    if (cfg_table_entry != NULL) {
+        ti_common = &cfg_table_entry->ti_common;
+        if (ti_common->conn_type != CONN_UDP) {
+            if (ti_common->handle != INVALID_SOCKET) {
+                int connid;
+
+                CCSIP_DEBUG_REG_STATE("%s: Close the TCP connection\n", fname);
+                connid = sip_tcp_fd_to_connid(ti_common->handle);
+                sip_tcp_purge_entry(connid);
+                ti_common->handle = INVALID_SOCKET;
+            }
+            CCSIP_DEBUG_REG_STATE("%s: Open a new connection\n", fname);
+            (void) sip_regmgr_ccm_get_conn(line_ccb->dn_line, cfg_table_entry);
+        }
+        ccm_id = cfg_table_entry->ti_specific.ti_ccm.ccm_id;
+        sip_regmgr_setup_new_standby_ccb(ccm_id);
+    }
+    /*
+     * Trigger monitoring the new standby
+     */
+    (void) ccsip_register_send_msg(SIP_REG_CANCEL, REG_BACKUP_CCB);
+
+    sip_platform_set_ccm_status();
+}
+
+/*
+ ** sip_regmgr_ev_token_wait_4xx_n_5xx
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: ccb and event
+ *
+ *  DESCRIPTION: TOKEN_WAIT state, set expire timer and transition
+ *               to IDLE state.
+ *
+ *  RETURNS: none
+ */
+void
+sip_regmgr_ev_token_wait_4xx_n_5xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "sip_regmgr_ev_token_wait_4xx_n_5xx";
+    fallback_ccb_t *fallback_ccb;
+    sipMessage_t   *response;
+    int             status_code = 0;
+    uint32_t        retry_after = 0;
+    const char     *msg_ptr = NULL;
+    int             timeout = 0;
+
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received event\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->index, ccb->dn_line, fname));
+    response = event->u.pSipMessage;
+    clean_method_request_trx(ccb, sipMethodRegister, TRUE);
+
+    if (sipGetResponseCode(response, &status_code) < 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE), ccb->index,
+                          ccb->dn_line, fname);
+        free_sip_message(response);
+        return;
+    }
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received a %d\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->index, ccb->dn_line, fname), status_code);
+
+    sip_stop_ack_timer(ccb);
+    fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+    if (!fallback_ccb) {
+        return;
+    }
+    sip_regmgr_fallback_generic_timer_stop(fallback_ccb->WaitTimer.timer);
+    // Look for retry-after tag
+    if (status_code == SIP_CLI_ERR_BUSY_HERE ||
+        status_code == SIP_SERV_ERR_UNAVAIL) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Received a 486/503 response!\n",
+                              DEB_F_PREFIX_ARGS(SIP_RESP, fname));
+        msg_ptr = sippmh_get_header_val(response,
+                                        (const char *)SIP_HEADER_RETRY_AFTER,
+                                        NULL);
+        if (msg_ptr) {
+            retry_after = strtoul(msg_ptr, NULL, 10);
+        }
+        /*
+         * Start timer with the retry-after value
+         */
+        if (retry_after > 0) {
+            timeout = retry_after;
+        } else {
+            // use keep alive timer if retry_after in 486 is missing or 0
+            timeout = sip_config_get_keepalive_expires();
+        }
+        if (cprStartTimer(fallback_ccb->WaitTimer.timer, timeout * 1000,
+                    fallback_ccb) == CPR_FAILURE) {
+            CCSIP_DEBUG_REG_STATE(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                                  ccb->index, 0, fname, "cprStartTimer");
+            sip_regmgr_generic_timer_start_failure(fallback_ccb,
+                                                   SIP_TMR_REG_WAIT);
+        }
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Started timer with retry-after for %d secs\n",
+                              DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), timeout);
+    } else {
+        /*
+         * Unhandled 4xx message. start monitoring fallback ccb
+         */
+        sip_reg_sm_change_state(ccb, SIP_REG_STATE_IN_FALLBACK);
+        sip_regmgr_retry_timer_start(fallback_ccb);
+    }
+}
+
+void
+sip_regmgr_ev_token_wait_tmr_wait (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    static const char fname[] = "sip_regmgr_ev_token_wait_tmr_wait";
+    fallback_ccb_t *fallback_ccb;
+
+    /*
+     * Send a REFER message requesting for registering with
+     * the callmanager that has come back up. Cleanup any
+     * pending REFER transaction as new REFER is created for
+     * next transmission.
+     */
+    clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+    if (sipSPISendRefer(ccb, TOKEN_REFER_TO, SIP_REF_TOKEN)) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Successfully sent a REFER"
+                              " for token registration!\n", DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname));
+    } else {
+        CCSIP_DEBUG_ERROR(DEB_F_PREFIX"Error while trying to send"
+                              " REFER for token registration!\n", DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname));
+    }
+    fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+    if (fallback_ccb) {
+        sip_regmgr_retry_timer_start(fallback_ccb);
+    }
+}
+
+/*
+ ** sip_regmgr_check_and_transition
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Check and transition states
+ *
+ *  RETURNS: void
+ *
+ */
+void
+sip_regmgr_check_and_transition (ccsipCCB_t *ccb)
+{
+    static const char fname[] = "sip_regmgr_check_and_transition";
+
+    /*
+     * This routine gets called from IN_FALLBACK state when we
+     * receive a response to a keepalive message -or- from the
+     * STABILITY Check state when the Carefree algorithm successfully
+     * completes.
+     * If state is IN_FALLBACK and wan_failure is TRUE, transition
+     * to STABILITY_CHECK state and start the CAREFREE Algorithm.
+     * Else transition to the TOKEN_WAIT State.and send the refer
+     * message requesting for a token to register.
+     */
+    if (!ccb) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Received event for invalid ccb\n", DEB_F_PREFIX_ARGS(SIP_EVT, fname));
+        return;
+    }
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received event\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->index, ccb->dn_line, fname));
+    if (wan_failure) {
+        /*
+         * Kick off the Carefree algorithm. Transition to STABILITY CHECK
+         * state
+         */
+        sip_reg_sm_change_state(ccb, SIP_REG_STATE_STABILITY_CHECK);
+        sip_regmgr_set_stability_total_msgs(
+                sip_regmgr_get_fallback_ccb_by_index(ccb->index));
+        sip_regmgr_wait_timer_start(
+                sip_regmgr_get_fallback_ccb_by_index(ccb->index));
+    } else {
+        /*
+         * Check to see if the ccm coming backup is going to
+         * replace the current active, if not just replace the
+         * current Standby
+         */
+        ti_config_table_t *ccm_table_entry, *fallback_ccb_entry;
+        CCM_ID current_ccm_id, fallback_ccm_id;
+        fallback_ccb_t *fallback_ccb;
+
+        fallback_ccb = sip_regmgr_get_fallback_ccb_by_index(ccb->index);
+        ccm_table_entry = CCM_Active_Standby_Table.active_ccm_entry;
+        current_ccm_id = ccm_table_entry->ti_specific.ti_ccm.ccm_id;
+
+        fallback_ccb_entry = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+        fallback_ccm_id = fallback_ccb_entry->ti_specific.ti_ccm.ccm_id;
+        /*
+         * Check to see if the current ccm coming back up is going to
+         * replace the current active ccm
+         */
+        if (current_ccm_id > fallback_ccm_id) {
+            if ((sip_platform_is_phone_idle()) &&
+                (CCM_Fallback_Table.fallback_ccm_entry == NULL)) {
+                /*
+                 * Send a REFER message requesting for registering with
+                 * the callmanager that has come back up. Cleanup any
+                 * existing REFER transactions before creating a new
+                 * REFER
+                 */
+                clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+                if (sipSPISendRefer(ccb, TOKEN_REFER_TO, SIP_REF_TOKEN)) {
+                    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Successfully sent a REFER"
+                                          " for token registration!\n", DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname));
+                    sip_reg_sm_change_state(ccb, SIP_REG_STATE_TOKEN_WAIT);
+                    if (fallback_ccb) {
+                        sip_regmgr_retry_timer_start(fallback_ccb);
+                    }
+                } else {
+                    CCSIP_DEBUG_ERROR(DEB_F_PREFIX"Error while trying to send"
+                                          " REFER for token registration!\n",
+                                          DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname));
+                }
+            } else {
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"phone not idle or fallback ccm entry non NULL \n",
+                                      DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+                // Send keep alive if phone not idle
+                sip_reg_sm_change_state(ccb, SIP_REG_STATE_IN_FALLBACK);
+                if (fallback_ccb) {
+                    sip_regmgr_retry_timer_start(fallback_ccb);
+                }
+                return;
+            }
+        } else if (current_ccm_id == fallback_ccm_id) {
+            CCSIP_DEBUG_REG_STATE("%s: Current ccm coming back up is the current active ccm\n", fname);
+            // Do nothing
+        } else {
+            /*
+             * Falling back as the standby.
+             * Any CCM coming back up will either replace the
+             * active or standby ccm. In this case it is not the
+             * active, so replace the standby ccm.
+             */
+            ccsipCCB_t *backup_ccb;
+            ti_config_table_t *ccm_table_entry2;
+            ti_ccm_t *ti_ccm;
+            CCM_ID ccm_id;
+
+            ccm_table_entry2 = (ti_config_table_t *)
+                ccb->cc_cfg_table_entry;
+            ti_ccm = &ccm_table_entry2->ti_specific.ti_ccm;
+            ccm_id = ti_ccm->ccm_id;
+
+            backup_ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
+            if (CCM_Active_Standby_Table.standby_ccm_entry) {
+                int16_t trx_index;
+                ti_config_table_t *standby_ccm_entry = NULL;
+                CCM_ID standby_ccmid;
+
+                standby_ccm_entry = CCM_Active_Standby_Table.standby_ccm_entry;
+                standby_ccmid = standby_ccm_entry->ti_specific.ti_ccm.ccm_id;
+
+                // Check to see if the ccm coming back up is going to
+                // replace the current standby ccm.
+                if (standby_ccmid < ccm_id) {
+                    /*
+                     * Clean the current fallback ccb and free it
+                     */
+                    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Freeing the fallback ccb"
+                                          " for %d ccm as current standby"
+                                          " is %d ccm!\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname),
+                                          ccm_id, standby_ccmid);
+                    sip_regmgr_free_fallback_ccb(ccb);
+                    return;
+                }
+                /*
+                 * Check to see if there is a transaction pending.
+                 * If so wait for the final response, else proceed.
+                 */
+                trx_index = get_method_request_trx_index(backup_ccb,
+                                                         sipMethodRegister,
+                                                         TRUE);
+                if (trx_index >= 0) {
+                    if (new_standby_available == NULL) {
+                        new_standby_available = (void *) ccm_table_entry2;
+                    } else {
+                        /*
+                         * There is already a ccm waiting to
+                         * transition into being the standby ccm.
+                         * Check current ccm id with the one
+                         * waiting to see who is a better fit.
+                         */
+                        ti_config_table_t *waiting_standby;
+                        CCM_ID this_ccm_id, waiting_ccm_id;
+
+                        waiting_standby = (ti_config_table_t *)
+                            new_standby_available;
+                        waiting_ccm_id = waiting_standby->
+                            ti_specific.ti_ccm.ccm_id;
+                        this_ccm_id = ccm_table_entry2->
+                            ti_specific.ti_ccm.ccm_id;
+                        if (waiting_ccm_id > this_ccm_id) {
+                            new_standby_available = (void *)
+                                ccm_table_entry2;
+                        }
+                    }
+                } else {
+
+                    ti_config_table_t *standby_ccm_entry2;
+
+                    standby_ccm_entry2 = (ti_config_table_t *)
+                        backup_ccb->cc_cfg_table_entry;
+                    // Update CCM Status
+                    ui_set_ccm_conn_status(standby_ccm_entry2->ti_common.addr_str,
+                                           CCM_STATUS_NONE);
+                    if (standby_ccm_entry2->ti_common.conn_type == CONN_TCP) {
+                        /*
+                         * Clean up the socket of the current standby
+                         * if tcp.
+                         */
+                        int connid;
+
+                        connid = sip_tcp_fd_to_connid(standby_ccm_entry2->
+                                                      ti_common.handle);
+                        sip_tcp_purge_entry(connid);
+                        sipTransportSetServerHandleAndPort(INVALID_SOCKET, 0,
+                                (ti_config_table_t *)(backup_ccb->cc_cfg_table_entry));
+                    }
+                    ui_set_ccm_conn_status(ccm_table_entry2->ti_common.addr_str,
+                                           CCM_STATUS_STANDBY);
+
+                    sip_regmgr_setup_new_standby_ccb(ccm_id);
+                    sip_regmgr_free_fallback_ccb(ccb);
+                    /*
+                     * New standby, start monitoring it.
+                     */
+                    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Start monitoring new"
+                                          " standby cc \n", DEB_F_PREFIX_ARGS(SIP_STANDBY, fname));
+                    (void) ccsip_register_send_msg(SIP_REG_CANCEL,
+                                                   backup_ccb->index);
+                }
+            } else {
+                /*
+                 * Here there is no current standby, so simply
+                 * make the current ccm the standby ccm and start
+                 * monitoring it.
+                 */
+
+                ui_set_ccm_conn_status(ccm_table_entry2->ti_common.addr_str,
+                                       CCM_STATUS_STANDBY);
+
+                sip_regmgr_setup_new_standby_ccb(ccm_id);
+                sip_regmgr_free_fallback_ccb(ccb);
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Start monitoring new"
+                                      " standby cc \n", DEB_F_PREFIX_ARGS(SIP_STANDBY, fname));
+                (void) ccsip_register_send_msg(SIP_REG_CANCEL,
+                                               backup_ccb->index);
+            }
+        }
+    }
+}
+
+/*
+ ** sip_regmgr_ev_failure_response
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_regmgr_ev_failure_response (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char     *fname = "sip_regmgr_ev_failure_response";
+    int             timeout;
+
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received event\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->index, ccb->dn_line, fname));
+    if (ccb->index == REG_BACKUP_CCB) {
+        /*
+         * Keep monitoring.
+         * Stay in unregistering state and wait for the
+         * expires timer to pop to send the next keepalive.
+         */
+
+        timeout = sip_config_get_keepalive_expires();
+
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Starting keep alive timer %d sec\n",
+                              DEB_F_PREFIX_ARGS(SIP_TIMER, fname), timeout);
+        (void) sip_platform_standby_keepalive_timer_start(timeout * 1000);
+
+    } else {
+        // Do not Initiate failover; Need not wait for the the ack_timer
+        if (sip_regmgr_get_cc_mode(1) == REG_MODE_CCM) {
+            config_update_required = TRUE;
+        }
+
+        /*
+         * Send indication of REG_ALLFAIL so platform can
+         * initiate a reboot.
+        */
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Registration rejected.\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+         // Cleanup fallback CCB list
+         sip_regmgr_free_fallback_ccb_list();
+         sip_reg_all_failed = TRUE;
+         sip_regmgr_handle_reg_all_fail();
+
+    }
+}
+
+/*
+ ** sip_regmgr_ev_cancel
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_regmgr_ev_cancel (ccsipCCB_t *ccb, sipSMEvent_t *event)
+{
+    const char *fname = "sip_regmgr_ev_cancel";
+    char user[MAX_LINE_NAME_SIZE];
+
+    /*
+     * If state is TokenWait,
+     * Then transition back into InFallback state.
+     * Else send out a keepalive message to the registration server.
+     * Note: Need to rename the event ???
+     */
+    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Received event\n",
+                          DEB_L_C_F_PREFIX_ARGS(SIP_EVT, ccb->index, ccb->dn_line, fname));
+    sip_util_get_new_call_id(ccb);
+    ccb->authen.cred_type = 0;
+    ccb->retx_counter     = 0;
+    ccb->reg.tmr_expire   = 0;
+    ccb->reg.act_time     = 0;
+    config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line, sizeof(user));
+
+    (void) sipSPISendRegister(ccb, 0, user, 0);
+}
+
+/*
+ ** sip_regmgr_ccm_get_conn
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS:
+ *
+ */
+ti_config_table_t *
+sip_regmgr_ccm_get_conn (line_t dn, ti_config_table_t *ccm_entry)
+{
+    /*
+     * Just check for the validity of the cpr_socket
+     * and return the entry.
+     */
+    if (ccm_entry->ti_common.handle != INVALID_SOCKET) {
+        return (ccm_entry);
+    } else {
+        /*
+         * Create a socket to the ccm
+         */
+        conn_create_status_t conn_status;
+
+        conn_status = sip_transport_setup_cc_conn(dn, ccm_entry->
+                                                  ti_specific.ti_ccm.ccm_id);
+        if (conn_status == CONN_SUCCESS) {
+            return (ccm_entry);
+        } else {
+            return (NULL);
+        }
+    }
+}
+
+/*
+ ** sip_regmgr_setup_cc_conns
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: void
+ *
+ *  DESCRIPTION: Replaces SIPTaskConnectToSipProxies
+ *
+ *  RETURNS:
+ *
+ */
+static int
+sip_regmgr_setup_cc_conns ()
+{
+    const char *fname = "sip_regmgr_setup_cc_conns";
+    RET_CODE ret_code = RET_SUCCESS;
+    CCM_ID cc_index;
+    cpr_socket_t active_fd = INVALID_SOCKET,
+                 standby_fd = INVALID_SOCKET;
+    line_t line;
+    conn_create_status_t conn_status;
+
+    /*
+     * We assume only one type of call control in the phone
+     * either ccm / csps. That is the reason for only one check
+     * When we support both ccm and csps on the same phone (on
+     * multiple lines we will need to check each line's cc_type
+     * and take the appropriate action.
+     */
+    if (CC_Config_Table[0].cc_type == CC_CCM) {
+        /*
+         * Find the active and standby connections for
+         * ccm.
+         */
+        for (cc_index = PRIMARY_CCM; cc_index < MAX_CCM; cc_index++) {
+            line = 1;
+
+            /*
+             * Note: The opening of sockets will fail only in the
+             * case of tcp/tls connections. In the case of udp,
+             * the socket will get opened and the failure to reach
+             * the destination will come in the form of timeouts/
+             * icmp unreachable messages.
+             */
+            conn_status = sip_transport_setup_cc_conn(line, cc_index);
+            if (conn_status == CONN_SUCCESS) {
+                if (active_fd == INVALID_SOCKET) {
+                    /*
+                     * Found Active fd and active_fd is not set
+                     * Save it for all lines.
+                     */
+                    active_fd = CCM_Config_Table[line - 1][cc_index]->ti_common.handle;
+                    set_active_ccm(CCM_Config_Table[line - 1][cc_index]);
+                } else {
+                    /*
+                     * Found Standby fd. Save it in the local table.
+                     * and break out of loop.
+                     */
+                    standby_fd = CCM_Config_Table[line - 1]
+                        [cc_index]->ti_common.handle;
+                    CCM_Active_Standby_Table.standby_ccm_entry =
+                        CCM_Config_Table[line - 1][cc_index];
+                    break;
+                }
+            } else if (conn_status == CONN_FAILURE) {
+                /*
+                 * We can  get this return code when connecting to
+                 * a server that uses UDP such as Asterisk. If that is the
+                 * case we then change the connection type to UDP and
+                 * attempt reconnection.
+                 *
+                 */
+
+               // change trans layer protocol to UDP
+               CC_Config_setIntValue(CFGID_TRANSPORT_LAYER_PROT, 2);
+               CCSIP_DEBUG_ERROR("%s: Attempting reconnection using UDP\n", fname);
+
+               // attempt re-connection
+               sipTransportInit();
+
+               conn_status = sip_transport_setup_cc_conn(line, cc_index);
+                if (conn_status == CONN_SUCCESS) {
+                    if (active_fd == INVALID_SOCKET) {
+                        /*
+                         * Found Active fd and active_fd is not set
+                         * Save it for all lines.
+                         */
+                        active_fd = CCM_Config_Table[line - 1][cc_index]->ti_common.handle;
+                        set_active_ccm(CCM_Config_Table[line - 1][cc_index]);
+                    } else {
+                        /*
+                         * Found Standby fd. Save it in the local table.
+                         * and break out of loop.
+                         */
+                        standby_fd = CCM_Config_Table[line - 1]
+                            [cc_index]->ti_common.handle;
+                        CCM_Active_Standby_Table.standby_ccm_entry =
+                            CCM_Config_Table[line - 1][cc_index];
+                        break;
+                    }
+                } else if (conn_status == CONN_FAILURE) {
+
+                       /*
+                        * We get this return code when we have the address
+                        * configured and we are not able to setup the
+                        * connection to the ccm.
+                        * Candidate for fallback monitoring. Note it in the
+                        * ccm_config_table and add it to a fallback monitor
+                        * table.
+                        *
+                        */
+                       CCSIP_DEBUG_ERROR("%s: Socket open failure: DN <%d> CCM <%d>\n",
+                                       fname, line, cc_index);
+                       (void) sip_regmgr_create_fallback_ccb(cc_index, line);
+                       ret_code = RET_START_FALLBACK;
+                }
+            }
+        }
+        if (active_fd == INVALID_SOCKET) {
+            /*
+             * No CC present for any calls from this phone.
+             */
+            CCSIP_DEBUG_ERROR("%s: NO CALL CONTROL AVAILABLE! Init a reboot!\n",
+                              fname);
+            set_active_ccm(&CCM_Dummy_Entry);
+
+            /*
+             * RegMgr Interface call, ALL_FAIL
+             */
+            ret_code = RET_INIT_REBOOT;
+        } else if (standby_fd == INVALID_SOCKET) {
+            /*
+             * No Standby CC present for this phone.
+             */
+            CCSIP_DEBUG_ERROR("%s: NO VALID STANDBY CALL CONTROL AVAILABLE!\n",
+                              fname);
+            ret_code = RET_NO_STANDBY;
+        }
+    } else {
+        /*
+         * Non CCM Case
+         */
+        for (line = 1; line <= MAX_REG_LINES; line++) {
+            conn_status = sip_transport_setup_cc_conn(line, UNUSED_PARAM);
+        }
+    }
+    return (ret_code);
+}
+
+/*
+ ** sip_regmgr_destroy_cc_conns
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Replaces SIPTaskDisconnectFromSipProxies
+ *
+ *  RETURNS:
+ *
+ */
+int
+sip_regmgr_destroy_cc_conns (void)
+{
+    const char *fname = "sip_regmgr_destroy_cc_conns";
+    line_t dn, max_iteration;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Destroying connections\n", DEB_F_PREFIX_ARGS(SIP_CC_CONN, fname));
+    if (CC_Config_Table[LINE1].cc_type == CC_CCM) {
+        /*
+         * The first iteration of calling sip_transport_destroy_cc_conn
+         * will close all the connections entries for pri, sec, tertiary
+         * connections.
+         */
+        max_iteration = 1;
+    } else {
+        max_iteration = MAX_REG_LINES;
+    }
+
+    for (dn = 1; dn <= max_iteration; dn++) {
+        /*
+         * Not checking if it is the active ccm connection that is getting torn down.
+         * Here we are killing everything so does'nt matter.
+         * Something else has triggered the closing of the tcp conns.
+         * So the unreg reason should be set by now.
+         * In theory we should never see this getting set.
+         */
+        sip_transport_destroy_cc_conn(dn, PRIMARY_CCM);
+    }
+    return (0);
+}
+
+/*
+ ** sip_regmgr_init
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS:
+ *
+ */
+int
+sip_regmgr_init (void)
+{
+    RET_CODE ret_code = RET_SUCCESS;
+
+    /*
+     * Create the fallback ccb link list, that will hold
+     * the fallback ccb's of the failed ccm's
+     */
+       fallback_ccb_list = sll_create(sip_regmgr_find_fallback_ccb);
+    ret_code = (RET_CODE) sip_regmgr_setup_cc_conns();
+    if (ret_code == RET_START_FALLBACK || ret_code == RET_NO_STANDBY) {
+        sip_regmgr_trigger_fallback_monitor();
+    } else if (ret_code == RET_INIT_REBOOT) {
+        /*
+         * Send indication of REG_ALLFAIL so platform can
+         * initiate a reboot.
+         */
+        // Cleanup fallback CCB list
+        sip_regmgr_free_fallback_ccb_list();
+        sip_reg_all_failed = TRUE;
+        sip_regmgr_handle_reg_all_fail();
+        return (SIP_ERROR);
+    }
+    CCM_Fallback_Table.fallback_ccm_entry = NULL;
+    CCM_Fallback_Table.is_idle = FALSE;
+    CCM_Fallback_Table.is_resp = FALSE;
+    CCM_Failover_Table.failover_started = FALSE;
+    sip_reg_all_failed = FALSE;
+    retry_times = 0;
+
+    return (SIP_OK);
+}
+
+
+/*
+ ** sip_regmgr_shutdown
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Unregister all lines and clean CCBs
+ *
+ *  RETURNS: none
+ *
+ */
+void
+sip_regmgr_shutdown (void)
+{
+    const char *fname = "sip_regmgr_shutown";
+    fallback_ccb_t *fallback_ccb = NULL;
+    line_t ndx = 0;
+    ccsipCCB_t *ccb;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+
+
+    // Shutdown registration
+    ccsip_register_shutdown();
+
+    // Cleanup fallback CCB list
+    while ((fallback_ccb = (fallback_ccb_t *)sll_next(fallback_ccb_list,
+                    NULL)) != NULL) {
+        sip_regmgr_clean_fallback_ccb(fallback_ccb);
+        (void) sll_remove(fallback_ccb_list, fallback_ccb);
+        cpr_free(fallback_ccb);
+    }
+    sll_destroy(fallback_ccb_list);
+    fallback_ccb_list = NULL;
+
+    for (ndx = REG_CCB_START; ndx <= REG_BACKUP_CCB; ndx++) {
+        ccb = sip_sm_get_ccb_by_index(ndx);
+        if (ccb != NULL) {
+            ccb->sipCallID[0] = '\0';
+        }
+    }
+
+    retry_times = 0;
+    set_active_ccm(NULL);
+    CCM_Active_Standby_Table.standby_ccm_entry = NULL;
+}
+
+/*
+ ** sip_regmgr_failover_rsp_start
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: none
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS: none
+ *
+ */
+void
+sip_regmgr_failover_rsp_start (void)
+{
+    const char *fname = "sip_regmgr_failover_rsp_start";
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+    /*
+     * Received response start
+     * Change the ip addr and port to point to the new ccm
+     * Set the CC_CONFIG_TABLE entry for all the lines to
+     * point to the new ccm.
+     * Set the ccb->cc_cfg_table_entry with the new entry.
+     */
+    sip_regmgr_setup_new_active_ccb(CCM_Failover_Table.failover_ccm_entry);
+    /*
+     * Monitoring of standby node will start when we get a 200 ok for the new
+     * active cucm, or, in case new active fails, then the stanby will be moved
+     * up to become new active cucm, and will come to this function again.
+     */
+    if (ccsip_register_get_register_state() == SIP_REG_NO_STANDBY) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Unable to get new standby ccm !\n", DEB_F_PREFIX_ARGS(SIP_STANDBY, fname));
+    }
+
+    sip_regmgr_register_lines(TRUE, FALSE);
+    // start notify timer
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"START TIMER \n", DEB_F_PREFIX_ARGS(SIP_TIMER, fname));
+    (void) sip_platform_notify_timer_start(5000);
+    CCM_Failover_Table.prime_registered = TRUE;
+}
+
+/*
+ ** sip_regmgr_failover_rsp_complete
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS: none
+ *
+ */
+void
+sip_regmgr_failover_rsp_complete (void)
+{
+    const char *fname = "sip_regmgr_failover_complete";
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname));
+    /*
+     * Received response complete
+     */
+    // stop notify timer
+    (void) sip_platform_notify_timer_stop();
+    CCM_Failover_Table.failover_started = FALSE;
+    sip_platform_cc_mode_notify();
+    sip_regmgr_register_lines(FALSE, FALSE);
+    sip_regmgr_update_call_ccb();
+    sip_platform_set_ccm_status();
+    sip_regmgr_trigger_fallback_monitor();
+    CCM_Failover_Table.prime_registered = FALSE;
+}
+
+void
+sip_regmgr_register_lines (boolean prime_only, boolean skip_prime)
+{
+    static const char fname[] = "sip_regmgr_register_lines";
+    ccsipCCB_t *ccb = 0;
+    line_t ndx;
+    line_t line_end;
+    line_t line_start;
+    char address[MAX_IPADDR_STR_LEN];
+    ti_config_table_t *standby_ccm;
+
+    line_end = 1;
+    if ((!CCM_Failover_Table.prime_registered && !skip_prime) || prime_only) {
+        line_start = REG_CCB_START;
+    } else {
+        line_start = REG_CCB_START + 1;
+    }
+    if (prime_only) {
+        line_end = REG_CCB_START;
+    } else {
+        line_end += TEL_CCB_END;
+        ccb = sip_sm_get_ccb_by_index(REG_CCB_START);
+        if (ccb->reg.registered ) {
+            ui_set_sip_registration_state(ccb->dn_line, TRUE);
+        }
+    }
+
+    if (line_start == REG_CCB_START) {
+        ccsip_register_set_register_state(SIP_REG_REGISTERING);
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"registering prime line \n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+        /*
+         * regmgr - Set the Backup (standby ccm) info.
+         */
+        standby_ccm = CCM_Active_Standby_Table.standby_ccm_entry;
+        if (standby_ccm) {
+            ti_ccm_t *ti_ccm = &standby_ccm->ti_specific.ti_ccm;
+
+            sip_regmgr_setup_new_standby_ccb(ti_ccm->ccm_id);
+        } else {
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"ERROR: Standby ccm entry is NULL\n",
+                                  DEB_F_PREFIX_ARGS(SIP_STANDBY, fname));
+        }
+    }
+
+    for (ndx = line_start; ndx <= line_end; ndx++) {
+        if (sip_config_check_line((line_t) (ndx - TEL_CCB_END))) {
+            ccb = sip_sm_get_ccb_by_index(ndx);
+            if (ccb) {
+                CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d, 0x%x\n",
+                                      DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname),
+                                      ndx, ccb);
+
+                ui_set_sip_registration_state(ccb->dn_line, FALSE);
+
+                sip_sm_call_cleanup(ccb);
+                sipTransportGetPrimServerAddress(ccb->dn_line, address);
+
+                sstrncpy(ccb->reg.proxy, address, MAX_IPADDR_STR_LEN);
+
+                ccb->reg.addr = ccb->dest_sip_addr;
+                ccb->reg.port = (uint16_t) ccb->dest_sip_port;
+
+                if (ccb->index == REG_CCB_START) {
+                   ui_update_registration_state_all_lines(FALSE);
+                   ccb->send_reason_header = TRUE;
+                } else {
+                    ccb->send_reason_header = FALSE;
+                }
+
+                if (ccsip_register_send_msg(SIP_REG_REQ, ndx) != SIP_REG_OK) {
+                    ccsip_register_cleanup(ccb, TRUE);
+                }
+            }
+        }
+    }
+    sip_platform_set_ccm_status();
+}
+
+/*
+ ** sip_regmgr_phone_idle
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_regmgr_phone_idle (boolean waited)
+{
+    const char *fname = "sip_regmgr_phone_idle";
+    ccsipCCB_t *ccb;
+
+    /*
+     * Received response
+     */
+    CCM_Fallback_Table.is_idle = TRUE;
+    // if waited = TRUE send refer and change to token wait
+    // and send ui unlock
+    // otherwise start fallback
+    if (waited) {
+        platform_reg_fallback_cfm();
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX" waited TRUE\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+        CCM_Fallback_Table.fallback_ccm_entry = NULL;
+        sip_regmgr_send_refer(CCM_Fallback_Table.ccb);
+
+    } else {
+        /*
+         * Cancel reg from current active ccm
+         */
+        ccsip_register_cancel(TRUE, FALSE);
+
+        ccb = CCM_Fallback_Table.ccb;
+        if (ccsip_register_send_msg(SIP_REG_CLEANUP, ccb->index) != SIP_REG_OK) {
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"failed to send SIP_REG_CLEANUP\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+        }
+        (void) sip_platform_notify_timer_start(5000);
+    }
+}
+
+/*
+ ** sip_regmgr_fallback_rsp
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS: none
+ *
+ */
+void
+sip_regmgr_fallback_rsp (void)
+{
+    const char *fname = "sip_regmgr_fallback_rsp";
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Entered\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+    /*
+     * Received response
+     */
+    (void) sip_platform_notify_timer_stop();
+    CCM_Fallback_Table.is_resp = TRUE;
+    sip_platform_cc_mode_notify();
+    if (CCM_Fallback_Table.fallback_ccm_entry) {
+        sip_regmgr_register_lines(FALSE, FALSE);
+        CCM_Fallback_Table.fallback_ccm_entry = NULL;
+    }
+    sip_regmgr_update_call_ccb();
+    CCM_Failover_Table.prime_registered = FALSE;
+}
+
+void
+sip_regmgr_rsp (int rsp_id, int rsp_type, boolean waited)
+{
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"rsp id=%s rsp type=%s waited=%d\n",
+        DEB_F_PREFIX_ARGS(SIP_RESP, "sip_regmgr_rsp"),
+        rsp_id == FAILOVER_RSP ? "FAILOVER_RSP" : "FALLBACK_RSP",
+        rsp_type == RSP_START ? "RSP_START" : "RSP_COMPLETE", waited);
+
+    if (rsp_type == RSP_START) {
+        if (rsp_id == FAILOVER_RSP) {
+            sip_regmgr_failover_rsp_start();
+            // Inform the Sub/Not manager that platform is about to failover.
+            (void) sip_subsManager_rollover();
+            publish_reset();
+        } else {
+            sip_regmgr_phone_idle(waited);
+            /*
+             * Inform the Sub/Not manager that platform is about to fallback.
+             * so that applications can clean up before re-register with the
+             * primary CCM.
+             */
+            (void) sip_subsManager_rollover();
+            publish_reset();
+        }
+    } else if (rsp_type == RSP_COMPLETE) {
+
+        SIPTaskReinitialize(TRUE);
+
+        if (rsp_id == FAILOVER_RSP) {
+            sip_regmgr_failover_rsp_complete();
+        } else {
+            sip_regmgr_fallback_rsp();
+        }
+    }
+}
+
+/*
+ *  Get the config address based on call manager id
+ *
+ *  @param ccm_id : callmanager idex
+ *         addr_str: address of allocated space to return
+ *                   the address in string format.
+ *
+ *  @return  none
+ *
+ *  @pre     (addr_str != NULL) (1< ccm_id < 4)
+ *
+ */
+
+void sip_regmgr_get_config_addr (int ccm_id, char *addr_str) {
+
+#ifdef IPV6_STACK_ENABLED
+    int     ip_mode = CPR_IP_MODE_IPV4;
+
+
+
+    config_get_value(CFGID_IP_ADDR_MODE,
+                         &ip_mode, sizeof(ip_mode));
+    if (ip_mode == CPR_IP_MODE_IPV4) {
+#endif
+        config_get_value(ccm_config_id_addr_str[ccm_id], addr_str,
+                        MAX_IPADDR_STR_LEN);
+#ifdef IPV6_STACK_ENABLED
+    } else if (ip_mode == CPR_IP_MODE_IPV6) {
+
+        config_get_value(ccm_config_id_ipv6_addr_str[ccm_id], addr_str,
+                             MAX_IPADDR_STR_LEN);
+    } else if (ip_mode == CPR_IP_MODE_DUAL) {
+
+        config_get_value(ccm_config_id_ipv6_addr_str[ccm_id], addr_str,
+                             MAX_IPADDR_STR_LEN);
+        /* Both mode get IPv6 first if not available get ipv4 */
+        if (addr_str[0] == NUL) {
+            config_get_value(ccm_config_id_addr_str[ccm_id], addr_str,
+                             MAX_IPADDR_STR_LEN);
+        }
+    }
+#endif
+}
+
+/*
+ ** sip_regmgr_check_config_change
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:None
+ *
+ *  DESCRIPTION: Check if there is a config change in transport protocol
+ *  or CCM address
+ *
+ *  RETURNS: TRUE if config changed
+ *
+ */
+boolean
+sip_regmgr_check_config_change (void)
+{
+    const char *fname = "sip_regmgr_check_config_change";
+    ti_common_t ti_common;
+    CCM_ID ccm_id;
+    ti_common_t *active_ti_common;
+#ifdef IPV6_STACK_ENABLED
+    int        ip_mode = CPR_IP_MODE_IPV4;
+
+    config_get_value(CFGID_IP_ADDR_MODE,
+                     &ip_mode, sizeof(ip_mode));
+#endif
+
+    {
+        uint32_t  transport_prot;
+        CONN_TYPE configured_conn = CONN_NONE;
+
+        config_get_value(CFGID_TRANSPORT_LAYER_PROT, &transport_prot,
+                         sizeof(transport_prot));
+        configured_conn = CCM_Device_Specific_Config_Table[PRIMARY_CCM].ti_common.configured_conn_type;
+        if (transport_prot != (uint32_t) configured_conn) {
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"protocol%d conn type %d\n", DEB_F_PREFIX_ARGS(SIP_CONFIG, fname),
+                                  transport_prot, configured_conn);
+            return (TRUE);
+        }
+    }
+    for (ccm_id = PRIMARY_CCM; ccm_id < MAX_CCM; ccm_id++) {
+
+        sip_regmgr_get_config_addr(ccm_id, ti_common.addr_str);
+
+        active_ti_common = &CCM_Device_Specific_Config_Table[ccm_id].ti_common;
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"CCM config%s active %s\n", DEB_F_PREFIX_ARGS(SIP_CONFIG, fname),
+                              ti_common.addr_str, active_ti_common->addr_str);
+        if (strncmp(ti_common.addr_str, active_ti_common->addr_str,
+                    strlen(active_ti_common->addr_str)) != 0) {
+            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"CCM changed\n", DEB_F_PREFIX_ARGS(SIP_CONFIG, fname));
+            return (TRUE);
+        }
+
+    }
+    return (FALSE);
+}
+
+/*
+ ** sip_regmgr_process_config_change
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:None
+ *
+ *  DESCRIPTION: Shut down and re-init sip
+ *
+ *  RETURNS: None
+ *
+ */
+void
+sip_regmgr_process_config_change (void)
+{
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"!!!Process_config_change\n",
+                          DEB_F_PREFIX_ARGS(SIP_CONFIG, "sip_regmgr_process_config_change"));
+
+    /* Register manager configuration changes need to restart sip task */
+    sip_shutdown_phase1(FALSE, UNREG_NO_REASON);
+}
+
+/*
+ ** sip_regmgr_clean_standby_ccb
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: None
+ *
+ *  DESCRIPTION: Clean standby CCB
+ *
+ *  RETURNS: None
+ *
+ */
+void
+sip_regmgr_clean_standby_ccb (ccsipCCB_t *ccb)
+{
+
+    if (ccb) {
+        ccb->reg.proxy[0] = '\0';
+        (void) sip_platform_register_expires_timer_stop(ccb->index);
+        sip_stop_ack_timer(ccb);
+        ccb->reg.port = 0;
+    }
+}
+
+/*
+ ** sip_regmgr_send_refer
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:ccb
+ *
+ *  DESCRIPTION:Send a REFER message requesting for registering with the CCM
+ *
+ *  RETURNS: None
+ *
+ */
+void
+sip_regmgr_send_refer (ccsipCCB_t *ccb)
+{
+    static const char fname[] = "sip_regmgr_send_refer";
+
+    /*
+     * Send a REFER message requesting for registering with
+     * the callmanager. Cleanup any
+     * pending REFER transaction as new REFER is created for
+     * next transmission.
+     */
+    clean_method_request_trx(ccb, sipMethodRefer, TRUE);
+    if (sipSPISendRefer(ccb, TOKEN_REFER_TO, SIP_REF_TOKEN)) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Successfully sent a REFER"
+                              " for token registration!\n", DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname));
+        sip_reg_sm_change_state(ccb, SIP_REG_STATE_TOKEN_WAIT);
+    } else {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Error while trying to send"
+                              " REFER for token registration!\n", DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname));
+    }
+}
+
+/*
+ ** sip_regmgr_update_call_ccb
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:ccb
+ *
+ *  DESCRIPTION:Update call ccb after failover/fallback
+ *
+ *  RETURNS: None
+ *
+ */
+static void
+sip_regmgr_update_call_ccb (void)
+{
+    line_t i;
+    ccsipCCB_t *ccb = NULL;
+
+    for (i = TEL_CCB_START; i <= TEL_CCB_END; i++) {
+        ccb = sip_sm_get_ccb_by_index(i);
+        if (ccb) {
+            ccb->local_port = sipTransportGetListenPort(ccb->dn_line, NULL);
+            sipTransportGetServerIPAddr(&(ccb->dest_sip_addr), ccb->dn_line);
+            ccb->dest_sip_port = sipTransportGetPrimServerPort(ccb->dn_line);
+        }
+    }
+}
+
+/*
+ *  Function: sip_regmgr_ccm_restarted
+ *
+ *  Parameters:
+ *      new_reg_ccb  - pointer to ccsipCCB_t of REG CCB that has seen
+ *                     by CCM as a new REG.
+ *
+ *  Description:
+ *      The function handles CCM has restarted condition.
+ *  The phone needs to clean up internal state that may be left
+ *  before CCM has gone down and come right up. This condition can
+ *  be seen when using UDP for transport and the CCM has gone down
+ *  between REG refresh cycles.
+ *
+ *  Returns:
+ *      None
+ */
+void
+sip_regmgr_ccm_restarted (ccsipCCB_t *new_reg_ccb)
+{
+    const char *fname = "sip_regmgr_ccm_restarted";
+    ccsipCCB_t *ccb;
+    line_t ndx, line_end;
+
+    if ((new_reg_ccb == NULL) || (new_reg_ccb->index == REG_BACKUP_CCB)) {
+        /* No ccb or it is back up CCB, ignore this one */
+        return;
+    }
+
+    /* Inform all applications that hat may subscribe to the CCM/Proxy */
+    (void) sip_subsManager_reset_reg();
+    /*
+     * If there are more than one line registered to this CCM/Proxy,
+     * start re-register the rest of the lines quickly.
+     */
+    line_end = 1;
+    /*
+     * REG CCBs start after the TEL CCBs.
+     * So, line_end equals the number of lines and then add the TEL_CCB_END
+     * to get the ending of the REG CCBs
+     */
+    line_end += TEL_CCB_END;
+
+    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
+        ccb = sip_sm_get_ccb_by_index(ndx);
+        if (!sip_config_check_line((line_t)(ndx - TEL_CCB_END)) ||
+            !ccb || (ccb == new_reg_ccb) ||
+            (ccb->state != (int) SIP_REG_STATE_REGISTERED) ||
+            (util_compare_ip(&(ccb->reg.addr), &(new_reg_ccb->reg.addr)) == FALSE)) {
+            /*
+             * Skip the CCB for the line that
+             * 1) is not configured or
+             * 2) is the same as the one that detects restarts or
+             * 3) is not registered or
+             * 4) the proxy/CCM address is not the same one as the
+             *    one that is seeing new registration indication.
+             *    (do not restart other that is not register to the
+             *    same server).
+             */
+            continue;
+        }
+
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Re-register %d\n", DEB_F_PREFIX_ARGS(SIP_REG, fname), ccb->dn_line);
+        /*
+         * This line still registers before the CCM went down and came back
+         * up. Do minimum resetting to the ccb just enough for it to
+         * send REG out to the CCM.
+         */
+
+        /* Set state back to idle */
+        sip_reg_sm_change_state(ccb, SIP_REG_STATE_IDLE);
+
+        /* Clear Registered flag */
+        ccb->reg.registered = 0;
+
+        /* Restart the register timer */
+        (void) sip_platform_register_expires_timer_start(ccb->reg.tmr_expire *
+                                                         1000, ccb->index);
+
+        /* Update UI to indicates that this line is not register */
+        ui_set_sip_registration_state(ccb->dn_line, FALSE);
+
+        /* Send REG out to the CCM */
+        if (ccsip_register_send_msg(SIP_REG_REQ, ndx) != SIP_REG_OK) {
+            ccsip_register_cleanup(ccb, TRUE);
+        }
+    }
+}
+
+/*
+ ** sip_regmgr_notify_timer_callback
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS: None
+ *
+ *  DESCRIPTION: Missed notify from CCM during failover/fallback
+ *
+ *  RETURNS: None
+ *
+ */
+void
+sip_regmgr_notify_timer_callback (void *data)
+{
+    const char *fname = "sip_regmgr_notify_timer_callback";
+    ccsipCCB_t *ccb;
+    sipServiceControl_t *scp = NULL;
+    const char *versionStamp = "0";
+    int versionStampLen;
+
+    ccb = sip_sm_get_ccb_by_index(REG_CCB_START);
+    if (ccb->reg.registered) {
+        // Prime line in registered state. Missed notify from ccm.
+        // fake a notify message to unlock UI, Use value 0 so that
+        // platform will download the config
+        scp = (sipServiceControl_t *)
+            cpr_calloc(1, sizeof(sipServiceControl_t));
+        if (scp) {
+            versionStampLen = strlen(versionStamp);
+            scp->action = SERVICE_CONTROL_ACTION_CHECK_VERSION;
+            scp->configVersionStamp = (char *)
+                cpr_calloc(1, versionStampLen + 1);
+            scp->dialplanVersionStamp = (char *)
+                cpr_calloc(1, versionStampLen + 1);
+            scp->softkeyVersionStamp = (char *)
+                cpr_calloc(1, versionStampLen + 1);
+
+            if (!scp->configVersionStamp ||
+                !scp->dialplanVersionStamp ||
+                !scp->softkeyVersionStamp) {
+                CCSIP_DEBUG_ERROR("%s: malloc failed\n", fname);
+            } else {
+                sstrncpy(scp->configVersionStamp, versionStamp, versionStampLen + 1);
+                sstrncpy(scp->dialplanVersionStamp, versionStamp, versionStampLen + 1);
+                sstrncpy(scp->softkeyVersionStamp, versionStamp, versionStampLen + 1);
+                sip_platform_handle_service_control_notify(scp);
+                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Fake NOTIFY TO Platform\n",
+                                      DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+            }
+            sippmh_free_service_control_info(scp);
+        }
+    } else {
+        // We should not get here. If so then some problem
+        // Add debug message for the time being
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"PRIME LINE UNREGISTRED STATE, UI LOCK!!!\n",
+                              DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+    }
+}
+
+
+/*
+ ** sip_regmgr_replace_standby
+ *
+ *  PARAMETERS: ccb
+ *
+ *  DESCRIPTION: Replace current standby with new one
+ *
+ *  RETURNS: None
+ *
+ */
+void
+sip_regmgr_replace_standby (ccsipCCB_t *ccb)
+{
+    const char *fname = "sip_regmgr_replace_standby";
+    ti_config_table_t *cfg_table_entry;
+    ti_common_t *ti_common;
+    ccsipCCB_t *ccb_of_fallback;
+    ti_config_table_t *standby_ccm_entry;
+
+    if (!new_standby_available) {
+        return;
+    }
+    /*
+     * Will get in here if a fallback of standby
+     * occurs when the current standby has an
+     * outstanding transaction in process. We wait
+     * till the transaction is complete.
+     */
+
+    // Update CCM Status
+    standby_ccm_entry = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+
+    ui_set_ccm_conn_status(standby_ccm_entry->ti_common.addr_str,
+                           CCM_STATUS_NONE);
+
+    cfg_table_entry = (ti_config_table_t *) new_standby_available;
+    ti_common = &cfg_table_entry->ti_common;
+    sip_regmgr_setup_new_standby_ccb(cfg_table_entry->ti_specific.ti_ccm.ccm_id);
+    if (sip_regmgr_find_fallback_ccb_by_addr_port(&(ti_common->addr),
+                ti_common->port, &ccb_of_fallback)) {
+        sip_regmgr_free_fallback_ccb(ccb_of_fallback);
+    } else {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Unable to find fallback"
+                              " ccb to free\n", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname));
+    }
+    /*
+     * New standby, start monitoring it.
+     */
+
+    ui_set_ccm_conn_status(cfg_table_entry->ti_common.addr_str,
+                           CCM_STATUS_STANDBY);
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Start monitoring new standby ccb\n", DEB_F_PREFIX_ARGS(SIP_STANDBY, fname));
+    (void) ccsip_register_send_msg(SIP_REG_CANCEL, ccb->index);
+    new_standby_available = NULL;
+}
+/**
+ ** sip_regmgr_regallfail_timer_callback
+ *  Callback function called when the reg-all-fail timer expires
+ *
+ *  @param  void*  data
+ *
+ *  @return void
+ *
+ */
+void
+sip_regmgr_regallfail_timer_callback (void *data)
+{
+    const char *fname = "sip_regmgr_regallfail_timer_callback";
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Registration Failed. Restarting the System now!\n",
+                          DEB_F_PREFIX_ARGS(SIP_REG, fname));
+    sip_regmgr_send_status(REG_SRC_SIP, REG_ALL_FAIL);
+}
+
+/**
+ ** sip_regmgr_handle_reg_all_fail
+ *  Handles the scenario when all the Registration attempts fail.
+ *
+ *  @param  none
+ *
+ *  @return void
+ *
+ */
+void
+sip_regmgr_handle_reg_all_fail (void)
+{
+    const char *fname = "sip_regmgr_handle_reg_all_fail";
+    line_t     line_end, ndx;
+    CCM_ID     ccm_index;
+    ccsipCCB_t *ccb;
+    unsigned long msec;
+    unsigned long high_regfailtime = 180000;
+    unsigned long low_regfailtime = 120000;
+    char          tmp_str[STATUS_LINE_MAX_LEN];
+
+  CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"All registration attempts failed.\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+
+    /* Start the timer and update UI only if no calls are in progress on the
+     * phone.*/
+    if (sip_platform_is_phone_idle()) {
+        for (ccm_index = PRIMARY_CCM; ccm_index < MAX_CCM; ccm_index++) {
+            if (0 != strcmp (CCM_Config_Table[0][ccm_index]->ti_common.addr_str,
+                              ""))  {
+                ui_set_ccm_conn_status(CCM_Config_Table[0][ccm_index]->
+                                       ti_common.addr_str, CCM_STATUS_NONE);
+            }
+        }
+        line_end = 1;
+        line_end += TEL_CCB_END;
+        for (ndx = REG_CCB_START; ndx <= line_end; ndx++){
+            if (sip_config_check_line((line_t) (ndx - TEL_CCB_END))) {
+                ccb = sip_sm_get_ccb_by_index(ndx);
+                if (!ccb) {
+                    continue;
+                }
+                ui_set_sip_registration_state(ccb->dn_line, FALSE);
+            }
+        }
+
+        /*
+         * Start regallfial with 100 msec timer once,
+         * After that get a Random time between high (3 mins)
+         * and low (2 mins) and start the timer with that time.
+         */
+        if (!regall_fail_attempt) {
+               msec = REGALL_FAIL_TIME;
+        } else {
+               msec = cpr_rand()%(high_regfailtime - low_regfailtime);
+               msec += low_regfailtime;
+        }
+
+        regall_fail_attempt = TRUE;
+
+        sip_platform_reg_all_fail_timer_start(msec);
+        if (platGetPhraseText(STR_INDEX_REGISTERING,
+                              (char *)tmp_str,
+                              STATUS_LINE_MAX_LEN - 1) == CPR_SUCCESS) {
+               ui_set_notification(CC_NO_LINE, CC_NO_CALL_ID, tmp_str, msec/1000, TRUE, DEF_NOTIFY_PRI);
+        }
+    } else {
+        /* Send the Indication to the platform immediately */
+        sip_regmgr_send_status(REG_SRC_SIP, REG_ALL_FAIL);
+    }
+}
+
+/*
+ ** notify_register_update
+ *
+ *  PARAMETERS: last_available_line - last available line button on the phone
+ *
+ *  DESCRIPTION: This function is used to notify the sip task to handle
+ *               any registration updates when sidecars are plugged in or
+ *               removed
+ *
+ *  RETURNS: None
+ *
+ */
+void notify_register_update(int last_line_available)
+{
+    static const char fname[] = "notify_register_update";
+
+    if (ccsip_register_send_msg(SIP_REG_UPDATE, (line_t)last_line_available) != SIP_REG_OK) {
+        CCSIP_DEBUG_ERROR("%s : Unable to send register update message\n", fname);
+    } else {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"last_available_line: %d\n",
+                              DEB_F_PREFIX_ARGS(SIP_REG, fname), last_line_available);
+    }
+}
+
+/*
+ ** update_ui_line_reg_state
+ *
+ *  PARAMETERS:
+ *       start_line - first line whose reg status need to be set
+ *       end_line - last line whose reg status needs to be set
+ *       registered - registered/unregistered
+ *
+ *  DESCRIPTION: This function is used to set the ui line reg status
+ *
+ *  RETURNS: None
+ *
+ */
+void update_ui_line_reg_state(int start_line, int end_line, boolean registered)
+{
+    line_t      line_index = 0;
+    ccsipCCB_t *line_ccb = NULL;
+
+    for (line_index = (TEL_CCB_END + (line_t)start_line + 1);
+         line_index <= (TEL_CCB_END + end_line);
+         line_index++) {
+         line_ccb = sip_sm_get_ccb_by_index(line_index);
+         if (line_ccb) {
+             if (sip_config_check_line(line_ccb->dn_line)) {
+                 ui_set_sip_registration_state(line_ccb->dn_line, registered);
+             }
+          }
+    }
+}
+
+
+/*
+ ** regmgr_handle_register_update
+ *
+ *  PARAMETERS: last_available_line - last available line button on the phone
+ *
+ *  DESCRIPTION: This function is used to handle any registration updates needed
+ *               when a sidecar is plugged in or unplugged
+ *
+ *  RETURNS: None
+ *
+ */
+void regmgr_handle_register_update(line_t last_available_line)
+{
+    static const char fname[] = "regmgr_handle_register_update";
+    ccsipCCB_t *line_ccb;
+    line_t      line_index = 0;
+    char address[MAX_IPADDR_STR_LEN];
+    int         last_line_button_present;
+    //boolean     reg_update_needed = TRUE;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"last_available_line: %d\n",
+                          DEB_F_PREFIX_ARGS(SIP_REG, fname), last_available_line);
+
+    if (last_available_line == 1) {
+        return;
+    }
+
+    last_line_button_present = 1;
+
+       /* added by cangchen fix CSCsz91640*/
+    if (last_line_button_present > last_available_line) {
+                       for (line_index = (TEL_CCB_END + (line_t)last_available_line + 1);
+                 line_index <= (TEL_CCB_END + 1) &&
+                 line_index <= (TEL_CCB_END + (line_t)(last_line_button_present));
+                 line_index++) {
+                 line_ccb = sip_sm_get_ccb_by_index(line_index);
+                 if (line_ccb) {
+                    if (sip_config_check_line(line_ccb->dn_line)) {
+                        CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d: 0x%x\n",
+                                              DEB_L_C_F_PREFIX_ARGS(SIP_CONFIG, line_ccb->index, line_ccb->dn_line, fname),
+                                              line_index, line_ccb);
+                   }
+                }
+            }
+       }
+
+        if (last_available_line > last_line_button_present) {
+            //sidecar added
+            for (line_index = (TEL_CCB_END + (line_t)last_line_button_present + 1);
+                 line_index <= (TEL_CCB_END + 1) &&
+                 line_index <= (TEL_CCB_END + (line_t)(last_available_line));
+                 line_index++) {
+                 //sidecar added.register all the lines that have been newly added
+                 line_ccb = sip_sm_get_ccb_by_index(line_index);
+                 if (line_ccb) {
+                    if (sip_config_check_line(line_ccb->dn_line)) {
+                        CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d: 0x%x\n",
+                                              DEB_L_C_F_PREFIX_ARGS(SIP_CONFIG, line_ccb->index, line_ccb->dn_line, fname),
+                                              line_index, line_ccb);
+
+                        ui_set_sip_registration_state(line_ccb->dn_line, FALSE);
+
+                        sip_sm_call_cleanup(line_ccb);
+                        sipTransportGetPrimServerAddress(line_ccb->dn_line, address);
+
+                        sstrncpy(line_ccb->reg.proxy, address, MAX_IPADDR_STR_LEN);
+
+                        line_ccb->reg.addr = line_ccb->dest_sip_addr;
+                        line_ccb->reg.port = (uint16_t) line_ccb->dest_sip_port;
+
+                        if (ccsip_register_send_msg(SIP_REG_REQ, line_index) != SIP_REG_OK) {
+                            ccsip_register_cleanup(line_ccb, TRUE);
+                        }
+                   }
+                }
+            }
+        } else if (last_line_button_present > last_available_line) {
+            for (line_index = (TEL_CCB_END + (line_t)last_available_line + 1);
+                 line_index <= (TEL_CCB_END + 1) &&
+                 line_index <= (TEL_CCB_END + (line_t)(last_line_button_present));
+                 line_index++) {
+                 //sidecar removed.unregister al the lines that were
+                 //previously registered
+                 line_ccb = sip_sm_get_ccb_by_index(line_index);
+                 if (line_ccb) {
+                        if (sip_config_check_line(line_ccb->dn_line)) {
+                        ui_set_sip_registration_state(line_ccb->dn_line, FALSE);
+
+                        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cancelling timers, line= %d\n",
+                                  DEB_F_PREFIX_ARGS(SIP_TIMER, fname), line_ccb->index);
+                        (void) sip_platform_register_expires_timer_stop(line_ccb->index);
+                        sip_stop_ack_timer(line_ccb);
+
+                        if (ccsip_register_send_msg(SIP_REG_CANCEL, line_index) != SIP_REG_OK) {
+                            ccsip_register_cleanup(line_ccb, TRUE);
+                        }
+                   }
+                }
+            }
+        }
+
+
+}
+
+
+
+
+
+
+
+
+
diff --git a/libs/sipcc/core/sipstack/sip_common_transport.c b/libs/sipcc/core/sipstack/sip_common_transport.c
new file mode 100644 (file)
index 0000000..86b5e69
--- /dev/null
@@ -0,0 +1,2184 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_in.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_errno.h"
+#include "cpr_socket.h"
+#include "cpr_locks.h"
+#include "phone_debug.h"
+#include "dialplan.h"
+#include "config.h"
+#include "configmgr.h"
+#include "ccsip_platform_udp.h"
+#include "ccsip_platform_timers.h"
+#include "ccsip_register.h"
+#include "sip_platform_task.h"
+#include "ccsip_task.h"
+#include "ccsip_messaging.h"
+#include "ccsip_reldev.h"
+#include "sip_common_transport.h"
+#include "sip_csps_transport.h"
+#include "sip_ccm_transport.h"
+#include "sip_common_regmgr.h"
+#include "util_string.h"
+#include "dns_utils.h"
+#include "debug.h"
+#include "phntask.h"
+#include "ccsip_subsmanager.h"
+#include "ccsip_publish.h"
+#include "ccsip_platform_tcp.h"
+#include "ccsip_platform_tls.h"
+#include "platform_api.h"
+#include "sessionTypes.h"
+
+uint16_t ccm_config_id_addr_str[MAX_CCM] = {
+    CFGID_CCM1_ADDRESS,
+    CFGID_CCM2_ADDRESS,
+    CFGID_CCM3_ADDRESS
+};
+
+#ifdef IPV6_STACK_ENABLED
+
+uint16_t ccm_config_id_ipv6_addr_str[MAX_CCM] = {
+    CFGID_CCM1_IPV6_ADDRESS,
+    CFGID_CCM2_IPV6_ADDRESS,
+    CFGID_CCM3_IPV6_ADDRESS
+};
+
+#endif
+
+uint16_t ccm_config_id_port[MAX_CCM] = {
+    CFGID_CCM1_SIP_PORT,
+    CFGID_CCM2_SIP_PORT,
+    CFGID_CCM3_SIP_PORT
+};
+
+uint16_t ccm_config_id_sec_level[MAX_CCM] = {
+    CFGID_CCM1_SEC_LEVEL,
+    CFGID_CCM2_SEC_LEVEL,
+    CFGID_CCM3_SEC_LEVEL
+};
+
+uint16_t ccm_config_id_is_valid[MAX_CCM] = {
+    CFGID_CCM1_IS_VALID,
+    CFGID_CCM2_IS_VALID,
+    CFGID_CCM3_IS_VALID
+};
+
+ti_config_table_t CCM_Device_Specific_Config_Table[MAX_CCM];
+ti_config_table_t CCM_Dummy_Entry;
+ti_config_table_t CSPS_Config_Table[MAX_REG_LINES];
+ti_config_table_t *CCM_Config_Table[MAX_REG_LINES + 1][MAX_CCM];
+int phone_local_tcp_port[UNUSED_PARAM];
+ti_csps_t CSPS_Device_Specific_Config_Table;
+
+sip_tcp_conn_t sip_tcp_conn_tab[MAX_CONNECTIONS];
+
+char sent_string[] = "Sent:";
+char rcvd_string[] = "Rcvd:";
+char cseq_string[] = " Cseq:";
+char callid_string[] = " CallId:";
+
+static cpr_socket_t listen_socket = INVALID_SOCKET;
+extern cc_config_table_t CC_Config_Table[];
+extern sipCallHistory_t gCallHistory[];
+extern ccm_act_stdby_table_t CCM_Active_Standby_Table;
+
+//extern uint16_t ccm_config_id_addr_str[MAX_CCM];
+//extern uint16_t ccm_config_id_port[MAX_CCM];
+extern void platAddCallControlClassifiers(
+            unsigned long myIPAddr, unsigned short myPort,
+            unsigned long cucm1IPAddr, unsigned short cucm1Port,
+            unsigned long cucm2IPAddr, unsigned short cucm2Port,
+            unsigned long cucm3IPAddr, unsigned short cucm3Port,
+            unsigned char  protocol);
+extern void platform_get_ipv4_address(cpr_ip_addr_t *ip_addr);
+
+#define SIP_IPPROTO_UDP 17
+#define SIP_IPPROTO_TCP 6
+
+/*
+ *  Function: ccsip_add_wlan_classifiers()
+ *
+ *  Parameters: None
+ *
+ *  Description: Add classifiers required for wlan
+ *
+ *  Returns: None
+ *
+ */
+void ccsip_add_wlan_classifiers ()
+{
+    cpr_ip_addr_t    my_addr;
+    uint32_t    local_port = 0;
+    uint32_t  transport_prot = 0;
+
+    platform_get_ipv4_address (&my_addr);
+
+    config_get_value(CFGID_VOIP_CONTROL_PORT, &local_port,
+                         sizeof(local_port));
+    config_get_value(CFGID_TRANSPORT_LAYER_PROT, &transport_prot,
+                         sizeof(transport_prot));
+
+    platAddCallControlClassifiers(my_addr.u.ip4, local_port ,
+            ntohl(CCM_Device_Specific_Config_Table[PRIMARY_CCM].ti_common.addr.u.ip4),
+            CCM_Device_Specific_Config_Table[PRIMARY_CCM].ti_common.port,
+            ntohl(CCM_Device_Specific_Config_Table[SECONDARY_CCM].ti_common.addr.u.ip4),
+            CCM_Device_Specific_Config_Table[SECONDARY_CCM].ti_common.port,
+            ntohl(CCM_Device_Specific_Config_Table[TERTIARY_CCM].ti_common.addr.u.ip4),
+            CCM_Device_Specific_Config_Table[TERTIARY_CCM].ti_common.port,
+            (transport_prot==CONN_UDP)? SIP_IPPROTO_UDP:SIP_IPPROTO_TCP);
+}
+
+void ccsip_remove_wlan_classifiers ()
+{
+    platRemoveCallControlClassifiers();
+
+}
+
+/*
+ ** sipTransportGetServerHandle
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS: Line id
+ *
+ *  DESCRIPTION: This function gets the server handle for a particular
+ *               line id.
+ *
+ *  RETURNS: The server handle
+ *
+ */
+static cpr_socket_t
+sipTransportGetServerHandle (line_t dn, line_t ndx)
+{
+
+    ti_config_table_t *ccm_table_ptr = NULL;
+    ti_common_t ti_common;
+    static const char *fname = "sipTransportGetServerHandle";
+
+    /*
+     * Checking for dn < 1, since we dereference the Config_Tables using
+     * dn-1
+     */
+    if (((int)dn < 1) || ((int)dn > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, dn);
+        return (INVALID_SOCKET);
+    }
+
+    if (CC_Config_Table[dn - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        if (ndx == REG_BACKUP_CCB) {
+            /*
+             * This is the reg backup ccb, so return the
+             * standby ccm handle
+             */
+            ccm_table_ptr = CCM_Active_Standby_Table.standby_ccm_entry;
+        } else if (ndx > REG_BACKUP_CCB) {
+            ccsipCCB_t *ccb = NULL;
+
+            ccb = sip_sm_get_ccb_by_index(ndx);
+            if (ccb != NULL) {
+                ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+            } else {
+                return (INVALID_SOCKET);
+            }
+        } else {
+            ccm_table_ptr = CCM_Active_Standby_Table.active_ccm_entry;
+        }
+        if (ccm_table_ptr) {
+            ti_common = ccm_table_ptr->ti_common;
+            return (ti_common.handle);
+        }
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        return (sipTransportCSPSGetProxyHandleByDN(dn));
+    }
+    return (INVALID_SOCKET);
+}
+
+/*
+ ** sipTransportGetServerHandleWithAddr
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS: IP addr
+ *
+ *  DESCRIPTION: This function gets the server handle
+ *
+ *  RETURNS: The server handle
+ *
+ */
+static cpr_socket_t
+sipTransportGetServerHandleWithAddr (cpr_ip_addr_t *remote_ip_addr)
+{
+
+    ti_config_table_t *ccm_table_ptr = NULL;
+
+    ccm_table_ptr = CCM_Active_Standby_Table.active_ccm_entry;
+
+    if (ccm_table_ptr && util_compare_ip(remote_ip_addr, &(ccm_table_ptr->ti_common.addr))) {
+        return (ccm_table_ptr->ti_common.handle);
+    }
+    ccm_table_ptr = CCM_Active_Standby_Table.standby_ccm_entry;
+    if (ccm_table_ptr && util_compare_ip(remote_ip_addr, &(ccm_table_ptr->ti_common.addr))) {
+        return (ccm_table_ptr->ti_common.handle);
+    }
+    return (INVALID_SOCKET);
+}
+
+/*
+ ** sipTransportGetServerAddress
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS: Line id
+ *
+ *  DESCRIPTION: This function gets the server address for a particular
+ *               line id.
+ *
+ *  RETURNS: 0 if successful
+ *
+ */
+uint16_t
+sipTransportGetServerAddress (cpr_ip_addr_t *pip_addr, line_t dn, line_t ndx)
+{
+    static const char *fname = "sipTransportGetServerAddress";
+    *pip_addr = ip_addr_invalid;
+
+    /*
+     * Checking for dn < 1, since we dereference the Config_Tables using
+     * dn-1
+     */
+    if (((int)dn < 1) || ((int)dn > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, dn);
+        return (0);
+    }
+
+    if (CC_Config_Table[dn - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        if (ndx == REG_BACKUP_CCB) {
+            /*
+             * This is the reg backup ccb, so return the
+             * standby ccm addr
+             */
+            if (CCM_Active_Standby_Table.standby_ccm_entry) {
+                *pip_addr = CCM_Active_Standby_Table.standby_ccm_entry->
+                        ti_common.addr;
+            }
+            return (0);
+        } else if (ndx > REG_BACKUP_CCB) {
+            ccsipCCB_t *ccb = NULL;
+
+            ccb = sip_sm_get_ccb_by_index(ndx);
+            if (ccb != NULL) {
+                ti_config_table_t *ccm_table_ptr = NULL;
+                ti_common_t ti_common;
+
+                ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+                if (ccm_table_ptr) {
+                    ti_common = ccm_table_ptr->ti_common;
+                    *pip_addr = ti_common.addr;
+                }
+            }
+            return (0);
+        } else {
+            if (CCM_Active_Standby_Table.active_ccm_entry) {
+                *pip_addr = CCM_Active_Standby_Table.active_ccm_entry->
+                        ti_common.addr;
+            }
+            return (0);
+        }
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        return (sipTransportCSPSGetProxyAddressByDN(pip_addr, dn));
+    }
+}
+
+/*
+ ** sipTransportGetServerPort
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS: Line id
+ *
+ *  DESCRIPTION: This function gets the server port for a particular
+ *               line id.
+ *
+ *  RETURNS: Server port as a short
+ *
+ */
+short
+sipTransportGetServerPort (line_t dn, line_t ndx)
+{
+    static const char *fname = "sipTransportGetServerPort";
+    /*
+     * Checking for dn < 1, since we dereference the Config_Tables using
+     * dn-1
+     */
+    if (((int)dn < 1) || ((int)dn > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, dn);
+        return (0);
+    }
+
+    if (CC_Config_Table[dn - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        if (ndx == REG_BACKUP_CCB) {
+            /*
+             * This is the reg backup ccb, so return the
+             * standby ccm port
+             */
+            if (CCM_Active_Standby_Table.standby_ccm_entry) {
+                return ((short) CCM_Active_Standby_Table.standby_ccm_entry->
+                        ti_common.port);
+            }
+        } else if (ndx > REG_BACKUP_CCB) {
+            ccsipCCB_t *ccb = NULL;
+
+            ccb = sip_sm_get_ccb_by_index(ndx);
+            if (ccb != NULL) {
+                ti_config_table_t *ccm_table_ptr = NULL;
+                ti_common_t ti_common;
+
+                ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+                if (ccm_table_ptr) {
+                    ti_common = ccm_table_ptr->ti_common;
+                    return (ti_common.port);
+                }
+            }
+            return (0);
+        } else {
+            if (CCM_Active_Standby_Table.active_ccm_entry) {
+                return ((short) CCM_Active_Standby_Table.active_ccm_entry->
+                        ti_common.port);
+            }
+            return (0);
+        }
+    }
+
+    /*
+     * Assume CSPS for now.
+     */
+    return ((short) sipTransportCSPSGetProxyPortByDN(dn));
+}
+
+conn_create_status_t
+sip_transport_setup_cc_conn (line_t dn, CCM_ID ccm_id)
+{
+    static const char *fname = "sip_transport_setup_cc_conn";
+    int             dnsErrorCode;
+    cpr_ip_addr_t   server_ipaddr;
+    uint16_t        server_port = 0, listener_port = 0;
+    cpr_socket_t    server_conn_handle = INVALID_SOCKET;
+    conn_create_status_t status = CONN_INVALID;
+    uint32_t        type;
+    ti_common_t    *ti_common;
+    int            ip_mode = CPR_IP_MODE_IPV4;
+       uint32_t                s_port;
+
+    /*
+     * Checking for dn < 1, since we dereference the Config_Tables using
+     * dn-1
+     */
+    if (((int)dn < 1) || ((int)dn > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, dn);
+        return (status);
+    }
+
+    if (ccm_id >= MAX_CCM) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"ccm id <%d> out of bounds.\n",
+                          fname, ccm_id);
+        return (status);
+    }
+    CPR_IP_ADDR_INIT(server_ipaddr);
+
+#ifdef IPV6_STACK_ENABLED
+
+    config_get_value(CFGID_IP_ADDR_MODE,
+                     &ip_mode, sizeof(ip_mode));
+#endif
+
+    if (CC_Config_Table[dn - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        ti_ccm_t *ti_ccm;
+
+        ti_ccm = &CCM_Config_Table[dn - 1][ccm_id]->ti_specific.ti_ccm;
+        if (!ti_ccm->is_valid) {
+            /*
+             * dont even attempt to create a connection if the
+             * platform has deemed the ccm info invalid
+             */
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Admin has not configured a valid cucm for cucm index=%s=%d.\n",
+                              fname, CCM_ID_PRINT(ccm_id), ccm_id);
+            return (status);
+        }
+        dnsErrorCode = dnsGetHostByName(CCM_Config_Table[dn - 1][ccm_id]->
+                                         ti_common.addr_str, &server_ipaddr,
+                                         100, 1);
+        if (dnsErrorCode != DNS_OK) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              "sip_transport_setup_cc_conn",
+                                "dnsGetHostByName() returned error:%s",
+                                CCM_Config_Table[dn - 1][ccm_id]->ti_common.addr_str);
+            return status;
+        }
+
+        util_ntohl(&server_ipaddr, &server_ipaddr);
+
+
+        config_get_value(CFGID_VOIP_CONTROL_PORT, &s_port, sizeof(s_port));
+        server_port =  (uint16_t) s_port;
+
+        if (CCM_Config_Table[dn - 1][ccm_id]->ti_common.conn_type == CONN_UDP) {
+            type = SOCK_DGRAM;
+            listener_port = CCM_Config_Table[dn - 1][ccm_id]->ti_common.listen_port;
+        } else {
+            type = SOCK_STREAM;
+        }
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        sipTransportGetServerIPAddr(&server_ipaddr, dn);
+        server_port   = (uint16_t) sipTransportGetPrimServerPort(dn);
+        if (CSPS_Config_Table[dn - 1].ti_common.conn_type == CONN_UDP) {
+            type = SOCK_DGRAM;
+            listener_port = CSPS_Config_Table[dn - 1].ti_common.listen_port;
+        } else {
+            type = SOCK_STREAM;
+        }
+    }
+    if (util_check_if_ip_valid(&server_ipaddr) && server_port != 0) {
+        char server_ipaddr_str[MAX_IPADDR_STR_LEN];
+        int  ret_status = SIP_ERROR;
+
+        ipaddr2dotted(server_ipaddr_str, &server_ipaddr);
+        if (type == SOCK_DGRAM) {
+            ret_status = sip_platform_udp_channel_create(ip_mode, &server_conn_handle,
+                                                         &server_ipaddr,
+                                                         server_port, 0);
+            if (ret_status == SIP_OK) {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"DN <%d>: CC UDP socket opened: "
+                                 "<%s>:<%d>, handle=<%d>\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname), dn,
+                                 server_ipaddr_str, server_port,
+                                 server_conn_handle);
+                status = CONN_SUCCESS;
+            } else {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"DN <%d>:"
+                                  "udp channel error"
+                                  "server addr=%s, server port=%d) failed.\n",
+                                  fname, dn, server_ipaddr_str, server_port);
+                server_conn_handle = INVALID_SOCKET;
+                status = CONN_FAILURE;
+            }
+        }
+        else {
+            sipSPIMessage_t sip_msg;
+
+            if (CC_Config_Table[dn - 1].cc_type != CC_CCM) {
+                /* We should not come here in non-ccm mode */
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"TLS and TCP not supported in non-ccm"
+                                  " mode\n", fname);
+                return (CONN_INVALID);
+            }
+
+            // TLS if tls and sec_level is not non secure
+            if (((CCM_Config_Table[dn - 1][ccm_id]->ti_specific.ti_ccm.sec_level
+                    == AUTHENTICATED) ||
+                 (CCM_Config_Table[dn - 1][ccm_id]->ti_specific.ti_ccm.sec_level
+                    == ENCRYPTED)) &&
+                (CCM_Config_Table[dn - 1][ccm_id]->ti_common.conn_type == CONN_TLS)) {
+                uint32_t port = 0;
+
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"server_ipaddr %d \n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname), server_ipaddr);
+                sip_msg.createConnMsg.addr = server_ipaddr;
+                config_get_value(ccm_config_id_port[ccm_id], &port,
+                                 sizeof(port));
+                sip_msg.createConnMsg.port = (uint16_t) port;
+                sip_msg.context = NULL;
+                server_conn_handle = sip_tls_create_connection(&sip_msg, TRUE,
+                        CCM_Config_Table[dn - 1][ccm_id]->ti_specific.ti_ccm.sec_level);
+                if (server_conn_handle != INVALID_SOCKET) {
+                    CCM_Config_Table[dn - 1][ccm_id]->ti_common.port =
+                        (uint16_t) port;
+                }
+            } else {
+                sip_msg.createConnMsg.addr = server_ipaddr;
+                sip_msg.createConnMsg.port = server_port;
+                sip_msg.context = NULL;
+                server_conn_handle = sip_tcp_create_connection(&sip_msg);
+            }
+            if (server_conn_handle != INVALID_SOCKET) {
+                listener_port = sip_msg.createConnMsg.local_listener_port;
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"DN <%d>: CC TCP socket opened: "
+                                 "to <%s>:<%d>, local_port: %d handle=<%d>\n",
+                                 DEB_F_PREFIX_ARGS(SIP_TRANS, fname), dn, server_ipaddr_str,
+                                 server_port, listener_port,
+                                 server_conn_handle);
+                status = CONN_SUCCESS;
+                phone_local_tcp_port[CCM_Config_Table[dn-1][ccm_id]->ti_specific.ti_ccm.ccm_id] =
+                    sip_msg.createConnMsg.local_listener_port;
+            } else {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"DN <%d>:"
+                                  "tcp channel create error "
+                                  "server addr=%s, server port=%d) failed.\n",
+                                  fname, dn, server_ipaddr_str, server_port);
+                status = CONN_FAILURE;
+            }
+        }
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"DN <%d>: CC address/port not configured.\n",
+                         fname, dn);
+        status = CONN_INVALID;
+    }
+
+    if ((status == CONN_SUCCESS) || (status == CONN_FAILURE)) {
+        if (CC_Config_Table[dn - 1].cc_type == CC_CCM) {
+            /*
+             * regmgr
+             */
+            /*
+             * Add the newly created socket to the CCM as the one
+             * we listen for incoming messages as well.
+             */
+            ti_common = &CCM_Config_Table[dn - 1][ccm_id]->ti_common;
+        } else {
+            /*
+             * Non CCM Case
+             */
+            ti_common = &CSPS_Config_Table[dn - 1].ti_common;
+        }
+        ti_common->addr = server_ipaddr;
+        ti_common->port = server_port;
+        ti_common->handle = server_conn_handle;
+        ti_common->listen_port = listener_port;
+    }
+
+    return (status);
+}
+
+
+int
+sip_transport_destroy_cc_conn (line_t dn, CCM_ID ccm_id)
+{
+    static const char *fname = "sip_transport_destroy_cc_conn";
+    int          disconnect_status = 0;
+    cpr_socket_t cc_handle;
+    CONN_TYPE    conn_type;
+    uint16_t     max_cc_count, cc_index;
+    ti_common_t *ti_common;
+    CC_ID        cc_type;
+
+    /*
+     * Checking for dn < 1, since we dereference the Config_Tables using
+     * dn-1
+     */
+    if (((int)dn < 1) || ((int)dn > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, dn);
+        return (disconnect_status);
+    }
+
+    if (ccm_id >= MAX_CCM) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"ccm id <%d> out of bounds.\n",
+                          fname, ccm_id);
+        return (disconnect_status);
+    }
+
+    cc_type = CC_Config_Table[dn - 1].cc_type;
+    if (cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        ti_common = &CCM_Config_Table[dn - 1][ccm_id]->ti_common;
+        max_cc_count = MAX_CCM;
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        ti_common = &CSPS_Config_Table[dn - 1].ti_common;
+        max_cc_count = MAX_CSPS;
+    }
+    cc_index = 0;
+    do {
+        cc_handle = ti_common->handle;
+        conn_type = ti_common->conn_type;
+        if (cc_handle != INVALID_SOCKET) {
+            /* Close the UDP send channel to the proxy */
+            if (sip_platform_udp_channel_destroy(cc_handle) < 0) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"DN <%d>:"
+                                  "handle=%d) \n", fname, dn, cc_handle);
+                disconnect_status = -1;
+            } else {
+                CCSIP_DEBUG_TASK(DEB_F_PREFIX"DN <%d>: CC socket closed: "
+                                 "handle=<%d>\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname), dn, cc_handle);
+                disconnect_status = 0;
+            }
+            if (conn_type != CONN_UDP) {
+                int connid;
+
+                connid = sip_tcp_fd_to_connid(ti_common->handle);
+                sipTcpFreeSendQueue(connid);
+                sip_tcp_purge_entry(connid);
+            }
+        } else {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"DN <%d>: CC socket already closed.\n",
+                             DEB_F_PREFIX_ARGS(SIP_TRANS, fname), dn);
+            disconnect_status = 0;
+        }
+        cc_index++;
+        ti_common = &CCM_Config_Table[dn - 1][cc_index]->ti_common;
+        /*
+         * Will break out for CSPS the first time in the loop.
+         */
+    } while (cc_index < max_cc_count);
+    if (listen_socket != INVALID_SOCKET) {
+        if (sip_platform_udp_channel_destroy(listen_socket) < 0) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"DN <%d>:"
+                              "(handle=%d)\n", fname, dn, listen_socket);
+            disconnect_status = -1;
+        } else {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"DN <%d>: CC socket closed: handle=<%d>\n",
+                             DEB_F_PREFIX_ARGS(SIP_TRANS, fname), dn, listen_socket);
+            disconnect_status = 0;
+        }
+        sip_platform_task_reset_listen_socket(listen_socket);
+        listen_socket = INVALID_SOCKET;
+    }
+    if (CC_Config_Table[dn - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        CCM_Config_Table[dn - 1][ccm_id]->ti_common.handle = INVALID_SOCKET;
+    } else {
+        ti_common = &CSPS_Config_Table[dn - 1].ti_common;
+        ti_common->addr = ip_addr_invalid;
+        ti_common->port = 0;
+        ti_common->handle = INVALID_SOCKET;
+    }
+    return (disconnect_status);
+}
+
+/*
+ ** sipTransportCreateSendMessage
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: This function first creates the char * sip message that
+ *               needs to be sent out and then calls the
+ *               sipTransportSendMessage() to send out the message.
+ *
+ *  RETURNS: -1 if failure and 0 if it succeeds.
+ *
+ *  NOTE: This function will be consolidated with the following function
+ *        so that only the following function will be necessary. Also the
+ *        signature of the new function that results will be made similar
+ *        to that of the IOS sip stacks sipTransportSendMessage() call.
+ *        That will make the port of IOS based Propel SIP stack into the
+ *        phone easier.
+ *
+ */
+int
+sipTransportCreateSendMessage (ccsipCCB_t *ccb,
+                               sipMessage_t *pSIPMessage,
+                               sipMethod_t message_type,
+                               cpr_ip_addr_t *cc_remote_ipaddr,
+                               uint16_t cc_remote_port,
+                               boolean isRegister,
+                               boolean reTx,
+                               int timeout,
+                               void *cbp,
+                               int reldev_stored_msg)
+{
+    const char *fname = "sipTransportCreateSendMessage";
+    static char aOutBuf[SIP_UDP_MESSAGE_SIZE + 1];
+    uint32_t    nbytes = SIP_UDP_MESSAGE_SIZE;
+    hStatus_t   sippmh_write_status = STATUS_FAILURE;
+
+    /*
+     * Check args
+     */
+    if (!pSIPMessage) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args: pSIPMessage is null\n", fname);
+        return (-1);
+    }
+
+    /*
+     * Get message from the reliable delivery stored msg first and
+     * if there is no message from the reliable delivery message storage
+     * then compose the SIP message.
+     */
+    nbytes = sipRelDevGetStoredCoupledMessage(reldev_stored_msg, &aOutBuf[0],
+                                              nbytes);
+    if (nbytes == 0) {
+        nbytes = SIP_UDP_MESSAGE_SIZE;
+        sippmh_write_status = sippmh_write(pSIPMessage, aOutBuf, &nbytes);
+    } else {
+        sippmh_write_status = STATUS_SUCCESS;
+    }
+    ccsip_dump_send_msg_info(aOutBuf, pSIPMessage, cc_remote_ipaddr,
+                            cc_remote_port);
+
+    free_sip_message(pSIPMessage);
+    if (sippmh_write_status == STATUS_FAILURE) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
+                          ccb ? ccb->index : 0, ccb ? ccb->dn_line : 0, fname,
+                          "sippmh_write()");
+        return (-1);
+    }
+    if ((aOutBuf[0] == '\0') || (nbytes == 0)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sippmh_write() returned empty buffer "
+                          "string\n", fname);
+        return (-1);
+    }
+    aOutBuf[nbytes] = '\0'; /* set NULL string for debug printing */
+
+    if (sipTransportSendMessage(ccb, aOutBuf, nbytes, message_type,
+                                cc_remote_ipaddr, cc_remote_port, isRegister,
+                                reTx, timeout, cbp) < 0) {
+        if (ccb) {
+            CCSIP_DEBUG_ERROR("SIPCC-ENTRY: LINE %d/%d: %-35s: message not "
+                "sent of type %s=%d. sipTransportSendMessage() failed.\n",
+                 ccb->index, ccb->dn_line, fname,
+                 message_type == sipMethodRegister ? "sipMethodRegister" : "", sipMethodRegister);
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sipTransportSendMessage()");
+        }
+        return (-1);
+    }
+
+    return (0);
+}
+
+/*
+ ** sipTransportSendMessage
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Sends out the sip message.
+ *
+ *  RETURNS: -1 on failure and 0 on success.
+ *
+ *  NOTE: This function will be consolidated with the following function
+ *        so that only the following function will be necessary. Also the
+ *        signature of the new function that results will be made similar
+ *        to that of the IOS sip stacks sipTransportSendMessage() call.
+ *        That will make the port of IOS based Propel SIP stack into the
+ *        phone easier.
+ */
+int
+sipTransportSendMessage (ccsipCCB_t *ccb,
+                         char *pOutMessageBuf,
+                         uint32_t nbytes,
+                         sipMethod_t message_type,
+                         cpr_ip_addr_t *cc_remote_ipaddr,
+                         uint16_t cc_remote_port,
+                         boolean isRegister,
+                         boolean reTx,
+                         int timeout,
+                         void *cbp)
+{
+    const char *fname = "sipTransportSendMessage";
+    char         cc_config_ipaddr_str[MAX_IPADDR_STR_LEN];
+    char         cc_remote_ipaddr_str[MAX_IPADDR_STR_LEN];
+    char         obp_address[MAX_IPADDR_STR_LEN];
+    cpr_socket_t send_to_proxy_handle = INVALID_SOCKET;
+    int          nat_enable, dnsErrorCode;
+    const char  *conn_type;
+    uint32_t     local_udp_port = 0;
+    int          tcp_error = SIP_TCP_SEND_OK;
+    int          ip_mode = CPR_IP_MODE_IPV4;
+
+    /*
+     * Check args
+     */
+    if ((!pOutMessageBuf) || (pOutMessageBuf[0] == '\0')) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args: pOutMessageBuf is empty\n", fname);
+        return (-1);
+    }
+
+#ifdef IPV6_STACK_ENABLED
+    config_get_value(CFGID_IP_ADDR_MODE,
+                     &ip_mode, sizeof(ip_mode));
+#endif
+    conn_type = sipTransportGetTransportType(1, TRUE, ccb);
+    if (ccb) {
+        /* Check to see whether the supplied address and port
+         * match those of the currently configured proxy.
+         * If not, open a new UDP channel, send the message,
+         * and close the channel.
+         */
+        /* Check to see whether the supplied address and port
+         * match those of the currently configured proxy.
+         * If not, open a new UDP channel, send the message,
+         * and close the channel.
+         */
+        cpr_ip_addr_t cc_config_ipaddr;
+        uint16_t cc_config_port;
+
+        sipTransportGetServerAddress(&cc_config_ipaddr, ccb->dn_line, ccb->index);
+        cc_config_port   = sipTransportGetServerPort(ccb->dn_line, ccb->index);
+
+        /*
+         * Convert IP address to string, for debugs
+         */
+        if (SipDebugMessage) {
+            ipaddr2dotted(cc_config_ipaddr_str, &cc_config_ipaddr);
+            ipaddr2dotted(cc_remote_ipaddr_str, cc_remote_ipaddr);
+        }
+
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"ccb <%d>: config <%s>:<%d> - remote <%s>:<%d>\n",
+                            DEB_F_PREFIX_ARGS(SIP_TRANS, fname), ccb->index,
+                            cc_config_ipaddr_str, cc_config_port,
+                            cc_remote_ipaddr_str, cc_remote_port);
+
+        if (conn_type != NULL) {
+            if (!cpr_strcasecmp(conn_type, "UDP")) {
+                if (util_compare_ip(&cc_config_ipaddr, cc_remote_ipaddr) &&
+                    (cc_config_port == cc_remote_port)) {
+                    send_to_proxy_handle = sipTransportGetServerHandle(ccb->dn_line,
+                                                                       ccb->index);
+                    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Got handle %d\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname),
+                                     send_to_proxy_handle);
+                }
+            } else { /* TCP */
+                if (util_compare_ip(&cc_config_ipaddr, cc_remote_ipaddr)) {
+                    send_to_proxy_handle = sipTransportGetServerHandle(ccb->dn_line,
+                                                                       ccb->index);
+                    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Got handle %d\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname),
+                                     send_to_proxy_handle);
+                    if (send_to_proxy_handle == INVALID_SOCKET) {
+                        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Invalid socket\n", fname);
+                        return (-1);
+                    }
+                }
+            }
+        } else {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "Invalid Connection type returned");
+            return (-1);
+        }
+    }
+
+    /*
+     * Make the send code below believe that we do not have a connection to
+     * the remote side so that it creates a send port for each SIP message.
+     * This send port will have a source UDP port retrieved from the NAT
+     * configuration settings
+     */
+    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
+    if (nat_enable == 1) {
+        send_to_proxy_handle = INVALID_SOCKET;
+        config_get_value(CFGID_VOIP_CONTROL_PORT, &local_udp_port,
+                         sizeof(local_udp_port));
+    }
+    /*
+     * Check if we need to get the outbound proxy address and port.  If we
+     * do then we will make the code below believe we do not have a
+     * connection to the remote side so it creates a send port for this
+     * message.  This involves two checks, the first check is if outbound
+     * proxy support is enabled at all.  The second check is that only
+     * REQUEST messages go to the outbound proxy.  All other message
+     * types follow the normal rules for sending
+     */
+    if (ccb) {
+        if (ccb->outBoundProxyPort == 0) {
+            sipTransportGetOutbProxyAddress(ccb->dn_line, obp_address);
+            if ((cpr_strcasecmp(obp_address, UNPROVISIONED) != 0) &&
+                (obp_address[0] != 0) && (obp_address[0] != '0')) {
+
+                /* Outbound proxy is configured, get it's IP address */
+                dnsErrorCode = sipTransportGetServerAddrPort(obp_address,
+                                         &ccb->outBoundProxyAddr,
+                                         (uint16_t *)&ccb->outBoundProxyPort,
+                                         &ccb->ObpSRVhandle,
+                                         TRUE);
+
+                if (dnsErrorCode != DNS_OK) {
+                    /*
+                     * SRV failed, do a DNS A record lookup
+                     * on the outbound proxy
+                     */
+                    if (util_check_if_ip_valid(&(ccb->outBoundProxyAddr)) == FALSE) {
+                        dnsErrorCode = dnsGetHostByName(obp_address,
+                                                         &ccb->outBoundProxyAddr,
+                                                         100, 1);
+                    }
+                    if (dnsErrorCode == DNS_OK) {
+                        util_ntohl(&(ccb->outBoundProxyAddr),&(ccb->outBoundProxyAddr));
+                    } else {
+                        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                          "sipTransportSendMessage",
+                                          "dnsGetHostByName() returned error");
+
+                        ccb->outBoundProxyAddr = ip_addr_invalid;
+                        ccb->outBoundProxyPort = 0;
+                        return (-1);
+                    }
+                } else {
+                    util_ntohl(&(ccb->outBoundProxyAddr), &(ccb->outBoundProxyAddr));
+                }
+            }
+        }
+
+        /*
+         * only use outbound proxy if we have one configured,
+         * we are using the default proxy, we are not using the Emergency
+         * route, and it is not the backup proxy registration.
+         */
+        if (util_check_if_ip_valid(&(ccb->outBoundProxyAddr)) &&
+            (ccb->proxySelection == SIP_PROXY_DEFAULT) &&
+            (ccb->routeMode != RouteEmergency) && (ccb->index != REG_BACKUP_CCB)) {
+            send_to_proxy_handle = INVALID_SOCKET;
+            switch (message_type) {
+            case sipMethodResponse:
+            case sipMethodUnknown:
+                ccb->outBoundProxyPort = cc_remote_port;
+                break;
+            default:
+                /*
+                 * We have determined that this message is a REQUEST
+                 * so we will change the one time outgoing port to
+                 * the outbound proxy address and port
+                 */
+                *cc_remote_ipaddr = ccb->outBoundProxyAddr;
+                if (ccb->outBoundProxyPort != 0) {
+                    cc_remote_port = (uint16_t) ccb->outBoundProxyPort;
+                } else {
+                    cc_remote_port = (uint16_t) sipTransportGetOutbProxyPort(ccb->dn_line);
+                    ccb->outBoundProxyPort = cc_remote_port;
+                }
+                break;
+            }
+        }
+    }
+
+    if (SipDebugTask || SipDebugMessage) {
+        ipaddr2dotted(cc_remote_ipaddr_str, cc_remote_ipaddr);
+    }
+
+    if ((conn_type != NULL) && (cpr_strcasecmp(conn_type, "UDP")) &&
+        (send_to_proxy_handle == INVALID_SOCKET)) {
+        send_to_proxy_handle = sipTransportGetServerHandleWithAddr(cc_remote_ipaddr);
+        CCSIP_DEBUG_TASK(DEB_F_PREFIX"<%s> remote ip addr\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname), cc_remote_ipaddr_str);
+        if (send_to_proxy_handle == INVALID_SOCKET) {
+            // for TCP and TLS return if we do not have a connection to the
+            // remote side
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No connection to ip addr <%s>\n", fname, cc_remote_ipaddr_str);
+            return (-1);
+        }
+    }
+    if (send_to_proxy_handle != INVALID_SOCKET) {
+        /*
+         * Get the connection type and call ...sendto() or
+         * ...send() appropriately (for udp and tcp respectively).
+         * Also this will need to change to send the correct line
+         * when we go with supporting CCM and proxy on the
+         * same EP. For now we can just pass '0' as line as the phone
+         * is only going to support either CSPS or CCM and not both at
+         * the same time.
+         */
+        if (!cpr_strcasecmp(conn_type, "UDP")) {
+            if (sip_platform_udp_channel_sendto(send_to_proxy_handle,
+                                                pOutMessageBuf,
+                                                nbytes,
+                                                cc_remote_ipaddr,
+                                                cc_remote_port) == SIP_ERROR) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "sip_platform_udp_channel_sendto()");
+                return (-1);
+            }
+        }
+        else {
+            /*
+             * Assume TCP for now
+             */
+            tcp_error = sip_tcp_channel_send(send_to_proxy_handle,
+                                     pOutMessageBuf,
+                                     (unsigned short)nbytes);
+            if (tcp_error == SIP_TCP_SEND_ERROR) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "sip_platform_tcp_channel_send()");
+                return (-1);
+            }
+        }
+
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Sent SIP message: handle=<%d>,"
+                            "length=<%d>, message=\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname),
+                            send_to_proxy_handle, nbytes);
+        CCSIP_DEBUG_MESSAGE_PKT(pOutMessageBuf);
+    } else {
+        /* Create a new handle for the supplied address and port */
+        cpr_socket_t one_time_handle = INVALID_SOCKET;
+        int status = -1;
+
+        /* Open UDP send channel to the specified address and port */
+        status = sip_platform_udp_channel_create(ip_mode, &one_time_handle,
+                                                 cc_remote_ipaddr,
+                                                 cc_remote_port,
+                                                 local_udp_port);
+        if (status < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sip_platform_udp_channel_create()");
+            return (-1);
+        }
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Opened a one-time UDP send channel to server "
+                            "<%s>:<%d>, handle = %d local port= %d\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname),
+                            cc_remote_ipaddr_str, cc_remote_port,
+                            one_time_handle, local_udp_port);
+
+        /* Send the message */
+        if (sip_platform_udp_channel_sendto(one_time_handle,
+                                            pOutMessageBuf,
+                                            nbytes,
+                                            cc_remote_ipaddr,
+                                            cc_remote_port) == SIP_ERROR) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sip_platform_udp_channel_sendto()");
+            /* Close the UDP send channel */
+            status = sip_platform_udp_channel_destroy(one_time_handle);
+            if (status < 0) {
+                CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                  fname, "sip_platform_udp_channel_destroy()");
+            }
+            return (-1);
+        }
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Sent SIP message to <%s>:<%d>, "
+                            "handle=<%d>, length=<%d>, message=\n",
+                            DEB_F_PREFIX_ARGS(SIP_TRANS, fname), cc_remote_ipaddr_str, cc_remote_port,
+                            one_time_handle, nbytes);
+        CCSIP_DEBUG_MESSAGE_PKT(pOutMessageBuf);
+
+        /* Close the UDP send channel */
+        status = sip_platform_udp_channel_destroy(one_time_handle);
+        if (status < 0) {
+            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                              fname, "sip_platform_udp_channel_destroy()");
+            return (-1);
+        }
+        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Closed a one-time UDP send channel "
+                            "handle = %d\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname), one_time_handle);
+    }
+
+    if (ccb) {
+        //
+        // Cancel any outstanding reTx timers, if any
+        //
+        /*
+         * Dont do for fallback ccb's.
+         */
+        if ((ccb->index <= REG_BACKUP_CCB) && reTx) {
+            CCSIP_DEBUG_STATE(get_debug_string(DEBUG_SIP_ENTRY),
+                              ccb->index, ccb->dn_line, fname,
+                              "Stopping reTx timer");
+            sip_platform_msg_timer_stop(ccb->index);
+
+            /*
+             * If the specified timeout value is not 0 and UDP, start the reTx timer
+             * When the phone is in remote location, then REG message send may fail
+             * for TCP. In that case, that message has to be retransmitted from the SIP
+             * layer. So if the error_no == CPR_ENOTCONN, then send the SIP message
+             * during next retry.
+             *
+             */
+            if ((timeout > 0) && ((!cpr_strcasecmp(conn_type, "UDP")
+                || ((tcp_error == CPR_ENOTCONN) && (!cpr_strcasecmp(conn_type, "TCP")))))) {
+                void *data;
+
+                data = isRegister ? (void *) ccb : (void *)(long)ccb->index;
+
+                CCSIP_DEBUG_STATE(DEB_F_PREFIX"LINE %d/%d: Starting reTx timer (%d "
+                                  "msec)\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname), ccb->index, ccb->dn_line,
+                                  timeout);
+                ccb->retx_flag = TRUE;
+                if (sip_platform_msg_timer_start(timeout, data, ccb->index,
+                                                 pOutMessageBuf, nbytes,
+                                                 (int) message_type, cc_remote_ipaddr,
+                                                 cc_remote_port, isRegister) != SIP_OK) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED), ccb->index,
+                                      ccb->dn_line, fname,
+                                      "sip_platform_msg_timer_start()");
+                    ccb->retx_flag = FALSE;
+                }
+            }
+        }
+
+        if (sipMethodCancel == message_type || sipMethodBye == message_type) {
+            gCallHistory[ccb->index].last_bye_dest_ipaddr = *cc_remote_ipaddr;
+            gCallHistory[ccb->index].last_bye_dest_port   = cc_remote_port;
+        }
+    }
+    else {
+        sipSCB_t *scbptr = (sipSCB_t *)cbp;
+        ccsip_publish_cb_t *pcb_p = (ccsip_publish_cb_t *)cbp;
+        sipTCB_t *tcbp = (sipTCB_t *)cbp;
+
+        if (cbp != NULL) {
+            sipPlatformUITimer_t *timer = NULL;
+            uint32_t id = 0;
+
+            scbptr->hb.retx_flag = TRUE;
+            if (((ccsip_common_cb_t *)cbp)->cb_type == SUBNOT_CB) {
+                timer = &(sipPlatformUISMSubNotTimers[scbptr->line]);
+                id = scbptr->line;
+            } else if (((ccsip_common_cb_t *)cbp)->cb_type == PUBLISH_CB) {
+                timer = &(pcb_p->retry_timer);
+                id = pcb_p->pub_handle;
+            } else { // unsolicited NOTIFY
+                int temp_timeout = 0;
+                config_get_value(CFGID_TIMER_T1, &temp_timeout, sizeof(temp_timeout));
+                temp_timeout = (64 * temp_timeout);
+                if (cprStartTimer(tcbp->timer, temp_timeout, (void *)(long)(tcbp->trxn_id)) == CPR_FAILURE) {
+                    CCSIP_DEBUG_STATE(DEB_F_PREFIX"%s failed\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname), "cprStartTimer");
+                }
+            }
+
+            if (timeout > 0 && timer != NULL) {
+                CCSIP_DEBUG_STATE(DEB_F_PREFIX"Starting reTx timer for %d secs",
+                                  DEB_F_PREFIX_ARGS(SIP_TRANS, fname), timeout);
+                if (sip_platform_msg_timer_subnot_start(timeout, timer, id,
+                                                        pOutMessageBuf, nbytes,
+                                                        (int) message_type,
+                                                        cc_remote_ipaddr,
+                                                        cc_remote_port) != SIP_OK) {
+                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                                      fname, "sip_platform_msg_timer_subnot_start");
+                }
+            }
+        }
+
+    }
+
+    return (0);
+}
+
+/*
+ ** sipTransportGetListenPort
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS: Line id
+ *
+ *  DESCRIPTION: Reads and returns the Primary server port from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: Primary Server port (Currently only SIP Proxy specific)
+ *
+ */
+uint16_t
+sipTransportGetListenPort (line_t line, ccsipCCB_t *ccb)
+{
+    ti_config_table_t *ccm_table_ptr = NULL;
+    static const char *fname = "sipTransportGetListenPort";
+
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return 0;
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        if (ccb) {
+            ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+        }
+        if (ccm_table_ptr) {
+            CCM_ID ccm_id;
+
+            ccm_id = ccm_table_ptr->ti_specific.ti_ccm.ccm_id;
+            if (ccm_id >= MAX_CCM) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"ccm id <%d> out of bounds.\n",
+                                  fname, ccm_id);
+                return 0;
+            }
+            return ((uint16_t) CCM_Config_Table[line - 1][ccm_id]->
+                    ti_common.listen_port);
+        } else if (CCM_Active_Standby_Table.active_ccm_entry != NULL) {
+            return ((uint16_t) CCM_Active_Standby_Table.active_ccm_entry->
+                    ti_common.listen_port);
+        } else {
+            return ((uint16_t) CCM_Config_Table[line - 1][PRIMARY_CCM]->
+                    ti_common.listen_port);
+        }
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        return ((uint16_t) CSPS_Config_Table[line - 1].ti_common.listen_port);
+    }
+}
+
+/*
+ ** sipTransportGetTransportType
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS: Line id
+ *
+ *  DESCRIPTION: Reads and returns the Primary server port from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: Primary Server port (Currently only SIP Proxy specific)
+ *
+ */
+const char *
+sipTransportGetTransportType (line_t line, boolean upper_case,
+                              ccsipCCB_t *ccb)
+{
+    const char *tcp, *udp, *tls;
+    CONN_TYPE conn_type;
+    ti_config_table_t *ccm_table_ptr = NULL;
+    static const char *fname = "sipTransportGetTransportType";
+
+    tcp = (upper_case) ? "TCP" : "tcp";
+    udp = (upper_case) ? "UDP" : "udp";
+    tls = (upper_case) ? "TLS" : "tls";
+
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return udp;
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        // If ccb is not NULL get the conn type from cc_cfg_table_entry
+        // This would be the case for REGISTER/keep alive messages.
+        // else get from the active ccm entry if available.
+        if (ccb) {
+            ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+        }
+        if (ccm_table_ptr) {
+            conn_type = ccm_table_ptr->ti_common.conn_type;
+        } else if (CCM_Active_Standby_Table.active_ccm_entry != NULL) {
+            conn_type = CCM_Active_Standby_Table.active_ccm_entry->ti_common.conn_type;
+        } else {
+            conn_type = CCM_Device_Specific_Config_Table[PRIMARY_CCM].ti_common.conn_type;
+        }
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        conn_type = CSPS_Config_Table[line - 1].ti_common.conn_type;
+    }
+    switch (conn_type) {
+    case CONN_UDP:
+        return (udp);
+    case CONN_TCP:
+    case CONN_TCP_TMP:
+        return (tcp);
+    case CONN_TLS:
+        return (tls);
+    default:
+        return (NULL);
+    }
+}
+
+/*
+ ** sipTransportGetServerAddrPort
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Wrapper function for the sip dns srv functions.
+ *               Currently code only caters to SIP Proxies. But
+ *               with CCM, the code will have handle the different
+ *               scenarios of retries etc in the CCM environment as
+ *               well.
+ *
+ *  RETURNS: The dns error codes that the invoked functions return.
+ *
+ */
+int
+sipTransportGetServerAddrPort (char *domain, cpr_ip_addr_t *ipaddr_ptr,
+                               uint16_t *port, srv_handle_t *psrv_order,
+                               boolean retried_addr)
+{
+    int rc;
+
+    if (psrv_order == NULL) {
+        rc = sip_dns_gethostbysrvorname(domain, ipaddr_ptr, port);
+    } else {
+        rc = sip_dns_gethostbysrv(domain, ipaddr_ptr, port, psrv_order,
+                                  retried_addr);
+    }
+    return (rc);
+}
+
+/*
+ ** sipTransportGetPrimServerPort
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS: Line id
+ *
+ *  DESCRIPTION: Reads and returns the Primary server port from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: Primary Server port (Currently only SIP Proxy specific)
+ *
+ */
+int
+sipTransportGetPrimServerPort (line_t line)
+{
+    static const char *fname = "sipTransportGetPrimServerPort";
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return (0);
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        if (CCM_Active_Standby_Table.active_ccm_entry != NULL) {
+            return (CCM_Active_Standby_Table.active_ccm_entry->
+                    ti_common.port);
+        } else {
+            return (0);
+        }
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        return (CSPS_Config_Table[line - 1].ti_common.port);
+    }
+}
+
+/*
+ ** sipTransportGetBkupServerPort
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Reads and returns the Backup server port from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: Backup Server port (Currently only  SIP Proxy specific)
+ *
+ */
+int
+sipTransportGetBkupServerPort (line_t line)
+{
+    static const char *fname = "sipTransportGetBkupServerPort";
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return (0);
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr -
+         */
+        return (0);
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        ti_csps_t *ti_csps;
+
+        ti_csps = CSPS_Config_Table[line - 1].ti_specific.ti_csps;
+        return (ti_csps->bkup_pxy_port);
+    }
+}
+
+/*
+ ** sipTransportGetEmerServerPort
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Reads and returns the Emergency server port from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: Emergency Server port (Currently only  SIP Proxy specific)
+ *
+ */
+int
+sipTransportGetEmerServerPort (line_t line)
+{
+    static const char *fname = "sipTransportGetEmerServerPort";
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return (0);
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        return (0);
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        ti_csps_t *ti_csps;
+
+        ti_csps = CSPS_Config_Table[line - 1].ti_specific.ti_csps;
+        return (ti_csps->emer_pxy_port);
+    }
+}
+
+/*
+ ** sipTransportGetOutbProxyPort
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Reads and returns the Outbound server port from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: Outbound Server port (Currently only SIP Proxy specific)
+ *
+ */
+int
+sipTransportGetOutbProxyPort (line_t line)
+{
+    static const char *fname = "sipTransportGetOutbProxyPort";
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return (0);
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        return (0);
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        ti_csps_t *ti_csps;
+
+        ti_csps = CSPS_Config_Table[line - 1].ti_specific.ti_csps;
+        return (ti_csps->outb_pxy_port);
+    }
+}
+
+/*
+ ** sipTransportGetPrimServerAddress
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Reads and returns the Primary server address from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: IP address in buffer (Currently only SIP Proxy specific)
+ *
+ */
+cpr_ip_type
+sipTransportGetPrimServerAddress (line_t line, char *buffer)
+{
+    ti_common_t *ti_common;
+    cpr_ip_type ip_type = CPR_IP_ADDR_IPV4;
+    static const char *fname = "sipTransportGetPrimServerAddress";
+
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return (ip_type);
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        if (CCM_Active_Standby_Table.active_ccm_entry != NULL) {
+            sstrncpy(buffer, CCM_Active_Standby_Table.active_ccm_entry->
+                     ti_common.addr_str, MAX_IPADDR_STR_LEN);
+            ip_type = CCM_Active_Standby_Table.active_ccm_entry->ti_common.addr.type;
+
+        } else {
+            ti_common = &CCM_Device_Specific_Config_Table[PRIMARY_CCM].ti_common;
+            sstrncpy(buffer, ti_common->addr_str, MAX_IPADDR_STR_LEN);
+            ip_type = ti_common->addr.type;
+        }
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        ti_common = &CSPS_Config_Table[line - 1].ti_common;
+        sstrncpy(buffer, ti_common->addr_str, MAX_IPADDR_STR_LEN);
+        ip_type = ti_common->addr.type;
+    }
+
+    return(ip_type);
+}
+
+/*
+ ** sipTransportGetBkupServerAddress
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Reads and returns the Backup server address from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: IP Address as uint32_t and str in buffer (Currently only
+ *           SIP Proxy specific)
+ *
+ */
+uint16_t
+sipTransportGetBkupServerAddress (cpr_ip_addr_t *pip_addr,
+                                  line_t line, char *buffer)
+{
+    static const char *fname = "sipTransportGetBkupServerAddress";
+    *pip_addr = ip_addr_invalid;
+
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return (0);
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         * This would be standby address for the ccm
+         */
+        sstrncpy(buffer, "UNPROVISIONED", MAX_IPADDR_STR_LEN);
+        return (0);
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        ti_csps_t *ti_csps;
+
+        ti_csps = CSPS_Config_Table[line - 1].ti_specific.ti_csps;
+        sstrncpy(buffer, ti_csps->bkup_pxy_addr_str, MAX_IPADDR_STR_LEN);
+        *pip_addr = ti_csps->bkup_pxy_addr;
+        return (1);
+    }
+}
+
+/*
+ ** sipTransportGetEmerServerAddress
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Reads and returns the Emergency server address from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: IP address in buffer (Currently only SIP Proxy specific)
+ *
+ */
+void
+sipTransportGetEmerServerAddress (line_t line, char *buffer)
+{
+    static const char *fname = "sipTransportGetEmerServerAddress";
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return;
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        sstrncpy(buffer, "UNPROVISIONED", MAX_IPADDR_STR_LEN);
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        ti_csps_t *ti_csps;
+
+        ti_csps = CSPS_Config_Table[line - 1].ti_specific.ti_csps;
+        sstrncpy(buffer, ti_csps->emer_pxy_addr_str, MAX_IPADDR_STR_LEN);
+    }
+}
+
+/*
+ ** sipTransportGetOutbProxyAddress
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION: Reads and returns the Outbound server address from the
+ *               local UDP table that is filled at init time and during
+ *               config change notifications.
+ *
+ *  RETURNS: IP address in buffer (Currently only SIP Proxy specific)
+ *
+ */
+void
+sipTransportGetOutbProxyAddress (line_t line, char *buffer)
+{
+    static const char *fname = "sipTransportGetOutbProxyAddress";
+    /*
+     * Checking for line < 1, since we dereference the Config_Tables using
+     * line-1
+     */
+    if (((int)line < 1) || ((int)line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, line);
+        return;
+    }
+
+    if (CC_Config_Table[line - 1].cc_type == CC_CCM) {
+        /*
+         * regmgr
+         */
+        sstrncpy(buffer, "UNPROVISIONED", MAX_IPADDR_STR_LEN);
+    } else {
+        /*
+         * Assume CSPS for now.
+         */
+        ti_csps_t *ti_csps;
+
+        ti_csps = CSPS_Config_Table[line - 1].ti_specific.ti_csps;
+        sstrncpy(buffer, ti_csps->outb_pxy_addr_str, MAX_IPADDR_STR_LEN);
+    }
+}
+
+/*
+ * sipTransportGetServerIPAddr()
+ *
+ * Perform DNS lookup on the primary proxy name and
+ * return its IP address.
+ *
+ * Note: the IP Address is returned in the non-Telecaster
+ *       SIP format, which is not byte reversed.
+ *       Eg. 0xac2c33f8 = 161.44.51.248
+ */
+void
+sipTransportGetServerIPAddr (cpr_ip_addr_t *pip_addr, line_t line)
+{
+    const char     *fname = "sipTransportGetServerIPAddr";
+    cpr_ip_addr_t   IPAddress;
+    uint16_t        port;
+    srv_handle_t     srv_order = NULL;
+    int             dnsErrorCode = 0;
+    char            addr[MAX_IPADDR_STR_LEN];
+    char            obp_address[MAX_IPADDR_STR_LEN];
+
+    CPR_IP_ADDR_INIT(IPAddress);
+
+    sipTransportGetOutbProxyAddress(line, obp_address);
+    if ((cpr_strcasecmp(obp_address, UNPROVISIONED) != 0) &&
+        (obp_address[0] != 0) && (obp_address[0] != '0')) {
+        sstrncpy(addr, obp_address, MAX_IPADDR_STR_LEN);
+    } else {
+        sipTransportGetPrimServerAddress(line, addr);
+    }
+    dnsErrorCode = sipTransportGetServerAddrPort(addr, &IPAddress, &port,
+                                                 &srv_order, FALSE);
+    if (srv_order) {
+        dnsFreeSrvHandle(srv_order);
+    }
+
+    if (dnsErrorCode != DNS_OK) {
+        //CCSIP_DEBUG_TASK(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+        //                  fname, "sipTransportGetServerAddrPort");
+        dnsErrorCode = dnsGetHostByName(addr, &IPAddress, 100, 1);
+    }
+
+    if (dnsErrorCode != 0) {
+        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
+                          fname, "dnsGetHostByName()");
+    }
+    *pip_addr = IPAddress;
+    util_ntohl(pip_addr, &IPAddress);
+}
+
+/*
+ ** sip_regmgr_set_cc_info
+ *
+ *  FILENAME: ip_phone\sip\sip_common_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_regmgr_set_cc_info (line_t line, line_t dn_line,
+                        CC_ID *cc_type, void *cc_table_entry)
+{
+    static const char *fname = "sip_regmgr_set_cc_info";
+    ti_config_table_t **active_standby_table_entry =
+        (ti_config_table_t **) cc_table_entry;
+
+    /*
+     * Checking for dn_line < 1, since we dereference the Config_Tables using
+     * dn_line-1
+     */
+    if (((int)dn_line < 1) || ((int)dn_line > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN <%d> out of bounds.\n",
+                          fname, dn_line);
+        return;
+    }
+
+    *cc_type = CC_Config_Table[dn_line - 1].cc_type;
+    if (*cc_type == CC_CCM) {
+        if (line == REG_BACKUP_CCB) {
+            *active_standby_table_entry =
+                CCM_Active_Standby_Table.standby_ccm_entry;
+        } else {
+            *active_standby_table_entry =
+                CCM_Active_Standby_Table.active_ccm_entry;
+        }
+    }
+}
+
+int
+sipTransportGetCCType (int line, void *cc_table_entry)
+{
+    if (cc_table_entry != NULL) {
+        cc_table_entry = CC_Config_Table[line - 1].cc_table_entry;
+    }
+    return (CC_Config_Table[line - 1].cc_type);
+}
+
+/**
+ *
+ * SIPTransportUDPListenForSipMessages
+ *
+ * Establish a UDP socket to listen to for incoming SIP messages
+ *
+ * Parameters:   None
+ *
+ * Return Value: SIP_OK or SIP_ERROR
+ *
+ */
+int
+SIPTransportUDPListenForSipMessages (void)
+{
+    static const char *fname = "SIPTransportUDPListenForSipMessages";
+    uint32_t local_sip_control_port;
+    cpr_ip_addr_t    local_sip_ip_addr;
+    int            ip_mode = CPR_IP_MODE_IPV4;
+
+    /*
+     * Start listening on port 5060 for incoming SIP messages
+     */
+    CPR_IP_ADDR_INIT(local_sip_ip_addr);
+
+    config_get_value(CFGID_VOIP_CONTROL_PORT, &local_sip_control_port,
+                     sizeof(local_sip_control_port));
+
+#ifdef IPV6_STACK_ENABLED
+   config_get_value(CFGID_IP_ADDR_MODE, &ip_mode, sizeof(ip_mode));
+#endif
+    switch (ip_mode) {
+    case CPR_IP_MODE_IPV4:
+        local_sip_ip_addr.type = CPR_IP_ADDR_IPV4;
+        local_sip_ip_addr.u.ip4 = 0;
+        break;
+    case CPR_IP_MODE_IPV6:
+    case CPR_IP_MODE_DUAL:
+        local_sip_ip_addr = ip_addr_invalid;
+        local_sip_ip_addr.type = CPR_IP_ADDR_IPV6;
+        break;
+    default:
+        break;
+    }
+    /* Based on mode type configure the IP address IPv4 or IPv6 */
+
+
+    if (sip_platform_udp_channel_listen(ip_mode, &listen_socket, &local_sip_ip_addr,
+                                        (uint16_t) local_sip_control_port)
+            != SIP_OK) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_platform_udp_channel_listen(0, %d) "
+                          "returned error.\n", fname, local_sip_control_port);
+        return SIP_ERROR;
+    }
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Listening for SIP messages on UDP port <%d>, handle=<%d>\n",
+                     DEB_F_PREFIX_ARGS(SIP_TRANS, fname), local_sip_control_port, listen_socket);
+
+    return SIP_OK;
+}
+
+static void
+sipTransportCfgTableInit (boolean *cc_udp)
+{
+//    int cc_num;
+    line_t line;
+    CC_ID dev_cc_type = CC_OTHER;
+    uint32_t transport_prot = CONN_UDP;
+    ti_common_t *ti_common;
+    static const char *fname = "sipTransportCfgTableInit";
+
+
+    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Transport Interface init\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname));
+
+    ti_common = &CSPS_Config_Table[0].ti_common;
+    sip_config_get_proxy_addr(1, ti_common->addr_str, sizeof(ti_common->addr_str));
+
+    if (!cpr_strcasecmp(ti_common->addr_str, "USECALLMANAGER")) {
+        dev_cc_type = CC_CCM;
+    }
+    if (dev_cc_type == CC_CCM) {
+        /*
+         * Initialize the ccm table
+         */
+        uint32_t listen_port;
+        CCM_ID ccm_id;
+        ti_ccm_t *ti_ccm;
+
+        memset(CCM_Config_Table, 0,
+               (sizeof(uint32_t) * MAX_CCM * (MAX_REG_LINES + 1)));
+        config_get_value(CFGID_VOIP_CONTROL_PORT, &listen_port,
+                         sizeof(listen_port));
+        config_get_value(CFGID_TRANSPORT_LAYER_PROT, &transport_prot,
+                         sizeof(transport_prot));
+        if (transport_prot != CONN_UDP) {
+            *cc_udp = FALSE;
+        }
+
+        /*
+         * Initialize the dummy entry and point the Active
+         * and standby to it.
+         */
+        CCM_Dummy_Entry.cc_type = CC_CCM;
+        CCM_Dummy_Entry.ti_specific.ti_ccm.ccm_id = MAX_CCM;
+        CCM_Dummy_Entry.ti_common.conn_type = (CONN_TYPE) transport_prot;
+
+        for (ccm_id = PRIMARY_CCM; ccm_id < MAX_CCM; ccm_id++) {
+            uint32_t port;
+            phone_local_tcp_port[ccm_id] = 0;
+            ti_common = &CCM_Device_Specific_Config_Table[ccm_id].ti_common;
+            ti_ccm = &CCM_Device_Specific_Config_Table[ccm_id].ti_specific.ti_ccm;
+
+            CCM_Device_Specific_Config_Table[ccm_id].cc_type = CC_CCM;
+            sip_regmgr_get_config_addr(ccm_id, ti_common->addr_str);
+
+            config_get_value(ccm_config_id_port[ccm_id], &port, sizeof(port));
+            ti_common->port = (uint16_t) port;
+            ti_common->conn_type = ti_common->configured_conn_type = (CONN_TYPE) transport_prot;
+            ti_common->listen_port = (uint16_t) listen_port;
+            ti_common->handle = INVALID_SOCKET;
+            /*
+             * CCM specific config variable read
+             */
+            ti_ccm->ccm_id = (CCM_ID) ccm_id;
+            ti_ccm->sec_level = NON_SECURE;
+            ti_ccm->is_valid = 1;
+            config_get_value(ccm_config_id_sec_level[ccm_id],
+                             &ti_ccm->sec_level, sizeof(ti_ccm->sec_level));
+            config_get_value(ccm_config_id_is_valid[ccm_id],
+                             &ti_ccm->is_valid, sizeof(ti_ccm->is_valid));
+            if ((ti_ccm->sec_level == NON_SECURE) &&
+                (transport_prot == CONN_TLS)) {
+                ti_common->conn_type = CONN_TCP;
+            }
+            for (line = 0; line < MAX_REG_LINES; line++) {
+                CCM_Config_Table[line][ccm_id] =
+                    &CCM_Device_Specific_Config_Table[ccm_id];
+                if (ccm_id == PRIMARY_CCM) {
+                    CC_Config_Table[line].cc_type = CC_CCM;
+                    CC_Config_Table[line].cc_table_entry = (void *)
+                        CCM_Config_Table[ccm_id];
+                }
+            }
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"For CCM%d: line %d Addr: %s Port: %d"
+                                " listen Port: %d transport: %d"
+                                " Sec Level: %d Is Valid: %d\n",
+                                DEB_F_PREFIX_ARGS(SIP_TRANS, fname),
+                                ccm_id, line, ti_common->addr_str,
+                                ti_common->port, ti_common->listen_port,
+                                ti_common->conn_type, ti_ccm->sec_level,
+                                ti_ccm->is_valid);
+        }
+    } else {
+        ti_csps_t *ti_csps = &CSPS_Device_Specific_Config_Table;
+        uint32_t bkup_pxy_port;
+        uint32_t emer_pxy_port;
+        uint32_t outb_pxy_port;
+        uint32_t listen_port;
+
+        sip_config_get_backup_proxy_addr(&(ti_csps->bkup_pxy_addr),
+                                     ti_csps->bkup_pxy_addr_str,
+                                     sizeof(ti_csps->bkup_pxy_addr_str));
+        config_get_value(CFGID_PROXY_BACKUP_PORT, &bkup_pxy_port,
+                         sizeof(bkup_pxy_port));
+        ti_csps->bkup_pxy_port = (uint16_t) bkup_pxy_port;
+        config_get_string(CFGID_PROXY_EMERGENCY, ti_csps->emer_pxy_addr_str,
+                          sizeof(ti_csps->emer_pxy_addr_str));
+        config_get_value(CFGID_PROXY_EMERGENCY_PORT, &emer_pxy_port,
+                         sizeof(emer_pxy_port));
+        ti_csps->emer_pxy_port = (uint16_t) emer_pxy_port;
+        config_get_string(CFGID_OUTBOUND_PROXY, ti_csps->outb_pxy_addr_str,
+                          sizeof(ti_csps->outb_pxy_addr_str));
+        config_get_value(CFGID_OUTBOUND_PROXY_PORT, &outb_pxy_port,
+                         sizeof(outb_pxy_port));
+        ti_csps->outb_pxy_port = (uint16_t) outb_pxy_port;
+
+        config_get_value(CFGID_VOIP_CONTROL_PORT, &listen_port,
+                         sizeof(listen_port));
+        for (line = 0; line < MAX_REG_LINES; line++) {
+            ti_common = &CSPS_Config_Table[line].ti_common;
+            CSPS_Config_Table[line].ti_specific.ti_csps = ti_csps;
+
+            sip_config_get_proxy_addr((line_t)(line + 1), ti_common->addr_str,
+                                      sizeof(ti_common->addr_str));
+            ti_common->port = sip_config_get_proxy_port((line_t) (line + 1));
+            ti_common->conn_type = CONN_UDP;
+            ti_common->listen_port = (uint16_t) listen_port;
+            ti_common->addr = ip_addr_invalid;
+            ti_common->handle = INVALID_SOCKET;
+
+            CC_Config_Table[line].cc_table_entry = (void *) NULL; // NULL for now.
+            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"line %d Addr: %s Port: %d and listen Port: %d\n"
+                                " transport: %d\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname),
+                                line, ti_common->addr_str, ti_common->port,
+                                ti_common->listen_port, ti_common->conn_type);
+            if (line == 0) {
+                ti_csps_t *ti_csps_cfg_table;
+
+                ti_csps_cfg_table = CSPS_Config_Table[line].ti_specific.ti_csps;
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"bkup Addr: %s and Port: %d\n",
+                                    DEB_F_PREFIX_ARGS(SIP_TRANS, fname),
+                                    ti_csps_cfg_table->bkup_pxy_addr_str,
+                                    ti_csps_cfg_table->bkup_pxy_port);
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"emer Addr: %s and Port: %d\n",
+                                    DEB_F_PREFIX_ARGS(SIP_TRANS, fname),
+                                    ti_csps_cfg_table->emer_pxy_addr_str,
+                                    ti_csps_cfg_table->emer_pxy_port);
+                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"outb Addr: %s and Port: %d\n",
+                                    DEB_F_PREFIX_ARGS(SIP_TRANS, fname),
+                                    ti_csps_cfg_table->outb_pxy_addr_str,
+                                    ti_csps_cfg_table->outb_pxy_port);
+            }
+        }
+    }
+}
+
+
+/*
+ ** sipTransportInit
+ *
+ *  FILENAME: sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  Note: Make sure this gets calls during config change notifications.
+ *  from softphone\src-win\global_stub.c's and src-arm-79xx\config.c's
+ *  config_commit() function that calls the prot_config_change_notify().
+ *  RETURNS:
+ *
+ */
+int
+sipTransportInit (void)
+{
+    int result = 0;
+    static const char *fname = "sipTransportInit";
+    boolean cc_udp = TRUE;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"Transport_interface: Init function "
+                     "call !\n", DEB_F_PREFIX_ARGS(SIP_TRANS, fname));
+    /*
+     * Init the cc related info into the cc config table
+     */
+    sipTransportCfgTableInit(&cc_udp);
+    /*
+     * Make sure that the IP stack is up before trying to connect
+     */
+    if (PHNGetState() > STATE_IP_CFG) {
+        if (cc_udp) {
+            /*
+             * By now we know the DHCP is up, so we can
+             * start open sockets for listening for SIP messages and
+             * the UDP channel to the SIP proxy server
+             */
+
+            if (SIPTransportUDPListenForSipMessages() == SIP_ERROR) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"device unable to"
+                                  " receive SIP messages.\n", fname);
+            }
+        } else {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"CCM in non udp mode so not "
+                             "opening separate listen socket.\n",DEB_F_PREFIX_ARGS(SIP_TRANS, fname));
+        }
+        if (sip_regmgr_init() != SIP_OK) {
+            result = SIP_ERROR;
+        }
+    } else {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"IP Stack Not "
+                         "Initialized.\n", fname);
+        result = -1;
+    }
+    return (result);
+}
+
+/*
+ ** sipTransportShutdown
+ *
+ *  FILENAME: sip_common_transport.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS:
+ *
+ */
+void
+sipTransportShutdown ()
+{
+    CCSIP_DEBUG_STATE(DEB_F_PREFIX"Transport_interface: Shutting down!\n", DEB_F_PREFIX_ARGS(SIP_TRANS, "sipTransportShutdown"));
+    sip_regmgr_destroy_cc_conns();
+}
+
+/*
+ ** sipTransportClearServerHandle
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS: ip addr, port, connid
+ *
+ *  DESCRIPTION: This function clears the server handle and port
+ *
+ *  RETURNS: none
+ *
+ */
+void
+sipTransportClearServerHandle (cpr_ip_addr_t *ipaddr, uint16_t port, int connid)
+{
+
+    ti_common_t *ti_common;
+    CCM_ID cc_index;
+
+    CCSIP_DEBUG_TASK(DEB_F_PREFIX"addr 0x%x port %d connid %d\n",
+                     DEB_F_PREFIX_ARGS(SIP_TRANS, "sipTransportClearServerHandle"), ipaddr, port, connid);
+    for (cc_index = PRIMARY_CCM; cc_index < MAX_CCM; cc_index++) {
+        ti_common = &CCM_Device_Specific_Config_Table[cc_index].ti_common;
+        if (util_compare_ip(&(ti_common->addr),ipaddr) && ti_common->port == port) {
+            sip_tcp_purge_entry(connid);
+            ti_common->handle = INVALID_SOCKET;
+            ti_common->listen_port = 0;
+            return;
+        }
+    }
+}
+
+/*
+ ** sipTransportSetServerHandleAndPort
+ *
+ *  FILENAME: ip_phone\sip\sip_common_transport.c
+ *
+ *  PARAMETERS: Line id
+ *
+ *  DESCRIPTION: This function gets the server handle for a particular
+ *               line id.
+ *
+ *  RETURNS: The server handle
+ */
+void
+sipTransportSetServerHandleAndPort (cpr_socket_t socket_handle,
+                                    uint16_t listen_port,
+                                    ti_config_table_t *ccm_table_entry)
+{
+    ti_common_t *ti_common;
+    ti_ccm_t *ti_ccm;
+
+    ti_ccm = &ccm_table_entry->ti_specific.ti_ccm;
+
+    if (ti_ccm->ccm_id < MAX_CCM) {
+        ti_common = &CCM_Device_Specific_Config_Table[ti_ccm->ccm_id].ti_common;
+        ti_common->handle = socket_handle;
+        ti_common->listen_port = listen_port;
+    }
+}
+
+void
+sipTransportSetSIPServer() {
+       char addr_str[MAX_IPADDR_STR_LEN];
+       init_empty_str(addr_str);
+       config_get_string(CFGID_CCM1_ADDRESS, addr_str, MAX_IPADDR_STR_LEN);
+       sstrncpy(CCM_Config_Table[0][0]->ti_common.addr_str, addr_str, MAX_IPADDR_STR_LEN);
+       sstrncpy(CCM_Device_Specific_Config_Table[PRIMARY_CCM].ti_common.addr_str, addr_str, MAX_IPADDR_STR_LEN);
+}
diff --git a/libs/sipcc/core/sipstack/sip_csps_transport.c b/libs/sipcc/core/sipstack/sip_csps_transport.c
new file mode 100644 (file)
index 0000000..9982ab3
--- /dev/null
@@ -0,0 +1,206 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_in.h"
+#include "cpr_types.h"
+#include "phone_types.h"
+#include "cpr_string.h"
+#include "cpr_socket.h"
+#include "ccsip_platform.h"
+#include "sip_common_transport.h"
+#include "sip_csps_transport.h"
+#include "util_string.h"
+
+int
+sip_dns_gethostbysrv (char *domain,
+                      cpr_ip_addr_t *ipaddr_ptr,
+                      uint16_t *port,
+                      srv_handle_t *psrv_order,
+                      boolean retried_addr)
+{
+    int      bLoopDeath = 0;
+    uint16_t tmp_port = 0;
+    int      rc=DNS_ERR_NOHOST;
+
+    while (!bLoopDeath) {
+        /* Try and fetch a proxy using DNS SRV records */
+        rc = dnsGetHostBySRV("sip", "udp", (char *) domain, ipaddr_ptr,
+                              &tmp_port, 100, 1, psrv_order);
+        switch (rc) {
+        case DNS_ERR_TTL_EXPIRED:
+        case DNS_ERR_NOHOST:
+            /* Re-try if all entries for this proxy have expired
+             * or the end of the call record list was reached.
+             */
+            break;
+        default:
+            /* Run don't walk to the nearest exit */
+            bLoopDeath = 1;
+            break;
+        }
+    }
+    if (tmp_port) {
+        *port = tmp_port;
+    }
+    return rc;
+}
+
+int
+sip_dns_gethostbysrvorname (char *hname,
+                            cpr_ip_addr_t *ipaddr_ptr,
+                            uint16_t *port)
+{
+    /*
+     * OK according to rfc2543bis-03
+     * 1. If the destination is an IP address it is used. If no port is
+     *    specified then use 5060
+     * 2. If the destination specifies the default port (5060) or no port
+     *    then try SRV
+     * 3. If the destination specifies a port number other than 5060 or
+     *    there are no SRV records A record lookup
+     */
+    srv_handle_t srv_order = NULL;
+    int rc=DNS_ERR_NOHOST;
+
+    if ((*port == SIP_WELL_KNOWN_PORT) || (*port == 0)) {
+        rc = sip_dns_gethostbysrv(hname, ipaddr_ptr, port, &srv_order, FALSE);
+    }
+    if (rc != DNS_OK) {
+        rc = dnsGetHostByName(hname, ipaddr_ptr, 100, 1);
+    }
+    if (srv_order) {
+        dnsFreeSrvHandle(srv_order);
+    }
+    return rc;
+}
+
+cpr_socket_t
+sipTransportCSPSGetProxyHandleByDN (line_t dn)
+{
+    static const char *fname = "sipTransportCSPSGetProxyHandleByDN";
+    ti_common_t *ti_common;
+
+    if ((((int)dn) < 1) || (((int)dn) > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN %d out of "
+                          "bounds.\n", fname, dn);
+        return INVALID_SOCKET;
+    }
+    ti_common = &CSPS_Config_Table[dn - 1].ti_common;
+    return ((cpr_socket_t) ti_common->handle);
+}
+
+short
+sipTransportCSPSGetProxyPortByDN (line_t dn)
+{
+    static const char *fname = "sipTransportCSPSGetProxyPortByDN";
+    ti_common_t *ti_common;
+
+    if ((((int)dn) < 1) || (((int)dn) > MAX_REG_LINES)) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN %d out of "
+                          "bounds.\n", fname, dn);
+        return (-1);
+    }
+    ti_common = &CSPS_Config_Table[dn - 1].ti_common;
+    return ((short) ti_common->port);
+}
+
+// This function is broken
+uint16_t
+sipTransportCSPSGetProxyAddressByDN(cpr_ip_addr_t *ip_addr, line_t dn)
+{
+//    static const char *fname = "sipTransportCSPSGetProxyAddressByDN";
+//    ti_common_t *ti_common;
+//
+//    if ((((int)dn) < 1) || (((int)dn) > MAX_REG_LINES)) {
+//        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Args check: DN out of "
+//                          "bounds.\n", fname, dn);
+//        return (0);
+//    }
+//
+//    ti_common = &CSPS_Config_Table[dn - 1].ti_common;
+//
+//    ip_addr = &(ti_common->addr);
+//
+    return(1);
+}
+
+/*
+ * sip_config_get_proxy_port()
+ *
+ * Get table entry from the table string and option number
+ */
+uint16_t
+sip_config_get_proxy_port (line_t line)
+{
+    uint32_t port;
+
+    config_get_line_value(CFGID_PROXY_PORT, &port, sizeof(port), line);
+
+    if (port == 0) {
+        config_get_line_value(CFGID_PROXY_PORT, &port, sizeof(port), DEFAULT_LINE);
+    }
+
+    return ((uint16_t) port);
+}
+
+/*
+ * sip_config_get_proxy_addr()
+ *
+ * Get table entry from the table string and option number
+ */
+void
+sip_config_get_proxy_addr (line_t line, char *buffer, int buffer_len)
+{
+    config_get_line_string(CFGID_PROXY_ADDRESS, buffer, line, buffer_len);
+
+    if ((strcmp(buffer, UNPROVISIONED) == 0) || (buffer[0] == '\0')) {
+        config_get_line_string(CFGID_PROXY_ADDRESS, buffer, DEFAULT_LINE,
+                               buffer_len);
+    }
+}
+
+/*
+ * sip_config_get_backup_proxy_addr()
+ *
+ * Get table entry from the table string and option number
+ */
+uint16_t
+sip_config_get_backup_proxy_addr (cpr_ip_addr_t *IPAddress, char *buffer, int buffer_len)
+{
+
+    *IPAddress = ip_addr_invalid;
+
+    config_get_string(CFGID_PROXY_BACKUP, buffer, buffer_len);
+
+    if ((cpr_strcasecmp(buffer, UNPROVISIONED) == 0) || (buffer[0] == 0)) {
+        buffer[0] = 0;
+    } else {
+        (void) str2ip(buffer, IPAddress);
+    }
+    return (1);
+}
+
+/*
+ * sipTransportCSPSClearProxyHandle
+ *
+ * Clear Proxy handle from the table
+ */
+void
+sipTransportCSPSClearProxyHandle (cpr_ip_addr_t *ipaddr,
+                                  uint16_t port,
+                                  cpr_socket_t this_fd)
+{
+    ti_common_t *ti_common;
+    int i;
+
+    for (i = 0; i < MAX_REG_LINES; i++) {
+        ti_common = &CSPS_Config_Table[i].ti_common;
+        if ((ti_common->port == port) &&
+            util_compare_ip(&(ti_common->addr),ipaddr) &&
+            (ti_common->handle == this_fd)) {
+            ti_common->handle = INVALID_SOCKET;
+            return;
+        }
+    }
+}
diff --git a/libs/sipcc/core/sipstack/sip_interface_regmgr.c b/libs/sipcc/core/sipstack/sip_interface_regmgr.c
new file mode 100644 (file)
index 0000000..952cc80
--- /dev/null
@@ -0,0 +1,313 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "cpr_memory.h"
+#include "ccsip_task.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "phntask.h"
+#include "phone.h"
+#include "text_strings.h"
+#include "string_lib.h"
+#include "gsm.h"
+#include "sip_common_transport.h"
+#include "sip_common_regmgr.h"
+#include "sip_interface_regmgr.h"
+#include "ccsip_subsmanager.h"
+#include "platform_api.h"
+
+extern ccm_act_stdby_table_t CCM_Active_Standby_Table;
+extern cc_config_table_t CC_Config_Table[];
+
+/*
+ ** sip_regmgr_send_status
+ *
+ *  FILENAME: ip_phone\sip\sip_interface_regmgr.c
+ *
+ *  PARAMETERS: msg id  and the src task id.
+ *
+ *  DESCRIPTION: Posts the message to the destination task as
+ *               requested.
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_regmgr_send_status (reg_srcs_t src_id, reg_status_t msg_id)
+{
+    static const char fname[] = "sip_regmgr_send_status";
+
+    CCSIP_DEBUG_STATE(DEB_F_PREFIX"src_id: %d msg_id: %d", DEB_F_PREFIX_ARGS(SIP_REG, fname), src_id, msg_id);
+
+    if (msg_id == REG_ALL_FAIL) {
+        //All failed ind to platform
+        ui_reg_all_failed();
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"REG ALL FAILED \n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+    }
+    return;
+}
+
+/*
+ ** sip_regmgr_get_cc_mode
+ *
+ *  FILENAME: ip_phone\sip\sip_interface_regmgr.c
+ *
+ *  PARAMETER: line number for which mode is requested. Initially
+ *             for the ccm it will be same for all linesm, but
+ *             will change when mixed mode support is added.
+ *  DESCRIPTION: returns the current mode the phone is in i.e.
+ *               talking to a ccm -or- not talking to a ccm.
+ *
+ *  RETURNS:
+ *
+ */
+reg_mode_t
+sip_regmgr_get_cc_mode (line_t line)
+{
+    if (CCM_Active_Standby_Table.active_ccm_entry) {
+        return (REG_MODE_CCM);
+    } else {
+        return (REG_MODE_NON_CCM);
+    }
+}
+
+
+boolean
+sip_platform_is_phone_idle (void)
+{
+    if (gsm_is_idle()) {
+        return (TRUE);
+    }
+    return (FALSE);
+}
+
+/*
+ ** sip_platform_fallback_ind
+ *
+ *  FILENAME: ip_phone\sip\sip_interface_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *
+ *  RETURNS:
+ *
+ */
+void
+sip_platform_fallback_ind (CCM_ID ccm_id)
+{
+    static const char fname[] = "sip_platform_fallback_ind";
+    int from_id = CC_TYPE_CCM;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"ccm-id: %d", DEB_F_PREFIX_ARGS(SIP_FALLBACK, fname), ccm_id);
+
+    platform_reg_fallback_ind((void *)(long) from_id);
+}
+
+/*
+ ** sip_platform_failover_ind
+ *
+ *  FILENAME: ip_phone\sip\sip_interface_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS: none
+ *
+ */
+boolean plat_is_network_interface_changed(void );
+void
+sip_platform_failover_ind (CCM_ID ccm_id)
+{
+    static const char fname[] = "sip_platform_failover_ind";
+    int to_id = CC_TYPE_CCM;
+
+    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"ccm-id=%s=%d", DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname),
+        ccm_id == PRIMARY_CCM ? "PRIMARY_CCM" :
+        ccm_id == SECONDARY_CCM ? "SECONDARY_CCM" :
+        ccm_id == TERTIARY_CCM ? "TERTIARY_CCM" : "Unknown",
+        ccm_id);
+
+    if (plat_is_network_interface_changed()) {
+        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"network i/f changed, sending REG_ALL_FAIL instead", DEB_F_PREFIX_ARGS(SIP_FAILOVER, fname));
+        ui_reg_all_failed();
+        return;
+    }
+
+    if (ccm_id == UNUSED_PARAM){
+               to_id = 3;
+       }
+    platform_reg_failover_ind((void *)(long) to_id);
+}
+
+/*
+ ** sip_platform_logout_reset_req
+ *
+ *  FILENAME: ip_phone\sip\sip_platform_logout_reset_req
+ *
+ *  PARAMETERS: void
+ *
+ *  DESCRIPTION: Trigger vPhone auto logout and re-DHCP
+ *
+ *  RETURNS: none
+ *
+ */
+void
+sip_platform_logout_reset_req(void)
+{
+       platform_logout_reset_req();
+}
+
+/*
+ ** sip_platform_set_ccm_status
+ *
+ *  FILENAME: ip_phone\sip\sip_interface_regmgr.c
+ *
+ *  PARAMETERS:
+ *
+ *  DESCRIPTION:
+ *
+ *  RETURNS: none
+ *
+ */
+void
+sip_platform_set_ccm_status (void)
+{
+    static const char fname[] = "sip_platform_set_ccm_status";
+    ti_config_table_t *ccm_table_entry;
+    char dest_addr_str[MAX_IPADDR_STR_LEN];
+
+    CCSIP_DEBUG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
+    ccm_table_entry = CCM_Active_Standby_Table.active_ccm_entry;
+    if (ccm_table_entry) {
+        sstrncpy(dest_addr_str, ccm_table_entry->ti_common.addr_str,
+                 MAX_IPADDR_STR_LEN);
+        CCSIP_DEBUG_STATE(DEB_F_PREFIX"addr str1 %s\n", DEB_F_PREFIX_ARGS(SIP_REG, fname), dest_addr_str);
+
+        ui_set_ccm_conn_status(dest_addr_str, CCM_STATUS_ACTIVE);
+    }
+    ccm_table_entry = CCM_Active_Standby_Table.standby_ccm_entry;
+    if (ccm_table_entry) {
+
+        ui_set_ccm_conn_status(ccm_table_entry->ti_common.addr_str,
+                               CCM_STATUS_STANDBY);
+    }
+}
+
+/*
+ ** sip_regmgr_get_ccm_id
+ *
+ *  FILENAME: ip_phone\sip\sip_interface_regmgr.c
+ *
+ *  PARAMETERS: ccb
+ *
+ *  DESCRIPTION: get ccm id from ccb
+ *
+ *  RETURNS: ccm id
+ */
+CCM_ID
+sip_regmgr_get_ccm_id (ccsipCCB_t *ccb)
+{
+    ti_config_table_t *ccm_table_ptr = NULL;
+
+    ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
+    if (ccm_table_ptr) {
+        return (ccm_table_ptr->ti_specific.ti_ccm.ccm_id);
+    }
+    return (UNUSED_PARAM);
+}
+
+/*
+ ** sip_platform_cc_mode_notify
+ *
+ *  FILENAME: ip_phone\sip\sip_interface_regmgr.c
+ *
+ *  PARAMETERS: mode
+ *
+ *  DESCRIPTION: notify cc mode
+ *
+ *  RETURNS: None
+ */
+void
+sip_platform_cc_mode_notify (void)
+{
+    int mode;
+
+    if (CC_Config_Table[0].cc_type == CC_CCM) {
+        mode = REG_MODE_CCM;
+    } else {
+        mode = REG_MODE_NON_CCM;
+    }
+    platform_cc_mode_notify(mode);
+}
+
+/*
+ ** sip_regmgr_get_sec_level
+ *
+ *  FILENAME: ip_phone\sip\sip_interface_regmgr.c
+ *
+ *  PARAMETER: line number for which mode is requested. Initially
+ *             for the ccm it will be same for all linesm, but
+ *             will change when mixed mode support is added.
+ *  DESCRIPTION: returns the current sec level for the phone
+ *               values will be NON-SECURE, AUTHENTICATED and
+ *               ENCRYPTED.
+ *
+ *  RETURNS: sec level
+ *
+ */
+sec_level_t
+sip_regmgr_get_sec_level (line_t line)
+{
+    ti_config_table_t *ccm_table_entry;
+    ti_ccm_t *ti_ccm;
+
+    if (CCM_Active_Standby_Table.active_ccm_entry) {
+        ccm_table_entry = CCM_Active_Standby_Table.active_ccm_entry;
+        ti_ccm = &ccm_table_entry->ti_specific.ti_ccm;
+        return ((sec_level_t) ti_ccm->sec_level);
+    } else {
+        return (NON_SECURE);
+    }
+}
+
+/*
+ ** sip_regmgr_srtp_fallback_enabled
+ *
+ *  FILENAME: ip_phone\sip\sip_interface_regmgr.c
+ *
+ *  PARAMETER: line number for which mode is requested. Initially
+ *             for the ccm it will be same for all linesm, but
+ *             will change when mixed mode support is added.
+ *  DESCRIPTION: returns whether the SRTP fallback is enabled or not.
+ *
+ *  RETURNS: sec level
+ */
+boolean
+sip_regmgr_srtp_fallback_enabled (line_t line)
+{
+    ccsipCCB_t *ccb;
+    line_t      ndx;
+
+    if ((line == 0) || (line > MAX_REG_LINES)) {
+        /* Invalid Line, requested */
+        return 0;
+    }
+
+    /* Map the (dn)line number to registered CCB */
+    ndx = line - 1 + REG_CCB_START;
+    ccb = sip_sm_get_ccb_by_index(ndx);
+    if (ccb != NULL) {
+        if (ccb->supported_tags & cisco_srtp_fallback_tag) {
+            return (TRUE);
+        }
+    }
+    return (FALSE);
+}
+
diff --git a/libs/sipcc/core/sipstack/sip_platform_task.c b/libs/sipcc/core/sipstack/sip_platform_task.c
new file mode 100644 (file)
index 0000000..853ae3d
--- /dev/null
@@ -0,0 +1,654 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_ipc.h"
+#include "cpr_errno.h"
+#include "cpr_socket.h"
+#include "cpr_in.h"
+#include "cpr_rand.h"
+#include "cpr_string.h"
+#include "cpr_threads.h"
+#include "ccsip_core.h"
+#include "ccsip_task.h"
+#include "sip_platform_task.h"
+#include "ccsip_platform_udp.h"
+#include "sip_common_transport.h"
+#include "phntask.h"
+#include "phone_debug.h"
+#include "util_string.h"
+#include "ccsip_platform_tcp.h"
+#include "ccsip_task.h"
+#include "sip_socket_api.h"
+#include "platform_api.h"
+
+/*---------------------------------------------------------
+ *
+ * Definitions
+ *
+ */
+
+/* The maximum number of messages parsed from the message queue at one time */
+#define MAX_SIP_MESSAGES 8
+
+/* The maximum number of connections allowed */
+#define MAX_SIP_CONNECTIONS (64 - 2)
+
+/* SIP Message queue waiting thread and the main thread IPC names */
+#ifdef __ANDROID__
+#define SIP_MSG_IPC_PATH  "/data/data/com.cisco.telephony.provider/"
+#else
+#define SIP_MSG_IPC_PATH  "/tmp/"
+#endif
+#define SIP_MSG_SERV_NAME "SIP-Main-%d"
+#define SIP_MSG_CLNT_NAME "SIP-MsgQ-%d"
+
+#define SIP_PAUSE_WAIT_IPC_LISTEN_READY_TIME   50  /* 50ms. */
+#define SIP_MAX_WAIT_FOR_IPC_LISTEN_READY    1200  /* 50 * 1200 = 1 minutes */
+
+
+/*---------------------------------------------------------
+ *
+ * Local Variables
+ *
+ */
+fd_set read_fds;
+fd_set write_fds;
+static cpr_socket_t listen_socket = INVALID_SOCKET;
+static cpr_socket_t sip_ipc_serv_socket = INVALID_SOCKET;
+static cpr_socket_t sip_ipc_clnt_socket = INVALID_SOCKET;
+static boolean main_thread_ready = FALSE;
+uint32_t nfds = 0;
+sip_connection_t sip_conn;
+
+/*
+ * Internal message structure between main thread and
+ * message queue waiting thread.
+ */
+typedef struct sip_int_msg_t_ {
+    void            *msg;
+    phn_syshdr_t    *syshdr;
+} sip_int_msg_t;
+
+/* Internal message queue (array) */
+static sip_int_msg_t sip_int_msgq_buf[MAX_SIP_MESSAGES] = {{0,0},{0,0}};
+
+/* Main thread and message queue waiting thread IPC names */
+static const char *sip_IPC_serv_name = SIP_MSG_IPC_PATH SIP_MSG_SERV_NAME;
+static const char *sip_IPC_clnt_name = SIP_MSG_IPC_PATH SIP_MSG_CLNT_NAME;
+static cpr_sockaddr_un_t sip_serv_sock_addr;
+static cpr_sockaddr_un_t sip_clnt_sock_addr;
+
+
+/*---------------------------------------------------------
+ *
+ * Global Variables
+ *
+ */
+extern sipGlobal_t sip;
+extern boolean  sip_reg_all_failed;
+
+
+/*---------------------------------------------------------
+ *
+ * Function declarations
+ *
+ */
+//static void write_to_socket(cpr_socket_t s);
+//static int read_socket(cpr_socket_t s);
+
+/*---------------------------------------------------------
+ *
+ * Functions
+ *
+ */
+
+/**
+ *
+ * sip_platform_task_init
+ *
+ * Initialize the SIP Task
+ *
+ * Parameters:   None
+ *
+ * Return Value: None
+ *
+ */
+static void
+sip_platform_task_init (void)
+{
+    uint16_t i;
+
+    for (i = 0; i < MAX_SIP_CONNECTIONS; i++) {
+        sip_conn.read[i] = INVALID_SOCKET;
+        sip_conn.write[i] = INVALID_SOCKET;
+    }
+
+    /*
+     * Initialize cprSelect call parameters
+     */
+    FD_ZERO(&read_fds);
+    FD_ZERO(&write_fds);
+    return;
+}
+
+/**
+ *  sip_create_IPC_sock creates and bind the socket for IPC.
+ *
+ *  @param[in]name - pointer to the const. character for the
+ *                  IPC address (name) to be bound when the
+ *                  IPC socket is successfully created.
+ *
+ *  @return cpr_socket_t - Returns a valid CPR's socket if
+ *                   the socket is created sucessafully otherwise
+ *                   returns INVALID_SOCKET.
+ *  @pre    (name != NULL)
+ */
+static cpr_socket_t sip_create_IPC_sock (const char *name)
+{
+    const char *fname = "sip_create_IPC_sock";
+    cpr_socket_t sock;
+    cpr_sockaddr_un_t addr;
+
+    /* Create socket */
+    sock = cprSocket(AF_LOCAL, SOCK_DGRAM, 0);
+    if (sock == INVALID_SOCKET) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"cprSocket() returned error"
+                          " errno=%d\n", fname, cpr_errno);
+        return (INVALID_SOCKET);
+    }
+
+    /* Bind to the local socket */
+    cpr_set_sockun_addr(&addr, name, getpid());
+
+    /* make sure file doesn't already exist */
+    unlink( (char *)addr.sun_path);
+
+    /* do the bind */
+    if (cprBind(sock, (cpr_sockaddr_t *)&addr,
+                cpr_sun_len(addr)) == CPR_FAILURE) {
+        (void) sipSocketClose(sock, FALSE);
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"cprBind() failed"
+                          " errno=%d\n", fname, cpr_errno);
+        return (INVALID_SOCKET);
+    }
+    return (sock);
+}
+
+/**
+ *  The function is a thread loop that waits on SIP message queue
+ *  visible for the external components (sip_msgq). The thread is used
+ *  to avoid having the main task loop from having to set a small time
+ *  waiting on select() to poll inter-thread messages. The small waiting
+ *  time on select() to poll internal messages queue increases the
+ *  unnecessary of waking up when system is idle. On the platform that
+ *  power conservative is critical such as handheld phone, waking up
+ *  periodically is not good for battery life.
+ *
+ *  This thread splits the message queue waiting function from the
+ *  main thread waiting on select(). This thread simply listens on the
+ *  internal message queue and signal to the main thread via IPC socket
+ *  or local socket trigger. Therefore the main thread can uniformly
+ *  process internal message event and the network event via select().
+ *  The small internal poll time on select() can be
+ *  avoided.
+ *
+ *  @param[in] arg - pointer to SIP main thread's message queue.
+ *
+ *  @return None.
+ *
+ *  @pre     (arg != NULL)
+ */
+void sip_platform_task_msgqwait (void *arg)
+{
+    const char    *fname = "sip_platform_task_msgqwait";
+    cprMsgQueue_t *msgq = (cprMsgQueue_t *)arg;
+    unsigned int  wait_main_thread = 0;
+    phn_syshdr_t  *syshdr;
+    void          *msg;
+    uint8_t       num_messages = 0;
+    uint8_t       response = 0;
+    boolean       quit_thread = FALSE;
+
+    if (msgq == NULL) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"task msgq is null, exiting\n", fname);
+        return;
+    }
+
+    if (platThreadInit("SIP IPCQ task") != 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to attach thread to JVM\n", fname);
+        return;
+    }
+
+    /*
+     * Wait for SIP main thread ready for IPC connection.
+     */
+    while (!main_thread_ready) {
+        /* Pause for other threads to run while waiting */
+        cprSleep(SIP_PAUSE_WAIT_IPC_LISTEN_READY_TIME);
+
+        wait_main_thread++;
+        if (wait_main_thread > SIP_MAX_WAIT_FOR_IPC_LISTEN_READY) {
+            /* Exceed the number of wait time */
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"timeout waiting for listening IPC"
+                              " socket ready, exiting\n", fname);
+            return;
+        }
+    }
+
+    /*
+     * Adjust relative priority of SIP thread.
+     */
+#ifndef WIN32
+    (void) cprAdjustRelativeThreadPriority(SIP_THREAD_RELATIVE_PRIORITY);
+#else
+    /* Use default priority */
+    (void) cprAdjustRelativeThreadPriority(0);
+#endif
+
+    /*
+     * The main thread is ready. set global client socket address
+     * so that the server can send back response.
+     */
+    cpr_set_sockun_addr(&sip_clnt_sock_addr, sip_IPC_clnt_name, getpid());
+
+    sip_ipc_clnt_socket = sip_create_IPC_sock(sip_IPC_clnt_name);
+
+    if (sip_ipc_clnt_socket == INVALID_SOCKET) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_create_IPC_sock() failed,"
+                          "  exiting\n", fname);
+        return;
+    }
+
+    while (quit_thread == FALSE) {
+        msg = cprGetMessage(msgq, TRUE, (void **) &syshdr);
+        while (msg != NULL) {
+            /*
+             * There is a message to be forwarded to the main SIP
+             * thread for processing.
+             */
+            sip_int_msgq_buf[num_messages].msg    = msg;
+            sip_int_msgq_buf[num_messages].syshdr = syshdr;
+            num_messages++;
+
+            switch (syshdr->Cmd) {
+            case THREAD_UNLOAD:
+                quit_thread = TRUE;
+                    break;
+                default:
+                    break;
+            }
+
+            if (num_messages == MAX_SIP_MESSAGES) {
+                /*
+                 * Limit the number of messages passed to the main SIP
+                 * thread to MAX_SIP_MESSAGES since SIP main thread only
+                 * process messages up to MAX_SIP_MESSAGES at a time.
+                 */
+                break;
+            }
+            /*
+             * Check to see if there is more message on the queue
+             * before sending IPC message trigger to the main SIP
+             * thread. This is to minimize the overhead of the
+             * the main SIP thread in processing select().
+             */
+            msg = cprGetMessage(msgq, 0, (void **) &syshdr);
+        }
+
+        if (num_messages) {
+            CCSIP_DEBUG_TASK(DEB_F_PREFIX"%d msg available on msgq\n", DEB_F_PREFIX_ARGS(SIP_MSG_QUE, fname), num_messages);
+            /*
+             * There are some number of messages sent to the main thread,
+             * trigger the main SIP thread via IPC to process the message.
+             */
+            if (cprSendTo(sip_ipc_clnt_socket, (void *)&num_messages,
+                          sizeof(num_messages), 0,
+                          (cpr_sockaddr_t *)&sip_serv_sock_addr,
+                          cpr_sun_len(sip_serv_sock_addr)) < 0) {
+                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"send IPC failed errno=%d\n", fname, cpr_errno);
+            }
+
+            if (FALSE == quit_thread) {
+               /*
+                * Wait for main thread to signal us to get more message.
+                */
+               if (cprRecvFrom(sip_ipc_clnt_socket, &response,
+                               sizeof(response), 0, NULL, NULL) < 0) {
+                       CCSIP_DEBUG_ERROR(SIP_F_PREFIX"read IPC failed:"
+                                       " errno=%d\n", fname, cpr_errno);
+               }
+               num_messages = 0;
+            }
+        }
+    }
+}
+
+/**
+ *  sip_process_int_msg - process internal IPC message from the
+ *  the message queue waiting thread.
+ *
+ *  @param - none.
+ *
+ *  @return none.
+ */
+static void sip_process_int_msg (void)
+{
+    const char    *fname = "sip_process_int_msg";
+    ssize_t       rcv_len;
+    uint8_t       num_messages = 0;
+    uint8_t       response = 0;
+    sip_int_msg_t *int_msg;
+    void          *msg;
+    phn_syshdr_t  *syshdr;
+
+    /* read the msg count from the IPC socket */
+    rcv_len = cprRecvFrom(sip_ipc_serv_socket, &num_messages,
+                          sizeof(num_messages), 0, NULL, NULL);
+
+    if (rcv_len < 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"read IPC failed:"
+                          " errno=%d\n", fname, cpr_errno);
+        return;
+    }
+
+    if (num_messages == 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"message queue is empty!\n", fname);
+        return;
+    }
+
+    if (num_messages > MAX_SIP_MESSAGES) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"number of  messages on queue exceeds maximum %d\n", fname,
+                          num_messages);
+        num_messages = MAX_SIP_MESSAGES;
+    }
+
+    /* process messages */
+    int_msg = &sip_int_msgq_buf[0];
+    while (num_messages) {
+        msg    = int_msg->msg;
+        syshdr = int_msg->syshdr;
+        if (msg != NULL && syshdr != NULL) {
+            SIPTaskProcessListEvent(syshdr->Cmd, msg, syshdr->Usr.UsrPtr,
+                syshdr->Len);
+            cprReleaseSysHeader(syshdr);
+
+            int_msg->msg    = NULL;
+            int_msg->syshdr = NULL;
+        }
+
+        num_messages--;  /* one less message to work on */
+        int_msg++;       /* advance to the next message */
+    }
+
+    /*
+     * Signal message queue waiting thread to get more messages.
+     */
+    if (cprSendTo(sip_ipc_serv_socket, (void *)&response,
+                  sizeof(response), 0,
+                  (cpr_sockaddr_t *)&sip_clnt_sock_addr,
+                  cpr_sun_len(sip_clnt_sock_addr)) < 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"%d sending IPC\n", fname);
+    }
+}
+
+/**
+ *
+ * sip_platform_task_loop
+ *
+ * Run the SIP task
+ *
+ * Parameters: arg - SIP message queue
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_loop (void *arg)
+{
+    static const char *fname = "sip_platform_task_loop";
+    int pending_operations;
+    uint16_t i;
+    fd_set sip_read_fds;
+    fd_set sip_write_fds;
+    sip_tcp_conn_t *entry;
+
+    sip_msgq = (cprMsgQueue_t) arg;
+    if (!sip_msgq) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_msgq is null, exiting\n", fname);
+        return;
+    }
+    sip.msgQueue = sip_msgq;
+
+    sip_platform_task_init();
+    /*
+     * Initialize the SIP task
+     */
+    SIPTaskInit();
+
+    if (platThreadInit("SIPStack Task") != 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to attach thread to JVM\n", fname);
+        return;
+    }
+
+    /*
+     * Adjust relative priority of SIP thread.
+     */
+#ifndef WIN32
+    (void) cprAdjustRelativeThreadPriority(SIP_THREAD_RELATIVE_PRIORITY);
+#else
+    /* Use default priority */
+    (void) cprAdjustRelativeThreadPriority(0);
+#endif
+
+    /*
+     * Setup IPC socket addresses for main thread (server)
+     */
+    cpr_set_sockun_addr(&sip_serv_sock_addr, sip_IPC_serv_name, getpid());
+
+    /*
+     * Create IPC between the message queue thread and this main
+     * thread.
+     */
+    sip_ipc_serv_socket = sip_create_IPC_sock(sip_IPC_serv_name);
+
+    if (sip_ipc_serv_socket == INVALID_SOCKET) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_create_IPC_sock() failed:"
+                          " errno=%d\n", fname, cpr_errno);
+        return;
+    }
+
+    /*
+     * On Win32 platform, the random seed is stored per thread; therefore,
+     * each thread needs to seed the random number.  It is recommended by
+     * MS to do the following to ensure randomness across application
+     * restarts.
+     */
+    cpr_srand((unsigned int)time(NULL));
+
+    /*
+     * Set read IPC socket
+     */
+    sip_platform_task_set_read_socket(sip_ipc_serv_socket);
+
+    /*
+     * Let the message queue waiting thread know that the main
+     * thread is ready.
+     */
+    main_thread_ready = TRUE;
+
+    /*
+     * Main Event Loop
+     */
+    while (TRUE) {
+        /*
+         * Wait on events or timeout
+         */
+        sip_read_fds = read_fds;
+
+        // start off by init to zero
+        FD_ZERO(&sip_write_fds);
+        // now look for sockets where data has been queued up
+        for (i = 0; i < MAX_CONNECTIONS; i++) {
+            entry = sip_tcp_conn_tab + i;
+            if (-1 != entry->fd && entry->sendQueue && sll_count(entry->sendQueue)) {
+                FD_SET(entry->fd, &sip_write_fds);
+            }
+        }
+
+        pending_operations = cprSelect((nfds + 1),
+                                       &sip_read_fds,
+                                       &sip_write_fds,
+                                       NULL, NULL);
+        if (pending_operations == SOCKET_ERROR) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"cprSelect() failed: errno=%d."
+                " Recover by initiating sip restart\n",
+                              fname, cpr_errno);
+            /*
+             * If we have come here, then either read socket related to
+             * sip_ipc_serv_socket has got corrupted, or one of the write
+             * socket related to cucm tcp/tls connection.
+             * We will recover, by first clearing all fds, then re-establishing
+             * the connection with sip-msgq by listening on
+             * sip_ipc_serv_socket.
+             */
+            sip_platform_task_init(); /* this clear FDs */
+            sip_platform_task_set_read_socket(sip_ipc_serv_socket);
+
+            /*
+             * Since all sockets fds have been cleared above, we can not anyway
+             * send or receive msg from cucm. So, there is no point
+             * trying to send registration cancel msg to cucm. Also, a
+             * call may be active, and in that case we do not want to
+             * un-register. So, by setting sip_reg_all_failed to true, we
+             * make sure that no registration cancelation attempt is made.
+             */
+            sip_reg_all_failed = TRUE;
+            platform_reset_req(DEVICE_RESTART);
+            continue;
+        } else if (pending_operations) {
+            /*
+             * Listen socket is set only if UDP transport has been
+             * configured. So see if the select return was for read
+             * on the listen socket.
+             */
+            if ((listen_socket != INVALID_SOCKET) &&
+                (sip.taskInited == TRUE) &&
+                FD_ISSET(listen_socket, &sip_read_fds)) {
+                sip_platform_udp_read_socket(listen_socket);
+                pending_operations--;
+            }
+
+            /*
+             * Check IPC for internal message queue
+             */
+            if (FD_ISSET(sip_ipc_serv_socket, &sip_read_fds)) {
+                /* read the message to flush the buffer */
+                sip_process_int_msg();
+                pending_operations--;
+            }
+
+            /*
+             * Check all sockets for stuff to do
+             */
+            for (i = 0; ((i < MAX_SIP_CONNECTIONS) &&
+                         (pending_operations > 0)); i++) {
+                if ((sip_conn.read[i] != INVALID_SOCKET) &&
+                    FD_ISSET(sip_conn.read[i], &sip_read_fds)) {
+                    /*
+                     * Assume tcp
+                     */
+                    sip_tcp_read_socket(sip_conn.read[i]);
+                    pending_operations--;
+                }
+                if ((sip_conn.write[i] != INVALID_SOCKET) &&
+                    FD_ISSET(sip_conn.write[i], &sip_write_fds)) {
+                    int connid;
+
+                    connid = sip_tcp_fd_to_connid(sip_conn.write[i]);
+                    if (connid >= 0) {
+                        sip_tcp_resend(connid);
+                    }
+                    pending_operations--;
+                }
+            }
+        }
+    }
+}
+
+/**
+ *
+ * sip_platform_task_set_listen_socket
+ *
+ * Mark the socket for cpr_select to be read
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_set_listen_socket (cpr_socket_t s)
+{
+    listen_socket = s;
+    sip_platform_task_set_read_socket(s);
+}
+
+/**
+ *
+ * sip_platform_task_set_read_socket
+ *
+ * Mark the socket for cpr_select to be read
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_set_read_socket (cpr_socket_t s)
+{
+    if (s != INVALID_SOCKET) {
+        FD_SET(s, &read_fds);
+        nfds = MAX(nfds, (uint32_t)s);
+    }
+}
+
+/**
+ *
+ * sip_platform_task_reset_listen_socket
+ *
+ * Mark the socket  as INVALID
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_reset_listen_socket (cpr_socket_t s)
+{
+    sip_platform_task_clr_read_socket(s);
+    listen_socket = INVALID_SOCKET;
+}
+
+/**
+ *
+ * sip_platform_task_clr_read_socket
+ *
+ * Mark the socket for cpr_select to be read
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_clr_read_socket (cpr_socket_t s)
+{
+    if (s != INVALID_SOCKET) {
+        FD_CLR(s, &read_fds);
+    }
+}
+
diff --git a/libs/sipcc/core/sipstack/sip_platform_win32_task.c b/libs/sipcc/core/sipstack/sip_platform_win32_task.c
new file mode 100755 (executable)
index 0000000..c9760b1
--- /dev/null
@@ -0,0 +1,340 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_ipc.h"
+#include "cpr_errno.h"
+#include "cpr_socket.h"
+#include "cpr_in.h"
+#include "cpr_rand.h"
+#include "cpr_string.h"
+#include "cpr_threads.h"
+#include "ccsip_core.h"
+#include "ccsip_task.h"
+#include "sip_platform_task.h"
+#include "ccsip_platform_udp.h"
+#include "sip_common_transport.h"
+#include "sip_interface_regmgr.h"
+#include "phntask.h"
+#include "phone_debug.h"
+#include "util_string.h"
+#include "ccsip_platform_tcp.h"
+#include "ccsip_task.h"
+
+/*---------------------------------------------------------
+ *
+ * Definitions
+ *
+ */
+
+/* The maximum number of messages parsed from the message queue at one time */
+#define MAX_SIP_MESSAGES 8
+
+/* The maximum number of connections allowed */
+#define MAX_SIP_CONNECTIONS (64 - 2)
+
+/* The socket select waiting time out values */
+#define SIP_SELECT_NORMAL_TIMEOUT   25000    /* normal select timeout in usec*/
+#define SIP_SELECT_QUICK_TIMEOUT        0    /* quick select timeout in usec */
+
+/*---------------------------------------------------------
+ *
+ * Local Variables
+ *
+ */
+fd_set read_fds;
+fd_set write_fds;
+static cpr_socket_t listen_socket = INVALID_SOCKET;
+uint32_t nfds = 0;
+sip_connection_t sip_conn;
+
+
+/*---------------------------------------------------------
+ *
+ * Global Variables
+ *
+ */
+extern sipGlobal_t sip;
+
+
+/*---------------------------------------------------------
+ *
+ * Function declarations
+ *
+ */
+//static void write_to_socket(cpr_socket_t s);
+//static int read_socket(cpr_socket_t s);
+
+
+/*---------------------------------------------------------
+ *
+ * Functions
+ *
+ */
+
+/**
+ *
+ * sip_platform_task_init
+ *
+ * Initialize the SIP Task
+ *
+ * Parameters:   None
+ *
+ * Return Value: None
+ *
+ */
+static void
+sip_platform_task_init (void)
+{
+    uint16_t i;
+
+    for (i = 0; i < MAX_SIP_CONNECTIONS; i++) {
+        sip_conn.read[i] = INVALID_SOCKET;
+        sip_conn.write[i] = INVALID_SOCKET;
+    }
+
+    /*
+     * Initialize cprSelect call parameters
+     */
+    FD_ZERO(&read_fds);
+    FD_ZERO(&write_fds);
+    return;
+}
+
+
+/**
+ *
+ * sip_platform_task_loop
+ *
+ * Run the SIP task
+ *
+ * Parameters: arg - SIP message queue
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_loop (void *arg)
+{
+    static const char *fname = "sip_platform_task_loop";
+    int pending_operations;
+    struct cpr_timeval timeout;
+    void *msg;
+    uint32_t cmd;
+    uint16_t len;
+    void *usr;
+    phn_syshdr_t *syshdr;
+    uint16_t i;
+    fd_set sip_read_fds;
+//    fd_set sip_write_fds;
+
+    sip_msgq = (cprMsgQueue_t) arg;
+    if (!sip_msgq) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"sip_msgq is null, exiting\n", fname);
+        return;
+    }
+    sip.msgQueue = sip_msgq;
+
+    sip_platform_task_init();
+    /*
+     * Initialize the SIP task
+     */
+    SIPTaskInit();
+
+    if (platThreadInit("sip_platform_task_loop") != 0) {
+        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to attach thread to JVM\n", fname);
+        return;
+    }
+
+    /*
+     * Adjust relative priority of SIP thread.
+     */
+    (void) cprAdjustRelativeThreadPriority(SIP_THREAD_RELATIVE_PRIORITY);
+
+    /*
+     * Set the SIP task timeout at 25 milliseconds
+     */
+    timeout.tv_sec  = 0;
+    timeout.tv_usec = SIP_SELECT_NORMAL_TIMEOUT;
+
+    /*
+     * On Win32 platform, the random seed is stored per thread; therefore,
+     * each thread needs to seed the random number.  It is recommended by
+     * MS to do the following to ensure randomness across application
+     * restarts.
+     */
+    cpr_srand((unsigned int)time(NULL));
+
+    /*
+     * Main Event Loop
+     */
+    while (TRUE) {
+        /*
+         * Wait on events or timeout
+         */
+        sip_read_fds = read_fds;
+
+//        sip_write_fds = write_fds;
+        pending_operations = cprSelect((nfds + 1),
+                                       &sip_read_fds,
+                                       NULL,
+                                       NULL, &timeout);
+        if (pending_operations == SOCKET_ERROR) {
+            CCSIP_DEBUG_ERROR(SIP_F_PREFIX"cprSelect() failed: errno=%d\n",
+                              fname, cpr_errno);
+        } else if (pending_operations) {
+            /*
+             * Listen socket is set only if UDP transport has been
+             * configured. So see if the select return was for read
+             * on the listen socket.
+             */
+            if ((listen_socket != INVALID_SOCKET) &&
+                (sip.taskInited == TRUE) &&
+                FD_ISSET(listen_socket, &sip_read_fds)) {
+                sip_platform_udp_read_socket(listen_socket);
+                pending_operations--;
+            }
+            /*
+             * Check all sockets for stuff to do
+             */
+            for (i = 0; ((i < MAX_SIP_CONNECTIONS) &&
+                         (pending_operations != 0)); i++) {
+                if ((sip_conn.read[i] != INVALID_SOCKET) &&
+                    FD_ISSET(sip_conn.read[i], &sip_read_fds)) {
+                    /*
+                     * Assume tcp
+                     */
+                    sip_tcp_read_socket(sip_conn.read[i]);
+                    pending_operations--;
+                }
+                               /*
+                if ((sip_conn.write[i] != INVALID_SOCKET) &&
+                    FD_ISSET(sip_conn.write[i], &sip_write_fds)) {
+                    int connid;
+
+                    connid = sip_tcp_fd_to_connid(sip_conn.write[i]);
+                    if (connid >= 0) {
+                        sip_tcp_resend(connid);
+                    }
+                    pending_operations--;
+                }
+                               */
+            }
+        }
+
+        /*
+         * Process all messages on the message queue
+         * (e.g. timer callbacks)
+         */
+        i = 0;
+        while (i++ < MAX_SIP_MESSAGES) {
+            msg = cprGetMessage(sip_msgq, FALSE, (void **) &syshdr);
+            if (msg != NULL) {
+                cmd = syshdr->Cmd;
+                len = syshdr->Len;
+                usr = syshdr->Usr.UsrPtr;
+                SIPTaskProcessListEvent(cmd, msg, usr, len);
+                cprReleaseSysHeader(syshdr);
+                syshdr = NULL;
+            } else {
+                /* Stop checking for msgs if the queue is empty */
+                if (syshdr != NULL) {
+                    cprReleaseSysHeader(syshdr);
+                    syshdr = NULL;
+                }
+                break;
+            }
+        }
+
+        /*
+         * If message servicing loop reaches the maximum loop limit
+         * it is possible that there are more messages left on the message
+         * queue. Shorten the socket select time out to come back and
+         * check the message queue for the message that might be left on
+         * the queue.
+         */
+        if (i >= MAX_SIP_MESSAGES) {
+            timeout.tv_usec = SIP_SELECT_QUICK_TIMEOUT;
+        } else {
+            /* set normal time out value to poll msg. queue */
+            timeout.tv_usec = SIP_SELECT_NORMAL_TIMEOUT;
+        }
+    }
+}
+
+/**
+ *
+ * sip_platform_task_set_listen_socket
+ *
+ * Mark the socket for cpr_select to be read
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_set_listen_socket (cpr_socket_t s)
+{
+    listen_socket = s;
+    sip_platform_task_set_read_socket(s);
+}
+
+/**
+ *
+ * sip_platform_task_set_read_socket
+ *
+ * Mark the socket for cpr_select to be read
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_set_read_socket (cpr_socket_t s)
+{
+    if (s != INVALID_SOCKET) {
+        FD_SET(s, &read_fds);
+        nfds = MAX(nfds, (uint32_t)s);
+    }
+}
+
+/**
+ *
+ * sip_platform_task_reset_listen_socket
+ *
+ * Mark the socket  as INVALID
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_reset_listen_socket (cpr_socket_t s)
+{
+    sip_platform_task_clr_read_socket(s);
+    listen_socket = INVALID_SOCKET;
+}
+
+/**
+ *
+ * sip_platform_task_clr_read_socket
+ *
+ * Mark the socket for cpr_select to be read
+ *
+ * Parameters:   s - the socket
+ *
+ * Return Value: None
+ *
+ */
+void
+sip_platform_task_clr_read_socket (cpr_socket_t s)
+{
+    if (s != INVALID_SOCKET) {
+        FD_CLR(s, &read_fds);
+    }
+}
+
diff --git a/libs/sipcc/core/src-common/configapp.c b/libs/sipcc/core/src-common/configapp.c
new file mode 100644 (file)
index 0000000..849a55f
--- /dev/null
@@ -0,0 +1,145 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_timers.h"
+#include "cpr_strings.h"
+#include "ccsip_subsmanager.h"
+#include "subapi.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "phntask.h"
+
+int32_t g_configappDebug = 1;
+
+extern void update_kpmlconfig (int kpmlVal);
+/*
+ *  Function: configapp_init()
+ *
+ *  Parameters: none
+ *
+ *  Description: Register with Subscription manager for any imcoming
+ *              config subscribe messages
+ *
+ *  Returns: None
+ */
+void
+configapp_init (void)
+{
+    static const char fname[] = "configapp_init";
+
+    CONFIGAPP_DEBUG(DEB_F_PREFIX"Subscribing to SUB/NOT manager.\n",
+                    DEB_F_PREFIX_ARGS(CONFIG_APP, fname));
+
+    (void) sub_int_subnot_register(CC_SRC_MISC_APP, CC_SRC_SIP,
+                                   CC_SUBSCRIPTIONS_CONFIGAPP,
+                                   NULL, CC_SRC_MISC_APP,
+                                   SUB_MSG_CONFIGAPP_SUBSCRIBE, NULL,
+                                   SUB_MSG_CONFIGAPP_TERMINATE, 0, 0);
+
+}
+
+
+/*
+ *  Function: configapp_shutdown()
+ *
+ *  Parameters: none
+ *
+ *  Description: Currently a no-op.
+ *
+ *  Returns: None
+ */
+void
+configapp_shutdown (void)
+{
+}
+
+
+/*
+ *  Function: configapp_free_event_data()
+ *
+ *  Parameters: ccsip_event_data_t*
+ *
+ *  Description: Frees the event data after the processing is complete
+ *
+ *  Returns: None
+ */
+void
+configapp_free_event_data (ccsip_event_data_t *data)
+{
+    ccsip_event_data_t *next_data;
+
+    while(data) {
+        next_data = data->next;
+        cpr_free(data);
+        data = next_data;
+    }
+}
+
+
+/*
+ *  Function: configapp_process_request()
+ *
+ *  Parameters: ccsip_sub_not_data_t*
+ *
+ *  Description: Processes the kpml config update request. Invokes the
+ *               jni update_kpmlconfig() so the java side can
+ *               trigger the config
+ *               change and also to initialize the dialplan.
+ *
+ *  Returns: None
+ */
+
+void
+configapp_process_request (ccsip_sub_not_data_t *msg)
+{
+    static const char fname[] = "configapp_process_request";
+    ConfigApp_req_data_t *configdata;
+
+    configdata = &(msg->u.subs_ind_data.eventData->u.configapp_data);
+
+    update_kpmlconfig(configdata->sip_profile.kpml_val);
+    CONFIGAPP_DEBUG(DEB_F_PREFIX"Updated kpml config value to %d.\n",
+                           DEB_F_PREFIX_ARGS(CONFIG_APP, fname),
+                           configdata->sip_profile.kpml_val);
+
+    (void)sub_int_subscribe_ack(CC_SRC_MISC_APP, CC_SRC_SIP, msg->sub_id,
+                                (uint16_t)SIP_SUCCESS_SETUP, 0);
+
+    (void)sub_int_subscribe_term(msg->sub_id, TRUE, 0, CC_SUBSCRIPTIONS_CONFIGAPP);
+    configapp_free_event_data(msg->u.subs_ind_data.eventData);
+    return;
+}
+
+
+
+/*
+ *  Function: configapp_process_msg()
+ *
+ *  Parameters: ccsip_sub_not_data_t*
+ *
+ *  Description: Determines if it is kpmlconfig update request.
+ *
+ *  Returns: None
+ */
+void
+configapp_process_msg (uint32_t cmd, void *msg)
+{
+    static const char fname[] = "configapp_process_msg";
+
+    switch (cmd) {
+    case SUB_MSG_CONFIGAPP_SUBSCRIBE:
+         configapp_process_request((ccsip_sub_not_data_t *)msg);
+         break;
+    case SUB_MSG_CONFIGAPP_TERMINATE:
+         break;
+    default:
+         CONFIGAPP_DEBUG(DEB_F_PREFIX"Received invalid event.\n",
+                         DEB_F_PREFIX_ARGS(CONFIG_APP, fname));
+         break;
+    }
+}
+
+
diff --git a/libs/sipcc/core/src-common/dialplan.c b/libs/sipcc/core/src-common/dialplan.c
new file mode 100755 (executable)
index 0000000..36b1cd3
--- /dev/null
@@ -0,0 +1,1207 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+#include <limits.h>
+
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "xml_defs.h"
+#include "logger.h"
+#include "logmsg.h"
+#include "util_parse.h"
+#include "debug.h"
+#include "phone_types.h"
+#include "regmgrapi.h"
+
+
+#include "upgrade.h"
+#include "dialplan.h"
+
+extern char DirectoryBuffer[DIALPLAN_MAX_SIZE];
+
+/*
+ * Tones with Bellcore- are Bellcore defined tones.
+ * Tones with Cisco- are our tones.
+ * The order of these names MUST match the order of
+ * the vcm_tones_t type in vcm.h
+ */
+const char *tone_names[] = {
+    "Bellcore-Inside",
+    "Bellcore-Outside",
+    "Bellcore-Busy",
+    "Bellcore-Alerting",
+    "Bellcore-BusyVerify",
+    "Bellcore-Stutter",
+    "Bellcore-MsgWaiting",
+    "Bellcore-Reorder",
+    "Bellcore-CallWaiting",
+    "Bellcore-Cw2",
+    "Bellcore-Cw3",
+    "Bellcore-Cw4",
+    "Bellcore-Hold",
+    "Bellcore-Confirmation",
+    "Bellcore-Permanent",
+    "Bellcore-Reminder",
+    "Bellcore-None",
+    "Cisco-ZipZip",
+    "Cisco-Zip",
+    "Cisco-BeepBonk"
+};
+
+static struct DialTemplate *basetemplate;
+char DialTemplateFile[MAX_TEMPLATE_LENGTH];
+char g_dp_version_stamp[MAX_DP_VERSION_STAMP_LEN];
+
+/*
+ *  Function: addbytes()
+ *
+ *  Parameters:
+ *
+ *  Description:
+ *
+ *  Returns: None
+ */
+static void
+addbytes (char **output, int *outlen, const char *input, int inlen)
+{
+    char *target = *output;
+
+    if (inlen == -1) {
+        inlen = strlen(input);
+    }
+    if (inlen >= *outlen) {
+        inlen = *outlen - 1;
+    }
+    memcpy(target, input, inlen);
+    target += inlen;
+    *outlen += inlen;
+    *output = target;
+    /*
+     * Null terminate the result
+     */
+    *target = '\0';
+}
+
+/*
+ *  Function: poundDialingEnabled()
+ *
+ *  Parameters: None
+ *
+ *  Description: Determines if '#' is treated as a "dial now" character
+ *
+ *  Returns: TRUE if # is treated as end of dial signal
+ *           FALSE if # is treated as a dialed digit
+ */
+static boolean
+poundDialingEnabled (void)
+{
+    if (sip_regmgr_get_cc_mode(1) == REG_MODE_NON_CCM) {
+        /*
+         * Operating in Peer-to-Peer SIP mode, allow # dialing
+         */
+        return (TRUE);
+    } else {
+        return (FALSE);
+    }
+}
+
+/*
+ *  Function: isDialedDigit()
+ *
+ *  Parameters: input - single char
+ *
+ *  Description: Determine if the char is 0-9 or +, *
+ *               # is matched here unless it is set to
+ *               be used as "dial immediately"
+ *
+ *  Returns: DialMatchAction
+ */
+boolean
+isDialedDigit (char input)
+{
+    boolean result = FALSE;
+
+    if (!isdigit(input)) {
+        if ((input == '*') || (input == '+') || ((input == '#') && (!poundDialingEnabled()))) {
+            result = TRUE;
+        }
+    } else {
+        result = TRUE;
+    }
+
+    return (result);
+}
+
+/*
+ *  Function: MatchLineNumber()
+ *
+ *  Parameters: templateLine - line number specified in Dial Plan
+ *              line         - line number to match
+ *
+ *  Description: The template line numbers are initialized to zero.
+ *               Zero means that all lines match. If the Line parm
+ *               is specified in the Dial Plan template, then its
+ *               value must match the specified line.
+ *^M
+ *  Returns: boolean
+ */
+static boolean
+MatchLineNumber (const line_t templateLine, const line_t line)
+{
+    /* Zero in the template matches any line. */
+    return (boolean) ((templateLine == line) || (templateLine == 0));
+}
+
+/*
+ *  Function: MatchDialTemplate()
+ *
+ *  Parameters: pattern - pattern string to match
+ *              line    - line number to match
+ *                        (May be 0 to match all lines)
+ *              timeout - returned dial timeout in seconds
+ *                        (May be NULL to not get a timeout)
+ *              rewrite - buffer to hold rewritten string
+ *                        (May be NULL for no rewrite)
+ *              rewritelen - Bytes available in the buffer to write to
+ *              routemode - pointer to location to hold route mode returned
+ *                          (May be NULL to not get a routemode)
+ *              tone - pointer to location to hold tone returned
+ *
+ *  Description: Find the best template to match a pattern
+ *
+ *  Returns: DialMatchAction
+ */
+DialMatchAction
+MatchDialTemplate (const char *pattern,
+                   const line_t line,
+                   int *timeout,
+                   char *rewrite,
+                   int rewritelen,
+                   RouteMode *pRouteMode,
+                   vcm_tones_t *pTone)
+{
+    DialMatchAction result = DIAL_NOMATCH;
+    struct DialTemplate *ptempl = basetemplate;
+    struct DialTemplate *pbestmatch = NULL;
+    boolean bestmatch_dialnow = FALSE;
+    int best_comma_count = 0;
+    DialMatchAction partialmatch_type = DIAL_NOMATCH;
+    boolean partialmatch = FALSE;
+
+    int matchlen = 0;
+    int partialmatchlen = 0;
+    int givedialtone = 0;
+    int comma_counter = 0;
+
+    /*
+     * We need to provide a default rewrite string in case we do not have any template matches.
+     * This happens when there is no template match (such as no * pattern) and when they have
+     * no dial plan file at all
+     */
+    if (rewrite != NULL) {
+        char *output = rewrite;
+        int room = rewritelen;
+
+        addbytes(&output, &room, pattern, -1);
+    }
+
+    /*
+     * If the dialplan is empty, check to see if a # is in the pattern
+     * and return DIAL_IMMEDIATELY. If not go ahead and return
+     * DIAL_NOMATCH since there will be no template match in
+     * an empty dialplan.
+     */
+    if (ptempl == NULL) {
+        if (strchr(pattern, '#') && (poundDialingEnabled())) {
+            return DIAL_IMMEDIATELY;
+        } else {
+            return DIAL_NOMATCH;
+        }
+    }
+
+    /*
+     * Iterate through all the templates. Skip the this template if it's not
+     * for this line or all lines.
+     */
+    while (ptempl != NULL) {
+        if (MatchLineNumber(ptempl->line, line)) {
+            char *pinput = (char *) pattern;
+            char *pmatch = ptempl->pattern;
+            int thismatchlen = 0;
+            DialMatchAction thismatch = DIAL_FULLMATCH;
+            char *subs[MAX_SUBTITUTIONS];
+            int subslen[MAX_SUBTITUTIONS];
+            int subscount = -1;
+            boolean dialnow = FALSE;
+
+            while (*pinput) {
+                int idx;
+
+                /* Since the code below combines multiple ,
+                 * in a row into "one" , only increment
+                 * comma counter once instead of once
+                 * for each comma combined.
+                 */
+                if (pmatch[0] == ',') {
+                    comma_counter++;
+                }
+
+                /*
+                 * Skip over any dial tone characters
+                 */
+                while (pmatch[0] == ',') {
+                    pmatch++;
+                }
+                /*
+                 * If this is a pattern character, we need to capture the digits for the
+                 * substitution strings
+                 */
+                if (((pmatch[0] == '.') && isDialedDigit(pinput[0])) ||
+                     (pmatch[0] == '*')) {
+                    /*
+                     * Get the next index in the substitution array (if any)
+                     * Note that if they have more pattern sections in the pattern string
+                     * the last one will get the counts for the characters.  So for example
+                     * with the MAX_SUBSTITUTIONS of 5 and a pattern of
+                     *  1.2.3.4.5.6.7.
+                     * and an input string of
+                     *  1a2b3c4d5e6f7g
+                     * the arrays of substitions would come out as follows:
+                     *   %0 = 1a2b3c4d5e6f7g  (as expected)
+                     *   %1 = a               (as expected)
+                     *   %2 = b               (as expected)
+                     *   %3 = c               (as expected)
+                     *   %4 = d               (as expected)
+                     *   %5 = e6f    NOT what they really wanted, but predictable from the algorithm
+                     */
+                    if (subscount < (MAX_SUBTITUTIONS - 1)) {
+                        subscount++;
+                        subs[subscount] = pinput;
+                        subslen[subscount] = 1;
+                    }
+                    if (pmatch[0] == '.') {
+                        thismatch = DIAL_FULLPATTERN;
+                        /*
+                         * . in the pattern will match anything but doesn't contribute
+                         * to our matching length for finding the best template
+                         */
+                        while (isdigit(pinput[1]) && (pmatch[1] == '.')) {
+                            pinput++;
+                            pmatch++;
+                            subslen[subscount]++;
+                        }
+                    } else {
+                        thismatch = DIAL_WILDPATTERN;
+                        /*
+                         * '*' is a wild card to match 1 or more characters
+                         * Do a hungry first match
+                         *
+                         * Note: If match is currently pointing to an escape character,
+                         *       we need to go past it to match the actual character.
+                         */
+                        if (pmatch[1] == DIAL_ESCAPE) {
+                            idx = 2;
+                        } else {
+                            idx = 1;
+                        }
+
+                        /*
+                         * The '*' will not match the '#' character since its' default use
+                         * causes the phone to "dial immediately"
+                         */
+                        if ((pinput[0] == '#') && (poundDialingEnabled())) {
+                            dialnow = TRUE;
+                        } else {
+                            while ((pinput[1] != '\0') &&
+                                   (pinput[1] != pmatch[idx])) {
+                                /*
+                                 * If '#' is found and pound dialing is enabled, break out of the loop.
+                                 */
+                                if ((pinput[1] == '#') &&
+                                    (poundDialingEnabled())) {
+                                    break;
+                                }
+                                pinput++;
+                                subslen[subscount]++;
+                            }
+                        }
+                    }
+                    /*
+                     * Any other character must match exactly
+                     */
+                } else {
+                    /*
+                     * Look for the Escape character '\' and remove it
+                     * Right now, the only character that needs to be escaped is '*'
+                     * Note that we treat \ at the end of a line as a non-special
+                     * character
+                     */
+                    if ((pmatch[0] == DIAL_ESCAPE) && (pmatch[1] != '\0')) {
+                        pmatch++;
+                    }
+
+                    if (pmatch[0] != pinput[0]) {
+                        /*
+                         * We found a '#' that doesn't match the current match template
+                         * This means that the '#" should be interpreted as the dial
+                         * termination character.
+                         */
+                        if ((pinput[0] == '#') && (poundDialingEnabled())) {
+                            dialnow = TRUE;
+                            break;
+                        }
+                        /*
+                         * No match, so abandon with no pattern to select
+                         */
+                        thismatchlen = -1;
+                        thismatch = DIAL_NOMATCH;
+                        break;
+
+                    } else {
+                        /*
+                         * We matched one character, count it for the overall template match
+                         */
+                        thismatchlen++;
+                    }
+                }
+                pmatch++;
+                pinput++;
+            }
+
+            /*
+             * *pinput = NULL means we matched everything that
+             * was dialed in this template
+             * Partial never matches * rules
+             * Since 97. should have precendence over 9..
+             * also check matchlen.
+             * Since fullmatches (exact digits) have precendence
+             * over fullpattern (.) check matchtype
+             */
+            if ((*pinput == NUL) || (dialnow)) {
+                if ((thismatchlen > partialmatchlen) ||
+                    ((thismatchlen == partialmatchlen) &&
+                     (thismatch > partialmatch_type))) {
+                    partialmatch_type = thismatch;
+                    partialmatchlen = thismatchlen;
+                    pbestmatch = ptempl;
+                    partialmatch = TRUE;
+                    bestmatch_dialnow = dialnow;
+                    best_comma_count = comma_counter;
+                    result = DIAL_NOMATCH;
+                }
+            }
+
+            /*
+             * If we exhausted the match string, then the template is a perfect match
+             * However, we don't want to take this as the best template unless it matched
+             * more digits than any other pattern.  For example if we have a pattern of
+             *   9011*
+             *   9.11
+             *  We would want 9011 to match against the first one even though it is not complete
+             *
+             * We also have to be careful that a pattern such as
+             *   *
+             * does not beat something like
+             *   9.......
+             * when you have 94694210
+             */
+            if (pmatch[0] == '\0') {
+                /*
+                 * If this pattern is better, we want to adopt it
+                 */
+                if ((thismatchlen > matchlen) ||
+                    ((thismatchlen == matchlen) && (thismatch > result)) ||
+                    ((thismatch == DIAL_WILDPATTERN) &&
+                     ((result == DIAL_NOMATCH) && (partialmatch == FALSE)))) {
+                    /*
+                     * this is a better match than what we found before
+                     */
+                    pbestmatch = ptempl;
+                    bestmatch_dialnow = dialnow;
+                    matchlen = thismatchlen;
+                    result = thismatch;
+                    /*
+                     * Generate a rewrite string
+                     *
+                     *  <TEMPLATE MATCH="31."     Timeout="0" User="Phone" Rewrite="11111111%s"/>
+                     *      310 -> 111111110
+                     *  <TEMPLATE MATCH="32.."    Timeout="0" User="Phone" Rewrite="122222232.."/>
+                     *      3255 -> 12222223255
+                     *  <TEMPLATE MATCH="33..."   Timeout="0" User="Phone" Rewrite="13333333%1"/>
+                     *  <TEMPLATE MATCH="34..1.." Timeout="0" User="Phone" Rewrite="14444%155%2"/>
+                     *  <TEMPLATE MATCH="34..2.." Timeout="0" User="Phone" Rewrite="1444%s"/>
+                     *  <TEMPLATE MATCH="34..3.." Timeout="0" User="Phone" Rewrite="11..11.."/>
+                     */
+                    if (rewrite != NULL) {
+                        int dotindex = -1;
+                        int dotsleft = 0;
+                        char *output = rewrite;
+                        int room = rewritelen;
+                        char *prewrite = pbestmatch->rewrite;
+
+                        if ((prewrite == NULL) || (prewrite[0] == '\0')) {
+                            /*
+                             * Null or empty patterns produce the input as the result
+                             */
+                            addbytes(&output, &room, pattern, -1);
+                        } else {
+                            while (prewrite[0] != '\0') {
+                                if (prewrite[0] == '.') {
+                                    /*
+                                     * For a dot, we copy over the single previously matched character
+                                     */
+                                    while ((dotsleft == 0) &&
+                                           (dotindex < subscount)) {
+                                        dotindex++;
+                                        dotsleft = subslen[dotindex];
+                                    }
+                                    if (dotsleft > 0) {
+                                        addbytes(&output, &room,
+                                                 subs[dotindex] +
+                                                 subslen[dotindex] - dotsleft,
+                                                 1);
+                                        dotsleft--;
+                                    }
+                                } else if (prewrite[0] == '%') {
+                                    int idx = prewrite[1] - '1';
+
+                                    prewrite++; // Consume the %
+                                    if ((prewrite[0] == 's') ||
+                                        (prewrite[0] == '0')) {
+                                        /*
+                                         * %0 or %s means copy the entire input string
+                                         */
+                                        addbytes(&output, &room, pattern, -1);
+                                    } else if ((idx >= 0) &&
+                                               (idx <= subscount)) {
+                                        /*
+                                         * %n where n is withing the range of substitution patterns copies over
+                                         * all of the characters that matched that group
+                                         */
+                                        addbytes(&output, &room, subs[idx],
+                                                 subslen[idx]);
+                                    } else if (prewrite[0]) {
+                                        /*
+                                         * %anything copies over anything.  This is how you would get a
+                                         * % in the rewrite string
+                                         */
+                                        addbytes(&output, &room, prewrite, 1);
+                                    }
+                                } else {
+                                    /*
+                                     * Just a normal character, just copy it over
+                                     */
+                                    addbytes(&output, &room, prewrite, 1);
+                                }
+                                /*
+                                 * Consume the character we used (if any)
+                                 */
+                                if (prewrite[0]) {
+                                    prewrite++;
+                                }
+                            }
+                        }
+                        /*
+                         * Put on the usermode (if specified)
+                         */
+                        switch (pbestmatch->userMode) {
+                        case UserPhone:
+                            if (*(output - 1) == '>') {
+                                --room;
+                                --output;
+                                addbytes(&output, &room, ";user=phone>", -1);
+                            } else {
+                                addbytes(&output, &room, ";user=phone", -1);
+                            }
+                            break;
+                        case UserIP:
+                            if (*(output - 1) == '>') {
+                                --room;
+                                --output;
+                                addbytes(&output, &room, ";user=ip>", -1);
+                            } else {
+                                addbytes(&output, &room, ";user=ip", -1);
+                            }
+                            break;
+                        default:
+                            break;
+                        }
+                    }
+                }
+            } else if (pmatch[0] == ',') {
+                givedialtone = 1;
+            }
+
+            /*
+             * Even if this pattern was not taken as a full match, remember the longest length
+             */
+            if (thismatchlen > matchlen) {
+                matchlen = thismatchlen;
+            }
+        }
+        /*
+         * Try the next template
+         */
+        ptempl = ptempl->next;
+        comma_counter = 0;
+    }
+    /*
+     * Did we get any templates at all?
+     */
+    switch (result) {
+    case DIAL_FULLPATTERN:
+    case DIAL_FULLMATCH:
+        givedialtone = 0;
+        /* FALLTHROUGH */
+    case DIAL_WILDPATTERN:
+        if (timeout != NULL) {
+            *timeout = pbestmatch->timeout;
+        }
+        if (pRouteMode != NULL) {
+            *pRouteMode = pbestmatch->routeMode;
+        }
+        break;
+
+    default:
+        /*
+         * If we have received a partial match, set the timeout
+         * to be the default timeout (i.e. interdigit timeout)
+         * to allow for additional digit collection.
+         */
+        if (partialmatch) {
+            if ((timeout != NULL) && (*timeout == 0)) {
+                *timeout = DIAL_TIMEOUT;
+            }
+        } else if ((*(pattern + strlen(pattern) - 1) == '#') &&
+                   (poundDialingEnabled())) {
+            /*
+             * No template was matched, however if the dialplan
+             * does not have the default '*' rule and the pattern
+             * dialed did not match any other template this code
+             * would be hit. Therefore check for a # to see if
+             * we need to set the dial immediately flag.
+             */
+            result = DIAL_IMMEDIATELY;
+        }
+        break;
+    }
+
+    /*
+     * If the bestmatch template says to dialnow, do it.
+     */
+    if (bestmatch_dialnow) {
+        /*
+         * If there is partial match, and dialled pattern has "#",
+         * then do not Dial immediately. Give user a chance to edit
+         * the mistake if any.
+         */
+        if (!((poundDialingEnabled()) && (strchr(pattern, '#')) &&
+              partialmatch)) {
+            result = DIAL_IMMEDIATELY;
+            if (timeout != NULL) {
+                *timeout = 0;
+            }
+        }
+    }
+
+    if (givedialtone) {
+        /*
+         * Give user dial-tone. The tone given
+         * is based on the rule. Default tone
+         * is Outside-Dial-Tone.
+         */
+        if (pTone != NULL) {
+            *pTone = VCM_DEFAULT_TONE;
+            if (pbestmatch != NULL) {
+                if (best_comma_count < pbestmatch->tones_defined) {
+                    *pTone = pbestmatch->tone[best_comma_count];
+                }
+            }
+        }
+        result = DIAL_GIVETONE;
+    }
+
+    return result;   // Nothing special to match against
+}
+
+
+/*
+ *  Function: InitDialPlan()
+ *
+ *  Parameters: wipe - should we wipe the current dialplan out before we start,
+ *                     or leave it in place for safety's sake
+ *
+ *  Description: Reads the Dial Plan from memory and submits a TFTP
+ *      request for the Dial Plan file.
+ *
+ *  Returns: None
+ */
+void
+InitDialPlan (boolean wipe)
+{
+}
+
+
+
+/*
+ *  Function: FreeDialTemplates()
+ *
+ *  Parameters: None
+ *
+ *  Description: Frees the Dial Templates from memory
+ *
+ *  Returns: None
+ */
+void
+FreeDialTemplates (void)
+{
+    struct DialTemplate *pnext;
+
+    while (basetemplate != NULL) {
+        pnext = basetemplate->next;
+        cpr_free(basetemplate);
+        basetemplate = pnext;
+    }
+}
+
+
+/*
+ *  Function: AddDialTemplate()
+ *
+ *  Parameters: pattern -
+ *              line    -
+ *              timeout -
+ *              userMode -
+ *              rewrite -
+ *              routeMode -
+ *              tone - array of tones to play when , is hit in the rule
+ *              tones_defined - # of tones this rule actually defined
+ *
+ *  Description: Add a dial template to the known list of templates
+ *
+ *  Returns: None
+ */
+static void
+AddDialTemplate (const char *pattern, const line_t line,
+                 int timeout, UserMode userMode,
+                 const char *rewrite, RouteMode routeMode,
+                 vcm_tones_t tone[MAX_TONES], int tones_defined)
+{
+    struct DialTemplate *pnewtemplate;
+    int patternlen = strlen(pattern);
+    int rewritelen = strlen(rewrite);
+    int counter;
+
+    pnewtemplate = (struct DialTemplate *)
+        cpr_malloc(sizeof(struct DialTemplate) + patternlen + rewritelen +
+                   2);
+    if (pnewtemplate != NULL) {
+        pnewtemplate->next = NULL;
+        pnewtemplate->pattern = (char *) (pnewtemplate + 1);
+        sstrncpy(pnewtemplate->pattern, (char *) pattern, patternlen + 1);
+        pnewtemplate->rewrite = pnewtemplate->pattern + patternlen + 1;
+        sstrncpy(pnewtemplate->rewrite, (char *) rewrite, rewritelen + 1);
+        pnewtemplate->line = line;
+        pnewtemplate->timeout = timeout;
+        pnewtemplate->userMode = userMode;
+        pnewtemplate->routeMode = routeMode;
+        pnewtemplate->tones_defined = tones_defined;
+        for (counter = 0; counter < MAX_TONES; counter++) {
+            pnewtemplate->tone[counter] = tone[counter];
+        }
+
+        /*
+         * Now add it to the end of all the templates
+         */
+        if (basetemplate == NULL) {
+            basetemplate = pnewtemplate;
+        } else {
+            struct DialTemplate *base = basetemplate;
+
+            while (base->next != NULL) {
+                base = base->next;
+            }
+            base->next = pnewtemplate;
+        }
+    }
+}
+
+/*
+ *  Function: show_dialplan_cmd
+ *
+ *  Parameters:   standard args
+ *
+ *  Description:  Display the current dialplan (if any)
+ *
+ *  Returns:
+ *
+ */
+int32_t
+show_dialplan_cmd (int32_t argc, const char *argv[])
+{
+    struct DialTemplate *pTemp;
+    char umode[32], rmode[32];
+    char line_str[32];
+    uint32_t idx = 1;
+    int32_t counter = 0;
+
+    debugif_printf("Dialplan is....\n");
+    debugif_printf("Dialplan version: %s\n", g_dp_version_stamp);
+
+    pTemp = basetemplate;
+    if (basetemplate == NULL) {
+        debugif_printf("EMPTY\n");
+        return 0;
+    }
+    while (pTemp != NULL) {
+        switch (pTemp->routeMode) {
+        case RouteEmergency:
+            sstrncpy(rmode, "Emergency", sizeof(rmode));
+            break;
+        case RouteFQDN:
+            sstrncpy(rmode, "FQDN", sizeof(rmode));
+            break;
+        default:
+            sstrncpy(rmode, "Default", sizeof(rmode));
+            break;
+        }
+
+        switch (pTemp->userMode) {
+        case UserPhone:
+            sstrncpy(umode, "Phone", sizeof(umode));
+            break;
+        case UserIP:
+            sstrncpy(umode, "IP", sizeof(umode));
+            break;
+        default:
+            sstrncpy(umode, "Unspecified", sizeof(umode));
+            break;
+        }
+
+        if (pTemp->line == 0) {
+            sprintf(line_str, "All");
+        } else {
+            sprintf(line_str, "%d", pTemp->line);
+        }
+        debugif_printf("%02d. Pattern: %s  Rewrite: %s Line: %s\n"
+                       "    Timeout: %04d   UserMode: %s  RouteMode: %s\n",
+                       idx, pTemp->pattern, pTemp->rewrite, line_str,
+                       pTemp->timeout, umode, rmode);
+        for (counter = 0; counter < pTemp->tones_defined; counter++) {
+            debugif_printf("    Tone %d: %s\n", counter + 1,
+                           tone_names[(int) (pTemp->tone[counter])]);
+        }
+        pTemp = pTemp->next;
+        idx++;
+
+    }
+    return (0);
+}
+
+
+/*
+ *  Function: ParseDialEntry()
+ *
+ *  Parameters: parseptr - pointer to bytes to be parsed
+ *
+ *  Description: Parse the contents of a TEMPLATE tag and add the data to
+ *      the dial template lists.
+ *      The keywords parsed are:
+ *           MATCH="string"
+ *           Line="number"
+ *           Timeout="number"
+ *           User="Phone" | "IP"
+ *           Rewrite="string"
+ *      All other keywords are silently ignored
+ *  The format of the TEMPLATE can be one ofthe following:
+ *  1. <TEMPLATE KEYWORD1="value1" ... KEYWORDN="valueN" />
+ *  2. <TEMPLATE KEYWORD1="value1" ... KEYWORDN="valueN"></TEMPLATE>
+ *
+ *  Returns: Int - The Number of errors encountered
+ */
+static int
+ParseDialEntry (char **parseptr)
+{
+    char dialtemplate[MAX_TEMPLATE_LENGTH];
+    char rewrite[MAX_TEMPLATE_LENGTH];
+    int timeout = DIAL_TIMEOUT; /* Default to DIAL_TIMEOUT if none specified */
+    unsigned char line = 0;     /* Default to 0 if not specified. Zero matches all lines. */
+    int counter = 0;
+    int tone_counter = 0;
+    UserMode usermode = UserUnspec;
+    RouteMode routeMode = RouteDefault;
+    vcm_tones_t tone[MAX_TONES];
+    ParseDialState state = STATE_ANY;
+    long strtol_result;
+    char *strtol_end;
+
+    dialtemplate[0] = '\0';
+    rewrite[0] = '\0';
+    // Set all tones to default. Rule may override.
+    for (counter = 0; counter < MAX_TONES; counter++) {
+        tone[counter] = VCM_DEFAULT_TONE;
+    }
+
+    for (;;) {
+        char buffer[64];
+        XMLToken tok;
+
+        tok = parse_xml_tokens(parseptr, buffer, sizeof(buffer));
+        switch (tok) {
+        case TOK_KEYWORD:
+            if (state != STATE_ANY) {
+                if ((cpr_strcasecmp(buffer, "TEMPLATE") == 0) &&
+                    (state == STATE_END_TAG_STARTED)) {
+                    state = STATE_END_TAG_FOUND;
+                } else {
+                    return 1;
+                }
+            } else if (cpr_strcasecmp(buffer, "MATCH") == 0) {
+                state = STATE_GOT_MATCH;
+            } else if (cpr_strcasecmp(buffer, "LINE") == 0) {
+                state = STATE_GOT_LINE;
+            } else if (cpr_strcasecmp(buffer, "TIMEOUT") == 0) {
+                state = STATE_GOT_TIMEOUT;
+            } else if (cpr_strcasecmp(buffer, "USER") == 0) {
+                state = STATE_GOT_USER;
+            } else if (cpr_strcasecmp(buffer, "REWRITE") == 0) {
+                state = STATE_GOT_REWRITE;
+            } else if (cpr_strcasecmp(buffer, "ROUTE") == 0) {
+                state = STATE_GOT_ROUTE;
+            } else if (cpr_strcasecmp(buffer, "TONE") == 0) {
+                state = STATE_GOT_TONE;
+            } else {
+                return 1;
+            }
+            break;
+
+        case TOK_EQ:
+            switch (state) {
+            case STATE_GOT_MATCH:
+                state = STATE_GOT_MATCH_EQ;
+                break;
+            case STATE_GOT_LINE:
+                state = STATE_GOT_LINE_EQ;
+                break;
+            case STATE_GOT_TIMEOUT:
+                state = STATE_GOT_TIMEOUT_EQ;
+                break;
+            case STATE_GOT_USER:
+                state = STATE_GOT_USER_EQ;
+                break;
+            case STATE_GOT_REWRITE:
+                state = STATE_GOT_REWRITE_EQ;
+                break;
+            case STATE_GOT_ROUTE:
+                state = STATE_GOT_ROUTE_EQ;
+                break;
+            case STATE_GOT_TONE:
+                state = STATE_GOT_TONE_EQ;
+                break;
+
+            default:
+                return 1;
+            }
+            break;
+
+        case TOK_STR:
+            switch (state) {
+            case STATE_GOT_MATCH_EQ:
+                sstrncpy(dialtemplate, buffer, sizeof(dialtemplate));
+                break;
+
+            case STATE_GOT_LINE_EQ:
+                errno = 0;
+                strtol_result = strtol(buffer, &strtol_end, 10);
+
+                if (errno || buffer == strtol_end || strtol_result < 0 || strtol_result > UCHAR_MAX) {
+                    return 1;
+                }
+
+                line = (unsigned char) strtol_result;
+                break;
+
+            case STATE_GOT_TIMEOUT_EQ:
+                errno = 0;
+                strtol_result = strtol(buffer, &strtol_end, 10);
+
+                if (errno || buffer == strtol_end || strtol_result < 0 || strtol_result > INT_MAX) {
+                    return 1;
+                }
+
+                timeout = (int) strtol_result;
+
+                break;
+
+            case STATE_GOT_USER_EQ:
+                if (cpr_strcasecmp(buffer, "PHONE") == 0) {
+                    usermode = UserPhone;
+                } else if (cpr_strcasecmp(buffer, "IP") == 0) {
+                    usermode = UserIP;
+                } else {
+                    return 1;
+                }
+                break;
+
+            case STATE_GOT_ROUTE_EQ:
+                if (cpr_strcasecmp(buffer, "DEFAULT") == 0) {
+                    routeMode = RouteDefault;
+                } else if (cpr_strcasecmp(buffer, "EMERGENCY") == 0) {
+                    routeMode = RouteEmergency;
+                } else if (cpr_strcasecmp(buffer, "FQDN") == 0) {
+                    routeMode = RouteFQDN;
+                } else {
+                    return 1;
+                }
+                break;
+
+            case STATE_GOT_TONE_EQ:
+                if (tone_counter < MAX_TONES) {
+                    /* Tone = "" check */
+                    if (*buffer == '\000') {
+                        tone[tone_counter] = VCM_DEFAULT_TONE;
+                    } else {
+                        for (counter = 0; counter < VCM_MAX_DIALTONE; counter++) {
+                            if (cpr_strcasecmp(buffer, tone_names[counter]) ==
+                                0) {
+                                tone[tone_counter] = (vcm_tones_t) counter;
+                                break;
+                            }
+                        }
+                        // Tone string not matched
+                        if (counter == VCM_MAX_DIALTONE) {
+                            return 1;
+                        }
+                    }
+                    tone_counter++;
+                    // Too many tones defined in the rule
+                } else {
+                    return 1;
+                }
+                break;
+
+            case STATE_GOT_REWRITE_EQ:
+                sstrncpy(rewrite, buffer, sizeof(rewrite));
+                break;
+
+            default:
+                return 1;
+            }
+            state = STATE_ANY;
+            break;
+
+        case TOK_EMPTYBRACKET: /* "/>" */
+            AddDialTemplate(dialtemplate, line, timeout, usermode, rewrite,
+                            routeMode, tone, tone_counter);
+            if (state == STATE_ANY) {
+                return 0;
+            }
+            /*
+             * There was a parsing error, so just ignore it
+             */
+            return 1;
+
+        case TOK_RBRACKET:     /* ">" */
+            if (state == STATE_ANY) {
+                state = STATE_START_TAG_COMPLETED;
+            } else if (state == STATE_END_TAG_FOUND) {
+                AddDialTemplate(dialtemplate, line, timeout, usermode, rewrite,
+                                routeMode, tone, tone_counter);
+                return 0;
+            } else {
+                return 1;
+            }
+            break;
+
+        case TOK_ENDLBRACKET:  /* "</" */
+            if (state == STATE_START_TAG_COMPLETED) {
+                state = STATE_END_TAG_STARTED;
+            } else {
+                return 1;
+            }
+            break;
+
+
+        default:
+            /*
+             * Problem parsing. Let them know
+             */
+            return 1;
+        }
+    }
+}
+
+/*
+ *  Function: ParseDialVersion()
+ *
+ *  Parameters: parseptr - pointer to bytes to be parsed
+ *
+ *  Description: Parse the contents of a versionStamp tag and store it
+ *  The format of the versionStamp is as follows:
+ *  <versionStamp>value up to 64 chars</versionStamp>
+ *
+ *  Returns: 0 - if the version stamp is successfully parsed.
+ *           1 - if the version stamp  parsing fails.
+ */
+static int
+ParseDialVersion (char **parseptr)
+{
+    ParseDialState state = STATE_ANY;
+    char version_stamp[MAX_DP_VERSION_STAMP_LEN] = { 0 };
+    int len;
+
+    for (;;) {
+        char buffer[MAX_DP_VERSION_STAMP_LEN];
+        XMLToken tok;
+
+        tok = parse_xml_tokens(parseptr, buffer, sizeof(buffer));
+        switch (tok) {
+        case TOK_KEYWORD:
+            switch (state) {
+            case STATE_START_TAG_COMPLETED:
+                /*
+                 * the keyword is version stamp value here. copy it temporarily.
+                 */
+                memcpy(version_stamp, buffer, MAX_DP_VERSION_STAMP_LEN);
+                break;
+            case STATE_END_TAG_STARTED:
+                if (cpr_strcasecmp(buffer, "versionStamp") != 0) {
+                    return 1;
+                }
+                state = STATE_END_TAG_FOUND;
+                break;
+
+            default:
+                break;
+            }
+            break;
+
+        case TOK_RBRACKET:     /* ">" */
+            if (state == STATE_ANY) {
+                state = STATE_START_TAG_COMPLETED;
+            } else if (state == STATE_END_TAG_FOUND) {
+                /* strip of the warping curly braces if they exist */
+                len = strlen(version_stamp);
+                if (len <= 2)
+                {
+                    CCAPP_ERROR("ParseDialVersion(): Version length [%d] is way too small", len);
+                    return (1);
+                }
+
+                memset(g_dp_version_stamp, 0, MAX_DP_VERSION_STAMP_LEN);
+                if ((version_stamp[0] == '{')
+                    && (version_stamp[len - 1] == '}')) {
+                    memcpy(g_dp_version_stamp, (version_stamp + 1), (len - 2));
+                } else {
+                    memcpy(g_dp_version_stamp, version_stamp, len);
+                }
+                return 0;
+            } else {
+                return 1;
+            }
+            break;
+
+        case TOK_ENDLBRACKET:  /* "</" */
+            if (state == STATE_START_TAG_COMPLETED) {
+                state = STATE_END_TAG_STARTED;
+            } else {
+                return 1;
+            }
+            break;
+
+        default:
+            /*
+             * Problem parsing. Let them know
+             */
+            return 1;
+        }
+    }
+}
+
+
+/*
+ *  Function: ParseDialTemplate()
+ *
+ *  Parameters: parseptr - pointer to bytes to be parsed
+ *
+ *  Description: Parse the contents of a dial template XML file
+ *      All Start and end tags are ignored
+ *      The empty element <TEMPLATE is parsed by ParseDialEntry
+ *
+ *  Returns: false if parse fails else true
+ */
+boolean
+ParseDialTemplate (char *parseptr)
+{
+    char buffer[MAX_TEMPLATE_LENGTH];
+    XMLToken tok;
+    int LookKey;
+    int LookEndKey;
+    int errors = 0;
+    int insideDialPlan = 0;
+
+    LookKey = 0;
+    LookEndKey = 0;
+    FreeDialTemplates();
+    /*
+     * reset the version stamp so that dialplans that do not contain versionStamp
+     * tag will not keep the previous versionstamp.
+     */
+    g_dp_version_stamp[0] = 0;
+
+
+    if (parseptr == NULL) {
+        debugif_printf("ParseDialTempate(): parseptr=NULL Returning.\n");
+        return (FALSE);
+    }
+
+    while ((tok =
+            parse_xml_tokens(&parseptr, buffer, sizeof(buffer))) != TOK_EOF) {
+        if (LookEndKey) {
+            if (tok == TOK_RBRACKET) {
+                LookEndKey = 0;
+            } else if ((tok == TOK_KEYWORD)
+                       && !cpr_strcasecmp(buffer, "DIALTEMPLATE")) {
+                insideDialPlan = 0;
+            }
+        } else if (tok == TOK_LBRACKET) {
+            LookKey = 1;
+        } else if ((LookKey != 0) && (tok == TOK_KEYWORD)
+                   && !cpr_strcasecmp(buffer, "DIALTEMPLATE")) {
+            insideDialPlan = 1;
+        } else if ((LookKey != 0) && (tok == TOK_KEYWORD)
+                   && !strcmp(buffer, "TEMPLATE")) {
+            if (insideDialPlan) {
+                errors += ParseDialEntry(&parseptr);
+            } else {
+                errors++;
+            }
+        } else if ((LookKey != 0) && (tok == TOK_KEYWORD)
+                   && !cpr_strcasecmp(buffer, "versionStamp")) {
+            if (insideDialPlan) {
+                /* <versionStamp> tag found. parse it */
+                errors += ParseDialVersion(&parseptr);
+            } else {
+                errors++;
+            }
+        } else if (tok == TOK_ENDLBRACKET) {
+            LookEndKey = 1;
+        } else {
+            LookKey = 0;
+        }
+    }
+    /*
+     * If we had any parse errors, put them into the log
+     */
+    log_clear(LOG_CFG_PARSE_DIAL);
+    if (errors != 0) {
+        log_msg(LOG_CFG_PARSE_DIAL, errors, DialTemplateFile);
+        return (FALSE);
+    }
+
+    return (TRUE);
+}
+
diff --git a/libs/sipcc/core/src-common/dialplanint.c b/libs/sipcc/core/src-common/dialplanint.c
new file mode 100755 (executable)
index 0000000..720fab6
--- /dev/null
@@ -0,0 +1,1349 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_memory.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "cpr_timers.h"
+#include "cpr_ipc.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "phntask.h"
+#include "prot_configmgr.h"
+#include "logger.h"
+#include "logmsg.h"
+#include "upgrade.h"
+#include "dialplan.h"
+#include "singly_link_list.h"
+#include "ccsip_subsmanager.h"
+#include "kpmlmap.h"
+#include "dialplanint.h"
+#include "ccapi.h"
+#include "lsm.h"
+#include "gsm.h"
+#include "fsm.h"
+#include "regmgrapi.h"
+#include "ccsip_register.h"
+
+#define PRIME_LINE_ID 1
+cc_int32_t DpintDebug = 0;
+static dp_data_t g_dp_int;
+
+/* note that we do not allow file with size more than DIALPLAN_MAX_SIZE (8k) */
+uint8_t dpLoadArea[DIALPLAN_MAX_SIZE - 1];
+
+
+#define KPML_DEFAULT_DIALPLAN "<DIALTEMPLATE><TEMPLATE MATCH=\".\"     Timeout=\"0\" User=\"Phone\"/></DIALTEMPLATE>"
+
+#define DIAL_KEY 0x82
+
+static void dp_check_dialplan(line_t line, callid_t call_id,
+                              unsigned char digit);
+static void dp_restart_dial_timer (line_t line, callid_t call_id, int timeout);
+
+extern cprBuffer_t cc_get_msg_buf(int min_size);
+extern cc_int32_t show_dialplan_cmd(cc_int32_t argc, const char *argv[]);
+
+
+static void
+dp_int_message (line_t line, callid_t call_id, char digit,
+                char *digit_str, boolean collect_more, void *tmr_data,
+                int msg_id, char *g_call_id, monitor_mode_t monitor_mode)
+{
+    char fname[] = "dp_int_message";
+    dp_int_t *pmsg;
+
+    pmsg = (dp_int_t *) cc_get_msg_buf(sizeof(dp_int_t));
+
+    if (!pmsg) {
+        err_msg(get_debug_string(CC_NO_MSG_BUFFER), fname);
+        return;
+    }
+
+    pmsg->line = line;
+    pmsg->call_id = call_id;
+    pmsg->digit = digit;
+    if (digit_str) {
+        sstrncpy(pmsg->digit_str, digit_str, MAX_DIALSTRING);
+    }
+    pmsg->collect_more = collect_more;
+    pmsg->tmr_ptr = tmr_data;
+    if (digit_str) {
+        sstrncpy(pmsg->global_call_id, g_call_id, CC_GCID_LEN);
+    }
+
+    pmsg->monitor_mode = monitor_mode;
+    if (gsm_send_msg(msg_id, (cprBuffer_t) pmsg, sizeof(dp_int_t)) !=
+        CPR_SUCCESS) {
+        err_msg(get_debug_string(CC_SEND_FAILURE), fname);
+    }
+}
+
+void
+dp_int_init_dialing_data (line_t line, callid_t call_id)
+{
+    dp_int_message(line, call_id, 0, NULL, FALSE, NULL, DP_MSG_INIT_DIALING,
+                   NULL, CC_MONITOR_NONE);
+}
+
+void
+dp_dial_timeout (void *data)
+{
+    DPINT_DEBUG(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(DIALPLAN, "dp_dial_timeout"));
+    dp_int_message(0, 0, 0, NULL, FALSE, &data, DP_MSG_DIGIT_TIMER, NULL, CC_MONITOR_NONE);
+}
+
+void
+dp_int_update_key_string (line_t line, callid_t call_id, char *digits)
+{
+    dp_int_message(line, call_id, 0, digits, FALSE, NULL, DP_MSG_DIGIT_STR,
+                   NULL, CC_MONITOR_NONE);
+}
+
+void
+dp_int_store_digit_string (line_t line, callid_t call_id, char* digit_str)
+{
+    dp_int_message(line, call_id, 0, digit_str, FALSE, NULL, DP_MSG_STORE_DIGIT,
+                   NULL, CC_MONITOR_NONE);
+}
+
+void
+dp_int_update_keypress (line_t line, callid_t call_id, unsigned char digit)
+{
+    dp_int_message(line, call_id, digit, NULL, FALSE,
+                   NULL, DP_MSG_DIGIT, NULL, CC_MONITOR_NONE);
+}
+
+void
+dp_int_dial_immediate (line_t line, callid_t call_id, boolean collect_more,
+                       char *digit_str, char *g_call_id,
+                       monitor_mode_t monitor_mode)
+{
+    dp_int_message(line, call_id, 0, digit_str, collect_more, NULL,
+                   DP_MSG_DIAL_IMMEDIATE, g_call_id, monitor_mode);
+}
+
+void
+dp_int_do_redial (line_t line, callid_t call_id)
+{
+    dp_int_message(line, call_id, 0, NULL, FALSE,
+                   NULL, DP_MSG_REDIAL, NULL, CC_MONITOR_NONE);
+}
+
+void
+dp_int_onhook (line_t line, callid_t call_id)
+{
+    dp_int_message(line, call_id, 0, NULL, FALSE,
+                   NULL, DP_MSG_ONHOOK, NULL, CC_MONITOR_NONE);
+}
+
+void
+dp_int_offhook (line_t line, callid_t call_id)
+{
+    dp_int_message(line, call_id, 0, NULL, FALSE,
+                   NULL, DP_MSG_OFFHOOK, NULL, CC_MONITOR_NONE);
+}
+
+void
+dp_int_update (line_t line, callid_t call_id, string_t called_num)
+{
+    dp_int_message(line, call_id, 0, (char *) called_num, FALSE, NULL,
+                   DP_MSG_UPDATE, NULL, CC_MONITOR_NONE);
+}
+
+void
+dp_int_cancel_offhook_timer (line_t line, callid_t call_id)
+{
+    dp_int_message(line, call_id, 0, NULL, FALSE,
+                   NULL, DP_MSG_CANCEL_OFFHOOK_TIMER, NULL, CC_MONITOR_NONE);
+}
+
+static void
+dp_store_digit_string (line_t line, callid_t call_id, char *digit_str)
+{
+    const char fname[] = "dp_store_digit_string";
+
+    if (g_dp_int.line != line && g_dp_int.call_id != call_id) {
+        return;
+    }
+
+    sstrncpy(g_dp_int.gDialed, digit_str, MAX_DIALSTRING);
+
+    DPINT_DEBUG(DEB_F_PREFIX"stored digits = %s", DEB_F_PREFIX_ARGS(DIALPLAN, fname), &g_dp_int.gDialed[0]);
+}
+
+
+void
+dp_store_digits (line_t line, callid_t call_id, unsigned char digit)
+{
+    const char fname[] = "dp_store_digits";
+    short len;
+
+    if (g_dp_int.line != line && g_dp_int.call_id != call_id) {
+        return;
+    }
+
+    if (digit == BKSPACE_KEY) {
+        return;
+    }
+
+    g_dp_int.line = line;
+    g_dp_int.call_id = call_id;
+
+    len = (short) strlen(g_dp_int.gDialed);
+    if (len >= MAX_DIALSTRING-1)
+    {   // safety check to prevent going out of bounds
+        CCAPP_ERROR(DEB_F_PREFIX"Unexpected dialstring [%s] (length [%d] > max [%d]) received", DEB_F_PREFIX_ARGS(DIALPLAN, fname), g_dp_int.gDialed,
+            len, MAX_DIALSTRING);
+        return;
+    }
+
+    g_dp_int.gDialed[len] = digit;
+    g_dp_int.gDialed[len + 1] = 0;
+
+    DPINT_DEBUG(DEB_F_PREFIX"digit = %c, dig_str = %s", DEB_F_PREFIX_ARGS(DIALPLAN, fname), digit,
+                &g_dp_int.gDialed[0]);
+}
+
+/*
+ *  Function: dp_check_plar_warmline()
+ *
+ *  Parameters: line - line number
+ *                                call_id - call indentification
+ *
+ *  Description: Check if the line is warmline or plar. Since digit is
+ *        detected, cancel the warmline timer.
+ *
+ *  Returns: boolean
+ */
+static boolean
+dp_check_plar_warmline (line_t line, callid_t call_id)
+{
+    /* Check the timeout (timeout==0 for plar) to make sure it is not warm line.
+     * If it is warm line then cancel the timer to stop sending a INV
+     */
+    if (g_dp_int.empty_rewrite[0] && (g_dp_int.offhook_timeout == 0)) {
+
+        //plar
+        return (TRUE);
+
+    } else if (g_dp_int.empty_rewrite[0] && g_dp_int.offhook_timeout) {
+
+        //warm line
+        (void) cprCancelTimer(g_dp_int.dial_timer);
+        memset(g_dp_int.empty_rewrite, 0, sizeof(g_dp_int.empty_rewrite));
+    }
+    return (FALSE);
+}
+
+
+/*
+ *   Function: dp_delete_last_digit
+ *
+ *   Parameters:
+ *          line_id : line identifier
+ *          call_id : call identifier
+ *   Description:
+ *        remove last digit from dial buffer. tell UI to remove it from input box
+ *
+ */
+void
+dp_delete_last_digit (line_t line_id, callid_t call_id)
+{
+    int len;
+
+    /* remove the last digit from gDialed */
+    len = strlen(g_dp_int.gDialed);
+    if (len) {
+        g_dp_int.gDialed[len - 1] = 0;
+    }
+
+    /* tell ui to remove the digit from input box */
+    ui_delete_last_digit(line_id, call_id);
+}
+
+/*
+ *  Function: dp_update_keypress()
+ *
+ *  Parameters: line - line number
+ *                                call_id - call indentification
+ *                                digit - collected digit
+ *
+ *  Description: Dialplan interface function to pass the collected
+ *                                digits. This routine will source collected digits
+ *                                (including backspace) to dialplan and KPML.
+ *                                Digits are passed during connected state and dialing
+ *                                state.
+ *
+ *
+ *  Returns: none
+ */
+static void
+dp_update_keypress (line_t line, callid_t call_id, unsigned char digit)
+{
+    const char fname[] = "dp_update_keypress";
+    lsm_states_t lsm_state;
+    int skMask[MAX_SOFT_KEYS];
+
+    DEF_DEBUG(DEB_L_C_F_PREFIX"KEY .\n", DEB_L_C_F_PREFIX_ARGS(DP_API, line, call_id, fname) );
+
+    lsm_state = lsm_get_state(call_id);
+
+    if (lsm_state == LSM_S_NONE) {
+        DPINT_DEBUG(DEB_F_PREFIX"call not found\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+        return;
+    }
+
+    /* If the call is in connected state, digits are DTMF key presses
+     * pass this to GSM and KPML module
+     */
+    if (lsm_state == LSM_S_RINGOUT ||
+        lsm_state == LSM_S_CONNECTED || lsm_state == LSM_S_HOLDING) {
+
+        DPINT_DEBUG(DEB_F_PREFIX"digit received in LSM state %s\n",
+                    DEB_F_PREFIX_ARGS(DIALPLAN, fname), lsm_state_name(lsm_state));
+        cc_digit_begin(CC_SRC_GSM, g_dp_int.call_id, g_dp_int.line, digit);
+        if (!kpml_update_dialed_digits(line, call_id, digit)) {
+            kpml_quarantine_digits(line, call_id, digit);
+        }
+        return;
+    }
+
+    if (g_dp_int.line != line) {
+        DPINT_DEBUG(DEB_F_PREFIX"line %d does not match dialplan line %d\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname),
+                    line, g_dp_int.line);
+        return;
+    }
+
+    if (dp_check_plar_warmline(line, call_id)) {
+        DPINT_DEBUG(DEB_F_PREFIX"warm line\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+        return;
+    }
+
+    if (digit == 0) {
+        DPINT_DEBUG(DEB_F_PREFIX"digit is 0\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+        return;
+    }
+
+    ui_control_feature(line, call_id, skMask, 1, FALSE);
+
+
+    /* if not in URL dialing mode pass it through kpml dialing
+     * to quarantine digits
+     */
+    if (g_dp_int.url_dialing == FALSE) {
+
+        if (!kpml_update_dialed_digits(line, call_id, digit)) {
+            /*
+             * no valid subscription case:
+             * if user pressed backspace, we can approve the delete without waiting
+             * for any response.
+             */
+            if (digit == BKSPACE_KEY) {
+                dp_delete_last_digit(line, call_id);
+            }
+
+            kpml_quarantine_digits(line, call_id, digit);
+        }
+    } else {
+        if (digit == BKSPACE_KEY) {
+            /* no kpml for URL; go ahead and approve the delete */
+            dp_delete_last_digit(line, call_id);
+        }
+    }
+
+    /* no further processing for back space key */
+    if (digit == BKSPACE_KEY) {
+        return;
+    }
+
+    /* Make sure that call is in dialing state
+     * if not then do not check the dial plan
+     */
+    switch (lsm_state) {
+    case LSM_S_OFFHOOK:
+
+        dp_check_dialplan(line, call_id, digit);
+        break;
+
+    default:
+        break;
+    }
+
+}
+
+/*
+ *  Function: dp_get_dialing_call_id()
+ *
+ *  Parameters: void
+ *
+ *  Description: retun call_id of dialing call.
+ *
+ *  Returns: none
+ */
+static callid_t
+dp_get_dialing_call_id (void)
+{
+    return (g_dp_int.call_id);
+}
+
+/*
+ *  Function: dp_update_key_string()
+ *
+ *  Parameters: line - line number
+ *                                call_id - call indentification
+ *                                digitstr - Pointer to collected digits
+ *
+ *  Description: Dialplan interface function to pass the collected
+ *                                digits. This routine will source collected digits
+ *                                (including backspace) to dialplan and KPML.
+ *                                Digits are passed during connected state and dialing
+ *                                state.
+ *
+ *  Returns: none
+ */
+static void
+dp_update_key_string (line_t line, callid_t call_id, char *digits)
+{
+    short indx = 0;
+
+    /* Get dialing call id if the call id is null */
+
+    if (call_id == CC_NO_CALL_ID) {
+
+        call_id = dp_get_dialing_call_id();
+    }
+
+    while (digits[indx]) {
+        dp_update_keypress(line, call_id, digits[indx++]);
+    }
+}
+
+/*
+ *  Function: dp_dial_timeout_event()
+ *
+ *  Parameters: timer - timer pointer
+ *
+ *  Description: Handles dial timeout for dialplan
+ *
+ *  Returns: none
+ */
+static void
+dp_dial_timeout_event (void *data)
+{
+    const char fname[] = "dp_dial_timeout_event";
+
+    DPINT_DEBUG(DEB_F_PREFIX"line=%d call_id=%d dialed digits=%s\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname),
+                g_dp_int.line, g_dp_int.call_id, g_dp_int.gDialed);
+
+
+    /* if there is a timeout without dialing digit
+     * then do the onhook
+     */
+    if (g_dp_int.empty_rewrite[0] != NUL) {
+
+        kpml_flush_quarantine_buffer(g_dp_int.line, g_dp_int.call_id);
+
+        cc_dialstring(CC_SRC_GSM,
+                      g_dp_int.call_id, g_dp_int.line, g_dp_int.empty_rewrite);
+    } else if ((g_dp_int.gDialed[0] == NUL) &&
+               (g_dp_int.gDialplanDone == FALSE)) {
+
+        /* Go onhook if no digits are dialed. Dialplandone is checked to see
+         * if the timeout happens during KPML digit collection
+         */
+        cc_onhook(CC_SRC_GSM, g_dp_int.call_id, g_dp_int.line, FALSE);
+    } else {
+        /* KPML subscription will not be rejected in this case.
+         * even though it is timeout which triggered invite
+         * remote party may have more digits to collect through
+         * Kpml
+         */
+        kpml_flush_quarantine_buffer(g_dp_int.line, g_dp_int.call_id);
+        cc_dialstring(CC_SRC_GSM,
+                      g_dp_int.call_id, g_dp_int.line, g_dp_int.gDialed);
+    }
+}
+
+/*
+ *  Function: dp_cancel_offhook_timer
+ *
+ *  Parameters:
+ *
+ *  Description:
+ *     called to cancel offhook timer.
+
+ *  Returns: none
+ */
+void dp_cancel_offhook_timer (void)
+{
+    if ((g_dp_int.gTimerType == DP_OFFHOOK_TIMER) && (g_dp_int.dial_timer)) {
+        (void) cprCancelTimer(g_dp_int.dial_timer);
+    }
+}
+
+/*
+ *  Function: dp_restart_dial_timer()
+ *
+ *  Parameters: line - line number
+ *              call_id - call indentification
+ *              timeout - timeout value for the dial timer
+ *
+ *  Description: Start dial timer
+ *
+ *  Returns: none
+ */
+static void
+dp_restart_dial_timer (line_t line, callid_t call_id, int timeout)
+{
+    const char fname[] = "dp_restart_dial_timer";
+
+    DPINT_DEBUG(DEB_F_PREFIX"line=%d call_id=%d timeout=%u\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line,
+                call_id, timeout);
+
+    g_dp_int.timer_info.index.line = line;
+    g_dp_int.timer_info.index.call_id = call_id;
+
+    if (g_dp_int.dial_timer) {
+        (void) cprCancelTimer(g_dp_int.dial_timer);
+
+        (void) cprStartTimer(g_dp_int.dial_timer, timeout,
+                             &g_dp_int.timer_info);
+
+    }
+}
+
+/*
+ *  Function: dp_check_dialplan()
+ *
+ *  Parameters: line - line number
+ *                                call_id - call indentification
+ *                                cursor - indicates number digits in the dial string
+ *                                strDigs - collected digit string.
+ *
+ *  Description: Check dial plan with collected digits.
+ *
+ *  Returns: None
+ */
+static void
+dp_check_dialplan (line_t line, callid_t call_id, unsigned char digit)
+{
+    const char fname[] = "dp_check_dialplan";
+    int timeout = DIAL_TIMEOUT;
+    DialMatchAction action;
+    vcm_tones_t tone;
+    lsm_states_t lsm_state;
+
+    /* if the dialing is done then don't
+     * do anything
+     */
+    if (g_dp_int.gDialplanDone) {
+        DPINT_DEBUG(DEB_F_PREFIX"Dialplan Match Completed: line=%d call_id=%d digits=%d",
+                    DEB_F_PREFIX_ARGS(DIALPLAN, fname), line, call_id, digit);
+        return;
+    }
+
+    DPINT_DEBUG(DEB_F_PREFIX"line=%d call_id=%d digits=%s", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line,
+                call_id, &g_dp_int.gDialed[0]);
+
+    dp_store_digits(line, call_id, digit);
+
+    /* get current digit in GSM format */
+    if (digit == '*') {
+        digit = 0x0E;
+    } else if (digit == '#') {
+        digit = 0x0F;
+    } else {
+        digit = digit - '0';
+    }
+
+    /* see if we match any dial plans */
+    action =
+        MatchDialTemplate(g_dp_int.gDialed, line, &timeout, NULL, 0, NULL,
+                          &tone);
+
+    switch (action) {
+
+    case DIAL_FULLPATTERN:
+        DPINT_DEBUG(DEB_F_PREFIX"Full pattern match\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+
+        if (timeout <= 0) {
+            cc_dialstring(CC_SRC_GSM, g_dp_int.call_id, g_dp_int.line,
+                          g_dp_int.gDialed);
+            /* flush collected digits. Invite has been sent */
+            kpml_flush_quarantine_buffer(line, call_id);
+
+            (void) cprCancelTimer(g_dp_int.dial_timer);
+
+            g_dp_int.gDialplanDone = TRUE;
+            return;
+        }
+
+        cc_digit_begin(CC_SRC_GSM, call_id, line, digit);
+        break;
+
+    case DIAL_IMMEDIATELY:
+        DPINT_DEBUG(DEB_F_PREFIX"Dial immediately\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+        /*
+         * The user pressed the # key and the phone should dial immediately
+         */
+        (void) cprCancelTimer(g_dp_int.dial_timer);
+        g_dp_int.gDialplanDone = TRUE;
+
+        cc_dialstring(CC_SRC_GSM, g_dp_int.call_id, g_dp_int.line,
+                      g_dp_int.gDialed);
+
+        //kpml_set_subscription_reject(line, call_id);
+
+        kpml_flush_quarantine_buffer(line, call_id);
+
+        return;
+
+    case DIAL_GIVETONE:
+        DPINT_DEBUG(DEB_F_PREFIX"Give tone\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+
+        /* we need new dial tone */
+        lsm_state = lsm_get_state(call_id);
+
+        if (lsm_state == LSM_S_NONE) {
+            DPINT_DEBUG(DEB_F_PREFIX"call not found\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+            return;
+        }
+
+        (void)cc_call_action(call_id, LSM_NO_LINE, CC_ACTION_STOP_TONE, NULL);
+        vcmToneStart(tone, FALSE, CREATE_CALL_HANDLE(line, call_id), 0 /* group_id */,
+                       0/* stream_id */, VCM_PLAY_TONE_TO_EAR);
+        break;
+
+    default:
+        DPINT_DEBUG(DEB_F_PREFIX"No match\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+        cc_digit_begin(CC_SRC_GSM, call_id, line, digit);
+        break;
+    }
+
+    dp_restart_dial_timer(line, call_id, timeout * 1000);
+    g_dp_int.gTimerType = DP_INTERDIGIT_TIMER;
+
+    return;
+}
+
+
+/*
+ *  Function: dp_get_kpml_state()
+ *
+ *  Parameters:
+ *
+ *  Description: Function called to check if the KPML is enabled. If the KPML is
+ *    enabled then function returns true. But under certain conditions like redial
+ *    or speed dial (dial immediately) dialing is completed and phone does not expect
+ *    any KPML subscriptions. So do not collect any more digits in this and UI state
+ *    will be set to proceed and frozen. To facilitate this allo_proceed variable is
+ *    added, if this is set to TRUE then function returns false (as if KPML is disabled).
+ *    As mentioned earlier this is called for redial and dial immediate functions.
+ *    Allow_proceed value is reset in this function, and dp_clear_dialing_data() function
+ *
+ *  Returns: TRUE to indicate, more digits has to be collected
+ *           FALSE to indicate no more digits to collect.
+ */
+boolean
+dp_get_kpml_state (void)
+{
+    if (g_dp_int.allow_proceed) {
+
+        g_dp_int.allow_proceed = FALSE;
+        return (FALSE);
+
+    } else {
+
+        return (kpml_get_state());
+    }
+}
+
+/*
+ *  Function: dp_get_gdialed_digits()
+ *
+ *  Parameters:
+ *
+ *  Description: Return dialed digits to the caller.
+ *
+ *
+ *  Returns: none
+ */
+char *
+dp_get_gdialed_digits (void)
+{
+    const char fname[] = "dp_get_gdialed_digits";
+
+    DPINT_DEBUG(DEB_F_PREFIX"Dialed digits:%s\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), g_dp_int.gDialed);
+
+    /* Digits are copied to Redial buffer after 180 is received
+     * after dp_update (). Afterthat gDialed buffer is cleared. So the dialed
+     * value is moved into gRedialed buffer
+     */
+
+    if (g_dp_int.gDialed[0] != NUL) {
+
+        return (&g_dp_int.gDialed[0]);
+    }
+
+    return (&g_dp_int.gReDialed[0]);
+}
+
+/*
+ *  Function: dp_get_redial_line()
+ *
+ *  Parameters: void
+ *
+ *  Description: returns redial line.
+ *
+ *
+ *  Returns: none
+ */
+line_t
+dp_get_redial_line (void)
+{
+    return (g_dp_int.gRedialLine);
+}
+
+static void
+dp_do_redial (line_t line, callid_t call_id)
+{
+    const char fname[] = "dp_do_redial";
+
+    DPINT_DEBUG(DEB_F_PREFIX"line=%d call_id=%d\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line, call_id);
+
+    if (line == 0) {
+        line = dp_get_redial_line();
+    }
+    //CSCsz63263, use prime line instead if last redialed line is unregistered
+    if(ccsip_is_line_registered(line) == FALSE) {
+         DPINT_DEBUG(DEB_F_PREFIX"line %d unregistered, use line %d instead \n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line,
+                        PRIME_LINE_ID);
+         line = PRIME_LINE_ID;
+         if( ccsip_is_line_registered(line) == FALSE) {
+            DPINT_DEBUG(DEB_F_PREFIX" prime line %d unregistered\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line);
+            return;
+       }
+     }
+    if (g_dp_int.gReDialed[0] == NUL) {
+        DPINT_DEBUG(DEB_F_PREFIX"NO DIAL STRING line=%d call_id=%d\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line,
+                    call_id);
+
+        return;
+    }
+
+    sstrncpy(g_dp_int.gDialed, g_dp_int.gReDialed, MAX_DIALSTRING);
+
+    g_dp_int.line = line;
+
+    g_dp_int.call_id = call_id;
+
+    g_dp_int.allow_proceed = TRUE;
+
+    /*
+     * If we are doing redial, then we won't be entering any more digits
+     */
+    g_dp_int.gDialplanDone = TRUE;
+
+    (void) cprCancelTimer(g_dp_int.dial_timer);
+
+    kpml_set_subscription_reject(line, call_id);
+
+    kpml_flush_quarantine_buffer(line, call_id);
+
+    cc_dialstring(CC_SRC_GSM, call_id, line, &g_dp_int.gReDialed[0]);
+}
+
+/*
+ *  Function: dp_start_dialing()
+ *
+ *  Parameters: line - line number
+ *                                call_id - call indentification
+ *
+ *  Description: Start dialing and initilize dialing data.
+ *
+ *  Returns: none
+ */
+static void
+dp_init_dialing_data (line_t line, callid_t call_id)
+{
+    const char fname[] = "dp_init_dialing_data";
+
+    DPINT_DEBUG(DEB_F_PREFIX"line=%d call_id=%d\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line, call_id);
+
+    g_dp_int.call_id = call_id;
+    g_dp_int.line = line;
+    g_dp_int.gDialplanDone = FALSE;
+    g_dp_int.url_dialing = FALSE;
+    g_dp_int.gTimerType = DP_NONE_TIMER;
+
+    memset(g_dp_int.gDialed, 0, sizeof(g_dp_int.gDialed));
+
+    /* get offhook timeout */
+    g_dp_int.offhook_timeout = DIAL_TIMEOUT;
+
+    config_get_value(CFGID_OFFHOOK_TO_FIRST_DIGIT_TIMER,
+                     &g_dp_int.offhook_timeout,
+                     sizeof(g_dp_int.offhook_timeout));
+
+    /* Flush any collected KPML digits on this line */
+    kpml_flush_quarantine_buffer(line, call_id);
+}
+
+/*
+ *  Function: dp_clear_dialing_data()
+ *
+ *  Parameters: line - line number
+ *                                call_id - call indentification
+ *
+ *  Description: This routine is called to stop and clean up dialing data.
+ *
+ *
+ *  Returns: none
+ */
+static void
+dp_clear_dialing_data (line_t line, callid_t call_id)
+{
+    const char fname[] = "dp_clear_dialing_data";
+
+    DPINT_DEBUG(DEB_F_PREFIX"line=%d call_id=%d\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line, call_id);
+
+    g_dp_int.call_id = 0;
+    g_dp_int.line = 0;
+    g_dp_int.gDialplanDone = FALSE;
+    g_dp_int.allow_proceed = FALSE;
+
+    memset(g_dp_int.gDialed, 0, sizeof(g_dp_int.gDialed));
+    memset(g_dp_int.empty_rewrite, 0, sizeof(g_dp_int.empty_rewrite));
+
+    /* Stop interdigit timer */
+    (void) cprCancelTimer(g_dp_int.dial_timer);
+
+    /* Flush any collected KPML digits on this line */
+    //kpml_flush_quarantine_buffer (line, call_id);
+}
+
+/*
+ *  Function: dp_onhook
+ *
+ *  Parameters: none
+ *
+ *  Description: Function called to indicate onhook.
+ *
+ *  Returns: none.
+ */
+static void
+dp_onhook (line_t line, callid_t call_id)
+{
+    if ((g_dp_int.line == line) && (g_dp_int.call_id == call_id)) {
+        dp_clear_dialing_data(line, call_id);
+    }
+}
+
+
+/*
+ *  Function: dp_check_plar_line
+ *
+ *  Parameters: line
+ *
+ *  Description: Function returns true if the call and line is plar.
+ *
+ *  Returns: TRUE - if plar configured and dialing completed.
+ */
+boolean
+dp_check_for_plar_line (line_t line)
+{
+    static char fname[] = "dp_check_for_plar_line";
+    DialMatchAction action;
+    char empty_rewrite[MAX_DIALSTRING];
+    char empty[1];
+    int timeout = 0;
+
+    empty[0] = '\0';            // This empty (i.e. "") pattern.
+    //Check dialplan to generate empty invite
+    action = MatchDialTemplate(empty, line, (int *) &timeout,
+                               empty_rewrite, sizeof(empty_rewrite),
+                               NULL, NULL);
+
+    if (action == DIAL_FULLMATCH) {
+
+        /* Plar line is only when timeout == 0 or else it is warm line
+         */
+        if (timeout <= 0) {
+
+            DPINT_DEBUG(DEB_F_PREFIX"line=%d PLAR line\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line);
+            return (TRUE);
+
+        }
+    }
+    return (FALSE);
+}
+
+/*
+ *  Function: dp_check_and_handle_plar_dialing
+ *
+ *  Parameters: call_id and line
+ *
+ *  Description: Responsible for checking if the line is plar line and
+ *         if so it will dial plar DN.
+ *
+ *  Returns: TRUE - if plar configured.
+ */
+boolean dp_check_and_handle_plar_dialing (line_t line, callid_t call_id)
+{
+    DialMatchAction action;
+    char empty[1];
+    int timeout = 0;
+
+    timeout = g_dp_int.offhook_timeout;
+
+    empty[0] = '\0';            // This empty (i.e. "") pattern.
+    //Check dialplan to generate empty invite
+    action = MatchDialTemplate(empty, line, (int *) &timeout,
+                               g_dp_int.empty_rewrite,
+                               sizeof(g_dp_int.empty_rewrite), NULL, NULL);
+
+
+    if (timeout != (int) g_dp_int.offhook_timeout) {
+        // Dialplan has timeout value convert that in to msec.
+        g_dp_int.offhook_timeout = timeout * 1000;
+    }
+    if (action == DIAL_FULLMATCH) {
+        /* This is either hotline (i.e. plar) or warmline. Timeout is
+         * what tells us the difference.
+         */
+        if (g_dp_int.offhook_timeout <= 0) {
+
+            g_dp_int.gDialplanDone = TRUE;
+
+            g_dp_int.allow_proceed = FALSE;
+
+            kpml_set_subscription_reject(g_dp_int.line, g_dp_int.call_id);
+
+            kpml_flush_quarantine_buffer(line, call_id);
+
+            cc_dialstring(CC_SRC_GSM, g_dp_int.call_id, g_dp_int.line,
+                          g_dp_int.empty_rewrite);
+
+            // copy rewrite into dialed digits so that it will be logged in the placed calls history.
+            memcpy(g_dp_int.gDialed, g_dp_int.empty_rewrite, MAX_DIALSTRING);
+            return (TRUE);
+
+        }
+    }
+
+    return(FALSE);
+}
+/*
+ *  Function: dp_offhook
+ *
+ *  Parameters: none
+ *
+ *  Description: Function called to indicate offhook.
+ *
+ *  Returns: TRUE - if plar configured and dialing completed.
+ */
+boolean dp_offhook (line_t line, callid_t call_id)
+{
+    const char fname[] = "dp_offhook";
+
+    DPINT_DEBUG(DEB_F_PREFIX"line=%d call_id=%d\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), line, call_id);
+
+    /*
+     * If this line and call_id is same that means
+     * dialing has been initialized already.  In addition, if
+     * gDialplanDone is true, there won't be any further digit entry
+     * so don't start the dial timer.
+     * Example case where remote-cc or speedial initates the call
+     * then caller will initialize the dp_int data structure. That
+     * also initilizes gDialpalnDone to True. So Do not restart the
+     * timer or process anything else in these cases.
+     */
+    if ((g_dp_int.line == line) && (g_dp_int.call_id == call_id)) {
+
+        if (g_dp_int.gDialplanDone == FALSE) {
+            dp_restart_dial_timer(line, call_id, g_dp_int.offhook_timeout);
+            g_dp_int.gTimerType = DP_OFFHOOK_TIMER;
+        }
+
+        return (FALSE);
+    }
+
+    //Start offhook timer
+    dp_init_dialing_data(line, call_id);
+
+    if (dp_check_and_handle_plar_dialing(line, call_id) == TRUE) {
+        return(TRUE);
+    }
+
+    /*
+     * Only start the dial timer if dialing is not yet completed.
+     */
+    if (g_dp_int.gDialplanDone == FALSE) {
+        dp_restart_dial_timer(line, call_id, g_dp_int.offhook_timeout);
+        g_dp_int.gTimerType = DP_OFFHOOK_TIMER;
+    }
+
+    return (FALSE);
+}
+
+/*
+ *  Function: dp_dial_immediate
+ *
+ *  Parameters:
+ *        line_id - line number
+ *        call_id - call indentification
+ *
+ *  Description:
+ *     called when the client wants DP module to stop matching and
+ *     send the dialstring. This used for example when User pressed Dial Softkey.
+ *
+ *  Returns: none
+ */
+static void
+dp_dial_immediate (line_t line, callid_t call_id, boolean collect_more,
+                   char *digit_str, char *global_call_id,
+                   monitor_mode_t monitor_mode)
+{
+    const char fname[] = "dp_dial_immediate";
+
+    if (g_dp_int.line != line || g_dp_int.call_id != call_id) {
+        return;
+    }
+
+    DPINT_DEBUG(DEB_F_PREFIX"line=%d call_id=%d dialed digits=%s\n",
+                DEB_F_PREFIX_ARGS(DIALPLAN, fname), line, call_id, g_dp_int.gDialed);
+
+    /* Do not dial anything else even if UI asks to do so, if the line is PLAR */
+    if (dp_check_and_handle_plar_dialing(line, call_id) == TRUE) {
+        return;
+    }
+
+    if (g_dp_int.dial_timer) {
+        (void) cprCancelTimer(g_dp_int.dial_timer);
+    }
+
+
+    if (g_dp_int.gDialplanDone) {
+
+        // if used with CCM, we should allow user to continue to input more digits
+        // after they've entered partial destination number onhook then press dial
+        if (sip_regmgr_get_cc_mode(line) == REG_MODE_CCM) {
+            return;
+        }
+        // KPML mode and user pressed DIAL softkey. So send 0x82 digit to KPML
+        if (!kpml_update_dialed_digits(line, call_id, (char)DIAL_KEY)) {
+
+            kpml_quarantine_digits(line, call_id, (char)DIAL_KEY);
+        }
+        return;
+    }
+
+    /* CTI applications can do a initcallreq without providing the dialstring
+     * In this case generate just the offhook event to GSM
+     */
+
+    if (digit_str[0] == 0 && global_call_id[0] != 0) {
+
+        cc_offhook_ext(CC_SRC_GSM, call_id, line,
+                       global_call_id, monitor_mode);
+        return;
+    }
+
+    g_dp_int.gDialplanDone = TRUE;
+
+    if (digit_str[0] != 0) {
+        sstrncpy(g_dp_int.gDialed, digit_str, MAX_DIALSTRING);
+    }
+
+    /* This case should not happen, as dial softkey is displayed only after the
+     * 1st digit. dp_dial_immediate function is called for dial softkey invocation.
+     */
+    if (g_dp_int.gDialed[0] == 0) {
+        return;
+
+    } else {
+        g_dp_int.line = line;
+        g_dp_int.call_id = call_id;
+
+        kpml_flush_quarantine_buffer(line, call_id);
+
+        cc_dialstring_ext(CC_SRC_GSM, call_id, line, g_dp_int.gDialed,
+                          global_call_id, monitor_mode);
+    }
+
+    if (collect_more == FALSE) {
+        /*
+         * Since no more digits are collected,
+         * allow proceed event to go through UI.
+         */
+        g_dp_int.allow_proceed = TRUE;
+
+        kpml_set_subscription_reject(line, call_id);
+
+        kpml_flush_quarantine_buffer(line, call_id);
+    }
+
+}
+
+/*
+ *  Function: dp_update
+ *
+ *  Parameters: none
+ *
+ *  Description: Function called to indicate alerting.
+ *
+ *  Returns: none
+ */
+static void
+dp_update (line_t line, callid_t call_id, string_t called_num)
+{
+    uint32_t          line_feature = 0;
+
+    config_get_line_value(CFGID_LINE_FEATURE, &line_feature,
+                          sizeof(line_feature), line);
+
+    /* Do not store PLAR redial string.
+     */
+
+    if (g_dp_int.gDialed[0] &&
+        (strcmp(g_dp_int.gDialed, CISCO_PLAR_STRING) != 0) &&
+        (strncmp(g_dp_int.gDialed, CISCO_BLFPICKUP_STRING, (sizeof(CISCO_BLFPICKUP_STRING) - 1)) != 0)) {
+
+        sstrncpy(g_dp_int.gReDialed, g_dp_int.gDialed, MAX_DIALSTRING);
+        g_dp_int.gRedialLine = line;
+    }
+
+    // fix bug CSCsm56180
+    if ((g_dp_int.line == line) && (g_dp_int.call_id == call_id)) {
+        dp_clear_dialing_data(line, call_id);
+        /* Clear kpml data and collected digits */
+        kpml_flush_quarantine_buffer(line, call_id);
+    }
+}
+
+
+/*
+ *  Function: dp_reset()
+ *
+ *  Parameters: none
+ *
+ *  Description: Called during soft reset
+ *
+ *  Returns: none
+ */
+void
+dp_reset (void)
+{
+    DPINT_DEBUG(DEB_F_PREFIX"Reset dp_int module\n", DEB_F_PREFIX_ARGS(DIALPLAN, "dp_reset"));
+    /* cleanup redial buffer */
+    memset(g_dp_int.gReDialed, 0, sizeof(g_dp_int.gReDialed));
+}
+
+/*
+ *  Function: dp_init()
+ *
+ *  Parameters: gsmMsgQueue - the msg queue for the GSM task. Used
+ *                  to tell CPR where to send the timer expiration
+ *                  msgs for the dialplan timer.
+ *
+ *  Description: Initialize dialplan and KPML
+ *
+ *
+ *  Returns: none
+ */
+void
+dp_init (void *gsmMsgQueue)
+{
+
+    g_dp_int.dial_timer =
+        cprCreateTimer("dial_timeout", GSM_DIAL_TIMEOUT_TIMER, TIMER_EXPIRATION,
+                       (cprMsgQueue_t) gsmMsgQueue);
+
+}
+
+/*
+ *  Function: dp_shutdown()
+ *
+ *  Parameters: none
+ *
+ *  Description: shutdown dialplan and cleanup memory
+ *
+ *
+ *  Returns: none
+ */
+void
+dp_shutdown (void)
+{
+    if (g_dp_int.dial_timer != NULL) {
+        (void)cprDestroyTimer(g_dp_int.dial_timer);
+        g_dp_int.dial_timer = NULL;
+    }
+    FreeDialTemplates();
+}
+
+/*
+ * Function: dp_init_template
+ *
+ * Parameters: maxSize - maximum size of the dialplan
+ *
+ * Description: initialize dialplan template from downloaded file
+ *              This is triggered by Adapter module when it triggers download of DP.
+ *
+ */
+int dp_init_template(const char * dial_plan_string, int length) {
+    static const char fname[] = "dp_init_template";
+    char *TemplateData = (char *) &dpLoadArea;
+
+    DPINT_DEBUG(DEB_F_PREFIX"Reading Dialplan string.  Length=[%d]\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), length);
+
+
+    /* Clear existing dialplan templates before building a new one */
+    FreeDialTemplates();
+    memset(dpLoadArea, 0, sizeof(dpLoadArea));
+
+    //Copy over data
+    memcpy (dpLoadArea, dial_plan_string, length);
+
+    if (length == 0 || ParseDialTemplate(TemplateData) == FALSE) {
+
+        if (kpml_get_state()) {
+
+            /**
+             * There is a error in the parsing so set the default dialplan when KPML is enabled
+             */
+            DPINT_DEBUG(DEB_F_PREFIX"Loading Default Dialplan with KPML enabled",
+                        DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+            memcpy(dpLoadArea, (const char *) KPML_DEFAULT_DIALPLAN,
+                   sizeof(KPML_DEFAULT_DIALPLAN));
+            length = sizeof(KPML_DEFAULT_DIALPLAN);
+            TemplateData[length] = '\00';
+            (void) ParseDialTemplate(TemplateData);
+        } else {
+            /**
+             * There is a error either in parsing so release any parsed dialplan structures
+             */
+            DPINT_DEBUG(DEB_F_PREFIX"Loading Default Dialplan with KPML disabled",
+                        DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+            FreeDialTemplates();
+        }
+
+
+        return (-1);
+    }
+
+    DPINT_DEBUG(DEB_F_PREFIX"Successfully Parsed Dialplan.  \n", DEB_F_PREFIX_ARGS(DIALPLAN, fname));
+    return (0);
+}
+
+char *
+dp_get_msg_string (uint32_t cmd)
+{
+    switch (cmd) {
+
+    case DP_MSG_INIT_DIALING:
+        return ("DP_MSG_INIT_DIALING");
+
+    case DP_MSG_DIGIT_TIMER:
+        return ("DP_MSG_DIGIT_TIMER");
+
+    case DP_MSG_DIGIT_STR:
+        return ("DP_MSG_DIGIT_STR");
+
+    case DP_MSG_STORE_DIGIT:
+        return ("DP_MSG_STORE_DIGIT");
+
+    case DP_MSG_DIGIT:
+        return ("DP_MSG_DIGIT");
+
+    case DP_MSG_DIAL_IMMEDIATE:
+        return ("DP_MSG_DIAL_IMMEDIATE");
+
+    case DP_MSG_REDIAL:
+        return ("DP_MSG_REDIAL");
+
+    case DP_MSG_ONHOOK:
+        return ("DP_MSG_ONHOOK");
+
+    case DP_MSG_OFFHOOK:
+        return ("DP_MSG_OFFHOOK");
+
+    case DP_MSG_CANCEL_OFFHOOK_TIMER:
+        return ("DP_MSG_CANCEL_OFFHOOK_TIMER");
+
+
+    case DP_MSG_UPDATE:
+        return ("DP_MSG_UPDATE");
+
+    default:
+        return ("DP_MSG_UNKNOWN_CMD");
+    }
+}
+
+void
+dp_process_msg (uint32_t cmd, void *msg)
+{
+    static const char fname[] = "dp_process_msg";
+    dp_int_t *dp_int_msg = (dp_int_t *) msg;
+
+    DPINT_DEBUG(DEB_F_PREFIX"cmd= %s\n", DEB_F_PREFIX_ARGS(DIALPLAN, fname), dp_get_msg_string(cmd));
+
+    switch (cmd) {
+
+    case DP_MSG_INIT_DIALING:
+        dp_init_dialing_data(dp_int_msg->line, dp_int_msg->call_id);
+        break;
+
+    case DP_MSG_DIGIT_TIMER:
+        dp_dial_timeout_event(dp_int_msg->tmr_ptr);
+        break;
+
+    case DP_MSG_DIGIT_STR:
+        dp_update_key_string(dp_int_msg->line, dp_int_msg->call_id,
+                             &(dp_int_msg->digit_str[0]));
+        break;
+
+    case DP_MSG_STORE_DIGIT:
+        dp_store_digit_string(dp_int_msg->line, dp_int_msg->call_id,
+                         &(dp_int_msg->digit_str[0]));
+        break;
+
+    case DP_MSG_DIGIT:
+        dp_update_keypress(dp_int_msg->line, dp_int_msg->call_id,
+                           dp_int_msg->digit);
+        break;
+
+    case DP_MSG_DIAL_IMMEDIATE:
+        dp_dial_immediate(dp_int_msg->line, dp_int_msg->call_id,
+                          dp_int_msg->collect_more, &(dp_int_msg->digit_str[0]),
+                          &(dp_int_msg->global_call_id[0]),
+                          dp_int_msg->monitor_mode);
+        break;
+
+    case DP_MSG_REDIAL:
+        dp_do_redial(dp_int_msg->line, dp_int_msg->call_id);
+        break;
+
+    case DP_MSG_ONHOOK:
+        dp_onhook(dp_int_msg->line, dp_int_msg->call_id);
+        break;
+
+    case DP_MSG_OFFHOOK:
+        (void) dp_offhook(dp_int_msg->line, dp_int_msg->call_id);
+        break;
+
+    case DP_MSG_CANCEL_OFFHOOK_TIMER:
+        dp_cancel_offhook_timer();
+        break;
+
+    case DP_MSG_UPDATE:
+        dp_update(dp_int_msg->line, dp_int_msg->call_id,
+                  &(dp_int_msg->digit_str[0]));
+        break;
+
+    default:
+        break;
+    }
+}
diff --git a/libs/sipcc/core/src-common/digcalc.c b/libs/sipcc/core/src-common/digcalc.c
new file mode 100644 (file)
index 0000000..3536e10
--- /dev/null
@@ -0,0 +1,144 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_string.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "md5.h"
+#include "digcalc.h"
+#include "phone_debug.h"
+
+
+void
+CvtHex (IN HASH Bin, OUT HASHHEX Hex)
+{
+    unsigned short i;
+    unsigned char j;
+
+    for (i = 0; i < HASHLEN; i++) {
+        j = (unsigned char) ((Bin[i] >> 4) & 0xf);
+        if (j <= 9)
+            Hex[i * 2] = (j + '0');
+        else
+            Hex[i * 2] = (j + 'a' - 10);
+        j = Bin[i] & 0xf;
+        if (j <= 9)
+            Hex[i * 2 + 1] = (j + '0');
+        else
+            Hex[i * 2 + 1] = (j + 'a' - 10);
+    };
+    Hex[HASHHEXLEN] = '\0';
+}
+
+/* calculate H(A1) as per spec */
+void
+DigestCalcHA1 (IN char *pszAlg,
+               IN char *pszUserName,
+               IN char *pszRealm,
+               IN char *pszPassword,
+               IN char *pszNonce,
+               IN char *pszCNonce,
+               OUT HASHHEX SessionKey)
+{
+    MD5_CTX Md5Ctx;
+    HASH HA1;
+    HASHHEX HA1Hex;
+
+    MD5Init(&Md5Ctx);
+    MD5Update(&Md5Ctx, (unsigned char *) pszUserName, strlen(pszUserName));
+    MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+    MD5Update(&Md5Ctx, (unsigned char *) pszRealm, strlen(pszRealm));
+    MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+    MD5Update(&Md5Ctx, (unsigned char *) pszPassword, strlen(pszPassword));
+    MD5Final((unsigned char *) HA1, &Md5Ctx);
+    if (cpr_strcasecmp(pszAlg, "md5-sess") == 0) {
+        MD5Init(&Md5Ctx);
+        CvtHex(HA1, HA1Hex);
+        MD5Update(&Md5Ctx, (unsigned char *) HA1Hex, HASHHEXLEN);
+        MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *) pszNonce, strlen(pszNonce));
+        MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *) pszCNonce, strlen(pszCNonce));
+        MD5Final((unsigned char *) HA1, &Md5Ctx);
+    };
+
+    CvtHex(HA1, SessionKey);
+}
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void
+DigestCalcResponse (IN HASHHEX HA1,       /* H(A1) */
+                    IN char *pszNonce,    /* nonce from server */
+                    IN char *pszNonceCount,/* 8 hex digits */
+                    IN char *pszCNonce,   /* client nonce */
+                    IN char *pszQop,      /* qop-value: "", "auth", "auth-int"*/
+                    IN char *pszMethod,   /* method from the request */
+                    IN char *pszDigestUri,/* requested URL */
+                    IN HASHHEX HEntity,   /* H(entity body) if qop="auth-int" */
+                    OUT HASHHEX Response) /* request-digest or response-digest */
+{
+    MD5_CTX Md5Ctx;
+    HASH HA2;
+    HASH RespHash;
+    HASHHEX HA2Hex;
+    static const char fname[] = "DigestCalcResponse";
+
+    // calculate H(A2)
+    MD5Init(&Md5Ctx);
+    MD5Update(&Md5Ctx, (unsigned char *) pszMethod, strlen(pszMethod));
+    MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+    MD5Update(&Md5Ctx, (unsigned char *) pszDigestUri, strlen(pszDigestUri));
+
+/*  Commented as causes problems on Windows ToDo
+ * qop is not used but this should be fixed
+    if (cpr_strcasecmp(pszQop, "auth-int") == 0) {
+        MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *) HEntity, HASHHEXLEN);
+    };
+*/
+    MD5Final((unsigned char *) HA2, &Md5Ctx);
+
+    CvtHex(HA2, HA2Hex);
+
+    // calculate response
+    MD5Init(&Md5Ctx);
+    MD5Update(&Md5Ctx, (unsigned char *) HA1, HASHHEXLEN);
+    MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+    MD5Update(&Md5Ctx, (unsigned char *) pszNonce, strlen(pszNonce));
+    MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+    if (pszQop /* && *pszQop */) {
+        MD5Update(&Md5Ctx, (unsigned char *) pszNonceCount,
+                  strlen(pszNonceCount));
+        MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *) pszCNonce, strlen(pszCNonce));
+        MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+        MD5Update(&Md5Ctx, (unsigned char *) pszQop, strlen(pszQop));
+        MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
+    };
+    MD5Update(&Md5Ctx, (unsigned char *) HA2Hex, HASHHEXLEN);
+    MD5Final((unsigned char *) RespHash, &Md5Ctx);
+
+    CvtHex(RespHash, Response);
+
+    AUTH_DEBUG(DEB_F_PREFIX"HA1=     %s\n", DEB_F_PREFIX_ARGS(SIP_REQ_DIGEST, fname), HA1);
+    AUTH_DEBUG(DEB_F_PREFIX"HEntity= %s\n", DEB_F_PREFIX_ARGS(SIP_REQ_DIGEST, fname), HEntity);
+    AUTH_DEBUG(DEB_F_PREFIX"HA2=     %s\n", DEB_F_PREFIX_ARGS(SIP_REQ_DIGEST, fname), HA2Hex);
+    AUTH_DEBUG(DEB_F_PREFIX"Digest=  %s\n", DEB_F_PREFIX_ARGS(SIP_REQ_DIGEST, fname), Response);
+}
+
+
+void
+DigestString (char *string, HASHHEX response)
+{
+    MD5_CTX Md5Ctx;
+    HASH hash;
+    unsigned int len = strlen(string);
+
+    MD5Init(&Md5Ctx);
+    MD5Update(&Md5Ctx, (unsigned char *)string, len);
+    MD5Final((unsigned char *)hash, &Md5Ctx);
+
+    CvtHex(hash, response);
+}
+
diff --git a/libs/sipcc/core/src-common/kpml_common_util.c b/libs/sipcc/core/src-common/kpml_common_util.c
new file mode 100755 (executable)
index 0000000..95ed584
--- /dev/null
@@ -0,0 +1,388 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+
+#include "cpr_types.h"
+#include "cpr_memory.h"
+#include "cpr_timers.h"
+#include "cpr_strings.h"
+#include "phntask.h"
+#include "ccsip_subsmanager.h"
+#include "singly_link_list.h"
+#include "ccapi.h"
+#include "subapi.h"
+#include "kpmlmap.h"
+#include "dialplanint.h"
+#include "uiapi.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "kpml_common_util.h"
+
+static kpml_status_type_t
+add_char_to_bitmask (char character, unsigned long *bitmask)
+{
+    kpml_status_type_t rc = KPML_STATUS_OK;
+
+    switch (character) {
+    case 'x':
+    case 'X':
+        *bitmask |= REGEX_0_9;
+        break;
+    case '1':
+        *bitmask |= REGEX_1;
+        break;
+    case '2':
+        *bitmask |= REGEX_2;
+        break;
+    case '3':
+        *bitmask |= REGEX_3;
+        break;
+    case '4':
+        *bitmask |= REGEX_4;
+        break;
+    case '5':
+        *bitmask |= REGEX_5;
+        break;
+    case '6':
+        *bitmask |= REGEX_6;
+        break;
+    case '7':
+        *bitmask |= REGEX_7;
+        break;
+    case '8':
+        *bitmask |= REGEX_8;
+        break;
+    case '9':
+        *bitmask |= REGEX_9;
+        break;
+    case '0':
+        *bitmask |= REGEX_0;
+        break;
+    case '*':
+        *bitmask |= REGEX_STAR;
+        break;
+    case '#':
+        *bitmask |= REGEX_POUND;
+        break;
+    case 'A':
+    case 'a':
+        *bitmask |= REGEX_A;
+        break;
+    case 'B':
+    case 'b':
+        *bitmask |= REGEX_B;
+        break;
+    case 'C':
+    case 'c':
+        *bitmask |= REGEX_C;
+        break;
+    case 'D':
+    case 'd':
+        *bitmask |= REGEX_D;
+        break;
+    case '+':
+        *bitmask |= REGEX_PLUS;
+        break;
+
+    default:
+
+        rc = KPML_ERROR_INVALID_VALUE;
+    }
+
+    return (rc);
+}
+
+/*
+ * handle_range_selector
+ *     Range selector is in the form:
+ *        [2-9]  - any of 2-9
+ *
+ * This function will set the bits in the bitmask according
+ * to the specified range selector string.
+ *
+ * Input:  str = selector str (ex. "[2-9]")
+ *         bitmask ptr - ptr to bitmask where bits should
+ *                    be set
+ * Returns: KPML_STATUS_OK -- success
+ *          KPML_ERROR_INVALID_VALUE -- str value is not supported
+ */
+static kpml_status_type_t
+handle_range_selector (char *str, unsigned long *bitmask)
+{
+    static const char *fname = "handle_range_selector";
+    char *char_ptr;
+    int first_digit = 0;
+    int last_digit = 0;
+    long first_shifted = 0;
+    long last_shifted = 0;
+    unsigned long temp_bitmask = 0;
+    char digit[2];
+    kpml_status_type_t rc = KPML_STATUS_OK;
+    long strtol_result;
+    char *strtol_end;
+
+    digit[1] = NUL;
+    if (!str || !bitmask) {
+        KPML_ERROR(KPML_F_PREFIX"Invalid input params", fname);
+        return (KPML_ERROR_INTERNAL);
+    }
+
+    char_ptr = str;
+
+    /* Parse [digit-digit] format */
+
+    /* skip initial '[' */
+    char_ptr++;
+    digit[0] = *char_ptr;
+
+    errno = 0;
+    strtol_result = strtol(digit, &strtol_end, 10);
+
+    if (errno || digit == strtol_end) {
+        KPML_ERROR(KPML_F_PREFIX"digit parse error: %s", __FUNCTION__, digit);
+        return (KPML_ERROR_INTERNAL);
+    }
+
+    first_digit = (int) strtol_result;
+
+    /* now check for '-' */
+    char_ptr++;
+    if (*char_ptr == '-') {
+        char_ptr++;
+        digit[0] = *char_ptr;
+
+        errno = 0;
+        strtol_result = strtol(digit, &strtol_end, 10);
+
+        if (errno || digit == strtol_end) {
+            KPML_ERROR(KPML_F_PREFIX"digit parse error: %s", __FUNCTION__, digit);
+            return (KPML_ERROR_INTERNAL);
+        }
+
+        last_digit = (int) strtol_result;
+
+        /* make sure there is only 1 char after '-' */
+        char_ptr++;
+        if (*char_ptr != ']') {
+            KPML_DEBUG(DEB_F_PREFIX"The Regex format %s is not supported.\n",
+                       DEB_F_PREFIX_ARGS(KPML_INFO, fname), str);
+            rc = KPML_ERROR_INVALID_VALUE;
+
+        } else if (first_digit > last_digit) {
+            KPML_DEBUG(DEB_F_PREFIX"The Regex format %s is not "
+                       "supported. First digit in the range must "
+                       "be greater than the second.\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname), str);
+            rc = KPML_ERROR_INVALID_VALUE;
+        }
+    } else {
+        KPML_DEBUG(DEB_F_PREFIX"The Regex format %s is not supported.\n",
+                   DEB_F_PREFIX_ARGS(KPML_INFO, fname), str);
+        rc = KPML_ERROR_INVALID_VALUE;
+    }
+
+    if (rc == KPML_STATUS_OK) {
+
+        /*
+         * set bitmask range
+         *
+         *  ex) 2-4
+         *    111111111100       (0-9 set, shifted left by 2)
+         *   &  000001111111111  (0-9 set, shifted right by 9-4)
+         *      ----------
+         *      0000011100
+         */
+
+        first_shifted = REGEX_0_9;
+        last_shifted = REGEX_0_9;
+        first_shifted = (first_shifted << first_digit);
+        last_shifted = (last_shifted >> (9 - last_digit));
+        temp_bitmask = (first_shifted & last_shifted);
+        *bitmask |= temp_bitmask;
+    }
+
+    KPML_DEBUG(DEB_F_PREFIX"1st/last digit=%d/%d, bitmask=%lu, "
+               "return status = %d\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname), first_digit,
+               last_digit, *bitmask, rc);
+
+    return (rc);
+}
+
+/*
+ * handle_character_selector
+ *     Character selector is in 2 possible forms:
+ *        [17#]  - 1, 7, or #
+ *        [^01]  - any of 2-9
+ *
+ * This function will set the bits in the bitmask according
+ * to the specified character selector string.
+ *
+ * Input:  str = selector str (ex. "[17#]")
+ *         bitmask ptr - ptr to bitmask where bits should
+ *                    be set
+ * Returns: KPML_STATUS_OK -- success
+ *          KPML_ERROR_INVALID_VALUE -- str value is not supported
+ */
+static kpml_status_type_t
+handle_character_selector (char *str, unsigned long *bitmask)
+{
+    static const char *fname = "handle_character_selector";
+    char *char_ptr;
+    boolean negative_selector = FALSE;
+    unsigned long temp_bitmask = 0;
+    unsigned long all_digits = REGEX_0_9;
+    kpml_status_type_t rc = KPML_STATUS_OK;
+
+    char_ptr = str;
+
+    /*
+     * Check for the default dtmf string [x*#ABCD].  This is the
+     * default regex used by the SIP gws.
+     */
+    if (cpr_strcasecmp(str, KPML_DEFAULT_DTMF_REGEX) == 0) {
+        *bitmask |= REGEX_0_9 | REGEX_STAR | REGEX_POUND |
+                    REGEX_A | REGEX_B | REGEX_C | REGEX_D;
+
+        return (rc);
+    }
+
+    /* skip initial '[' */
+    char_ptr++;
+
+    if (*char_ptr == '^') {
+        /* skip the '^' */
+        char_ptr++;
+        negative_selector = TRUE;
+    }
+
+
+    /* add each character to the bitmask */
+    while ((rc == KPML_STATUS_OK) && (*char_ptr)) {
+
+        if (*char_ptr != ']') {
+            rc = add_char_to_bitmask(*char_ptr, &temp_bitmask);
+            char_ptr++;
+        } else {
+            /* There should not be any characters after the ']' */
+            char_ptr++;
+            if (*char_ptr) {
+                KPML_DEBUG(DEB_F_PREFIX"The Regex format %s is not supported.\n",
+                            DEB_F_PREFIX_ARGS(KPML_INFO, fname), str);
+                rc = KPML_ERROR_INVALID_VALUE;
+            }
+        }
+    }
+
+    if (rc == KPML_STATUS_OK) {
+        if (!negative_selector) {
+            *bitmask |= temp_bitmask;
+        } else {
+            /* only digits are allowed with negative selector */
+            temp_bitmask = ~temp_bitmask;
+            *bitmask |= (temp_bitmask & all_digits);
+        }
+    }
+
+    KPML_DEBUG(DEB_F_PREFIX"bitmask=%lu, return status = %d\n",
+                DEB_F_PREFIX_ARGS(KPML_INFO, fname), *bitmask, rc);
+
+    return (rc);
+
+}
+
+/*
+ * kpml_parser_regex_str
+ *
+ * This function parses a regex string based on the rules in
+ * IETF KPML draft - http://www.ietf.org/internet-drafts/
+ *                         draft-ietf-sipping-kpml-04.txt
+ * Currently only single character patterns are supported.
+ * This function will set the bits in the regex_match bitmask.
+ *
+ * Input:  regex_str = regular expression string (ex. "[x*#ABCD]")
+ *         regex_match = structure which contains a bitmask of
+ *             kpml characters.  The appropriate bits will be set
+ *             by this function.
+ *
+ * Returns: KPML_STATUS_OK -- success
+ *          KPML_ERROR_INVALID_VALUE -- the regex format is not supported
+ *          KPML_ERROR_INTERNAL -- internal error
+ */
+kpml_status_type_t
+kpml_parse_regex_str (char *regex_str, kpml_regex_match_t *regex_match)
+{
+    static const char *fname = "kpml_parse_regex_str";
+    int len;
+    boolean single_char = FALSE;
+    kpml_status_type_t rc = KPML_STATUS_OK;
+
+    if (!regex_str || !regex_match) {
+        KPML_DEBUG(DEB_F_PREFIX"Invalid input params. \n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
+        return (KPML_ERROR_INTERNAL);
+    }
+
+    regex_match->num_digits = 1;
+    regex_match->u.single_digit_bitmask = 0;
+
+    /*
+     * This function currently only handles single-digit pattern
+     * matches.
+     *
+     * Examples:
+     *  x       any single digit 0-9
+     *  2       the digit 2
+     *  [17#]   1,7, or #
+     *  [^01]   any single digit from 2-9
+     *  [2-9]   any single digit from 2-9
+     *  x{1}    any single digit 0-9
+     *  x{1,1}  any single digit 0-9
+     */
+
+    len = strlen(regex_str);
+
+    if (len == 1) {
+        single_char = TRUE;
+    } else {
+        if (regex_str[1] == '{') {
+            if ((strncmp(&regex_str[1], "{1}", 3) == 0) ||
+                (strncmp(&regex_str[1], "{1,1}", 5) == 0)) {
+                single_char = TRUE;
+            } else {
+
+                KPML_DEBUG(DEB_F_PREFIX"The Regex format %s is not supported.\n",
+                           DEB_F_PREFIX_ARGS(KPML_INFO, fname), regex_str);
+                return (KPML_ERROR_INVALID_VALUE);
+            }
+        }
+    }
+
+    if (single_char) {
+        /*  x, x{1}, or x{1,1} format */
+        regex_match->num_digits = 1;
+        rc = add_char_to_bitmask(regex_str[0],
+                                 &(regex_match->u.single_digit_bitmask));
+
+    } else if (regex_str[0] == '[') {
+
+        if (strchr(regex_str, '-')) {
+            /* [2-9] format */
+            rc = handle_range_selector(regex_str,
+                                       &(regex_match->u.single_digit_bitmask));
+        } else {
+
+            /* [17#] or [^01] format */
+            rc = handle_character_selector(regex_str,
+                                           &(regex_match->u.single_digit_bitmask));
+        }
+
+
+    } else {
+        /* The regex format is not supported. */
+        KPML_DEBUG(DEB_F_PREFIX"The Regex format %s is not supported.\n",
+                DEB_F_PREFIX_ARGS(KPML_INFO, fname), regex_str);
+        rc = KPML_ERROR_INVALID_VALUE;
+    }
+
+    return (rc);
+}
diff --git a/libs/sipcc/core/src-common/kpmlmap.c b/libs/sipcc/core/src-common/kpmlmap.c
new file mode 100755 (executable)
index 0000000..9a1e44d
--- /dev/null
@@ -0,0 +1,2129 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_memory.h"
+#include "cpr_timers.h"
+#include "cpr_locks.h"
+#include "phntask.h"
+#include "ccsip_subsmanager.h"
+#include "singly_link_list.h"
+#include "ccapi.h"
+#include "subapi.h"
+#include "fsm.h"
+#include "uiapi.h"
+#include "rtp_defs.h"
+#include "lsm.h"
+#include "lsm_private.h"
+#include "kpmlmap.h"
+#include "dialplanint.h"
+#include "uiapi.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "kpml_common_util.h"
+#include "gsm.h"
+
+cc_int32_t KpmlDebug = 0;
+static sll_handle_t s_kpml_list = NULL;
+static int s_kpml_config;
+
+static void kpml_generate_notify(kpml_data_t *kpml_data, boolean no_body,
+                                 unsigned int resp_code, char *resp_text);
+static void kpml_generate_subscribe_response(kpml_data_t *kpml_data,
+                                             int resp_code);
+static void kpmlmap_show(void);
+void kpml_inter_digit_timer_callback(void *kpml_key_p);
+void kpml_subscription_timer_callback(void *kpml_key);
+static uint32_t g_kpml_id = CC_NO_CALL_ID;
+static cprMutex_t kpml_mutex;
+
+/*
+ *  Function: kpml_get_state()
+ *
+ *  Parameters:
+ *
+ *  Description: Function called to check if the KPML is configured for signaling.
+ *
+ *
+ *  Returns: none
+ */
+boolean
+kpml_get_state (void)
+{
+    config_get_value(CFGID_KPML_ENABLED, &s_kpml_config, sizeof(s_kpml_config));
+
+    if ((s_kpml_config == KPML_NONE) || (s_kpml_config == KPML_DTMF_ONLY)) {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ *  Function: kpml_get_config_value()
+ *
+ *  Parameters:
+ *
+ *  Description: Function called to check if the KPML is enabled.
+ *
+ *
+ *  Returns: none
+ */
+kpml_config_e
+kpml_get_config_value (void)
+{
+    int kpml_config = KPML_NONE;
+    config_get_value(CFGID_KPML_ENABLED, &kpml_config, sizeof(kpml_config));
+
+    return (kpml_config_e) kpml_config;
+}
+
+/*
+ *  Function: kpml_get_new_data()
+ *
+ *  Parameters: none
+ *
+ *  Description: Get a new kpml data
+ *
+ *  Returns: kpml_data_t
+ */
+static kpml_data_t *
+kpml_get_new_data (void)
+{
+    kpml_data_t *kpml_mem;
+
+    kpml_mem = (kpml_data_t *) cpr_malloc(sizeof(kpml_data_t));
+    if (kpml_mem == NULL)
+    {
+        return (NULL);
+    }
+
+    memset(kpml_mem, 0, sizeof(kpml_data_t));
+       kpml_mem->kpml_id = ++g_kpml_id;
+
+    return (kpml_mem);
+}
+
+/*
+ *  Function: kpml_release_data()
+ *
+ *  Parameters: kpml_data_t
+ *
+ *  Description: release kpml data
+ *
+ *  Returns: None
+ */
+
+static void
+kpml_release_data (kpml_data_t * kpml_data)
+{
+    cpr_free(kpml_data);
+}
+
+/*
+ *  Function: kpml_create_sm_key()
+ *
+ *  Parameters: kpml_key_t - key information
+ *              line - line number of the call
+ *              call_id - call indentifier
+ *
+ *  Description: This routine fills line and callid in the key structures.
+ *
+ *  Returns: None
+ */
+static void
+kpml_create_sm_key (kpml_key_t *key_p, line_t line, callid_t call_id,
+                    void *tmr_ptr)
+{
+    static const char fname[] = "kpml_create_sm_key";
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX" timer=0x%0x\n",
+                          DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname), tmr_ptr);
+
+    key_p->line = line;
+    key_p->call_id = call_id;
+    key_p->timer = tmr_ptr;
+}
+
+/*
+ *  Function: kpml_match_line_call_id()
+ *
+ *  Parameters: kpml_data - Kpml subscription data
+ *              kpml_key_t - key information
+ *
+ *  Description: Callback function provided to link list
+ *              to search for a particular line and call_id
+ *
+ *  Returns: ss_match_e
+ */
+static sll_match_e
+kpml_match_line_call_id (kpml_data_t * kpml_data_p, kpml_key_t * key_p)
+{
+    static const char fname[] = "kpml_match_line_call_id";
+
+    if ((kpml_data_p->call_id == key_p->call_id) &&
+        (kpml_data_p->line == key_p->line)) {
+
+        KPML_DEBUG(DEB_L_C_F_PREFIX"Match Found.\n",
+                   DEB_L_C_F_PREFIX_ARGS(KPML_INFO, key_p->line, key_p->call_id, fname));
+        return SLL_MATCH_FOUND;
+    }
+
+    return SLL_MATCH_NOT_FOUND;
+}
+
+/*
+ *  Function: kpml_data_present_for_subid()
+ *
+ *  Parameters: sub_id_t - sub id sent int he message
+ *
+ *  Description: Callback function provided to link list
+ *              to see if there kpml_data that matches a sub_id
+ *
+ *  Returns: kpml_data_t * - Return kpml_data if a match is found
+ */
+static kpml_data_t *
+kpml_data_for_subid(sub_id_t sub_id)
+{
+    kpml_data_t *kpml_data;
+
+    kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
+
+    while (kpml_data != NULL) {
+        if (kpml_data->sub_id == sub_id) {
+            return kpml_data;
+        }
+        kpml_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
+    }
+    return NULL;
+}
+
+/*
+ *  Function: kpml_start_timer()
+ *
+ *  Parameters: line_num - line id on which timer to start
+ *              callId   -  Callid of the call on which timer
+ *                 to start
+ *              timer_ptr - User allocated pointer
+ *              duration - in seconds
+ *              timer_callback- callback function once timer
+ *                    expires
+ *
+ *  Description: start any given timer with duration in seconds
+ *
+ *  Returns: None
+ */
+static void
+kpml_start_timer (line_t line, callid_t callId,
+                  void *timer_ptr, unsigned int duration,
+                  uint32_t kpml_id)
+{
+    if (timer_ptr) {
+
+        (void) cprCancelTimer(timer_ptr);
+
+        (void) cprStartTimer(timer_ptr, duration, (void *)(long)kpml_id);
+    }
+}
+
+/*
+ *  Function: kpml_stop_timer()
+ *
+ *  Parameters: timer_ptr - pointer to timer data to stop
+ *
+ *  Description: stop any specified timer
+ *
+ *  Returns: None
+ */
+static void
+kpml_stop_timer (void *timer_ptr)
+{
+    if (timer_ptr) {
+        (void) cprCancelTimer(timer_ptr);
+
+        (void) cprDestroyTimer(timer_ptr);
+    }
+}
+
+/*
+ *  Function: kpml_clear_timers()
+ *
+ *  Parameters: kpml_data - pointer to kpml data
+ *
+ *  Description: stop and delete all kpml timers
+ *
+ *  Returns: None
+ */
+static void
+kpml_clear_timers (kpml_data_t *kpml_data)
+{
+    static const char fname[] = "kpml_clear_timers";
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"Release kpml timers.\n",
+                DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
+
+    kpml_stop_timer(kpml_data->inter_digit_timer);
+    kpml_data->inter_digit_timer = NULL;
+
+    kpml_stop_timer(kpml_data->critical_timer);
+    kpml_data->critical_timer = NULL;
+
+    kpml_stop_timer(kpml_data->extra_digit_timer);
+    kpml_data->extra_digit_timer = NULL;
+
+    kpml_stop_timer(kpml_data->sub_timer);
+    kpml_data->sub_timer = NULL;
+}
+
+/*
+ *  Function: kpml_start_timers()
+ *
+ *  Parameters: kpml_data_t - subscription data
+ *              kpml_data_t - kpml data
+ *
+ *  Description: start different kpml related timers. Even though all the
+ *              KPML timers are allocated, only interdigit timer will be in use
+ *
+ *  Returns: None
+ */
+static void
+kpml_start_timers (kpml_data_t *kpml_data)
+{
+    static const char *fname ="kpml_start_timers";
+
+    kpml_data->inter_digit_timer =
+        cprCreateTimer("Interdigit timer", GSM_KPML_INTER_DIGIT_TIMER,
+                       TIMER_EXPIRATION, gsm_msg_queue);
+
+    kpml_data->critical_timer =
+        cprCreateTimer("Criticaldigit timer", GSM_KPML_CRITICAL_DIGIT_TIMER,
+                       TIMER_EXPIRATION, gsm_msg_queue);
+
+    kpml_data->extra_digit_timer =
+        cprCreateTimer("Extradigit timer", GSM_KPML_EXTRA_DIGIT_TIMER,
+                       TIMER_EXPIRATION, gsm_msg_queue);
+
+    /* Check if any of the timer cannot be allocated */
+    if (kpml_data->inter_digit_timer == NULL ||
+        kpml_data->critical_timer == NULL ||
+        kpml_data->extra_digit_timer == NULL) {
+
+        /* generate error to indicate timer cannot be allocated */
+        KPML_ERROR(KPML_F_PREFIX"No memory to allocate timer\n",
+                    fname);
+        return;
+    }
+
+    /* Start interdigit timer */
+    kpml_start_timer(kpml_data->line, kpml_data->call_id,
+                     kpml_data->inter_digit_timer,
+                     kpml_data->inttimeout, kpml_data->kpml_id);
+
+    /* Start critical timer */
+    kpml_start_timer(kpml_data->line, kpml_data->call_id,
+                     kpml_data->critical_timer,
+                     kpml_data->crittimeout, kpml_data->kpml_id);
+
+    /* Start extra digit timer */
+    kpml_start_timer(kpml_data->line, kpml_data->call_id,
+                     kpml_data->extra_digit_timer,
+                     kpml_data->extratimeout, kpml_data->kpml_id);
+
+}
+
+/*
+ *  Function: kpml_restart_timers()
+ *
+ *  Parameters: kpml_data_t - subscription data
+ *              kpml_data_t - kpml data
+ *
+ *  Description: Restart all the KPML related timers
+ *
+ *  Returns: None
+ */
+static void
+kpml_restart_timers (kpml_data_t * kpml_data)
+{
+    static const char fname[] = "kpml_restart_timers";
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"Restart all timers\n",
+               DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
+
+    kpml_stop_timer(kpml_data->critical_timer);
+
+    kpml_stop_timer(kpml_data->inter_digit_timer);
+
+    kpml_stop_timer(kpml_data->extra_digit_timer);
+
+    kpml_start_timers(kpml_data);
+
+    return;
+}
+
+/*
+ *  Function: kpml_clear_data()
+ *
+ *  Parameters: kpml_data - Subscription related information
+ *              kpml_data  - KPML realated information
+ *              kpml_sub_type_e - Type of kpml subscription - persistent
+ *                            single-notify or one shot
+ *
+ *  Description: Clear the KPML subscription data depending upon
+ *            KPML request type. If the KPML request type is one shot
+ *            clear the data or else maintain it for the duration of the
+ *            subscription.
+ *
+ *  Returns: kpml_data next in the list if succesful
+ *            NULL - if not
+ */
+static boolean
+kpml_clear_data (kpml_data_t *kpml_data, kpml_sub_type_e sub_type)
+{
+    static const char fname[] = "kpml_clear_data";
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"sub_type=%d",
+               DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname), sub_type);
+
+    switch (sub_type) {
+
+    case KPML_ONE_SHOT:
+        KPML_DEBUG(DEB_F_PREFIX"One shot\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
+
+        kpml_stop_timer(kpml_data->inter_digit_timer);
+        kpml_data->inter_digit_timer = NULL;
+
+        kpml_stop_timer(kpml_data->critical_timer);
+        kpml_data->critical_timer = NULL;
+
+        kpml_stop_timer(kpml_data->extra_digit_timer);
+        kpml_data->extra_digit_timer = NULL;
+
+        kpml_stop_timer(kpml_data->sub_timer);
+        kpml_data->sub_timer = NULL;
+
+        (void) sll_remove(s_kpml_list, kpml_data);
+
+        kpml_release_data(kpml_data);
+
+        kpmlmap_show();
+        return (TRUE);
+
+    case KPML_PERSISTENT:
+        KPML_DEBUG(DEB_F_PREFIX"Persistent\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
+        /* FALLTHROUGH */
+
+    case KPML_SINGLY_NOTIFY:
+        KPML_DEBUG(DEB_F_PREFIX"Singly notify\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
+
+        /*
+         * persistent KPML request so clear the
+         * digit buffer and then restart all the timer
+         */
+        kpml_data->kpmlDialed[0] = 00;
+
+        kpml_restart_timers(kpml_data);
+        /*
+         * if the persistent request is singly-notify
+         * do not terminate the subscription, but do
+         * not notify any more digits untill application
+         * sends out new kpml document. At this time persistent
+         * subsciption is not supported.
+         */
+
+        kpmlmap_show();
+        return (FALSE);
+
+    default:
+        KPML_DEBUG(DEB_F_PREFIX"KPML type not specified\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
+        return (FALSE);
+    }
+}
+
+/*
+ *  Function: kpml_quarantine_digits()
+ *
+ *  Parameters: line - line number
+ *              call_id - call id of the dialog
+ *              digits - collected difits
+ *              len - Length of collected digits, currently this length
+ *               is 1
+ *
+ *  Description: Quarantine collected digits in a chain of subscription
+ *              strucutre. if there is no subscription structure creaded
+ *              already then create a new one. The maximum number of digits
+ *              collected per dialog is 256. if there are more digits need to
+ *              be collected then overwrite the oldest digit.
+ *
+ *  Returns: None
+ */
+void
+kpml_quarantine_digits (line_t line, callid_t call_id, char digit)
+{
+    static const char fname[] = "kpml_quarantine_digits";
+    kpml_data_t *kpml_data;
+    kpml_key_t kpml_key;
+
+    if (kpml_get_config_value() == KPML_NONE) {
+        return;
+    }
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"digit=0x%0x\n",
+               DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname), digit);
+
+    kpml_create_sm_key(&kpml_key, line, call_id, NULL);
+
+    kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
+
+    if (!kpml_data) {
+
+        kpml_data = kpml_get_new_data();
+
+        if (kpml_data == NULL) {
+            KPML_ERROR(KPML_F_PREFIX"No memory for subscription data\n",
+                    fname);
+            return;
+        }
+
+        (void) sll_append(s_kpml_list, kpml_data);
+
+        kpml_data->line = line;
+
+        kpml_data->call_id = call_id;
+
+        kpml_data->pending_sub = FALSE;
+
+        kpml_data->dig_head = kpml_data->dig_tail = 0;
+
+    }
+
+    if (kpml_data->dig_head == (kpml_data->dig_tail + 1) % MAX_DIALSTRING) {
+
+        /* buffer is full so discard old collected digits */
+        kpml_data->dig_head = (kpml_data->dig_head + 1) % MAX_DIALSTRING;
+    }
+
+    kpml_data->q_digits[kpml_data->dig_tail] = digit;
+
+    kpml_data->dig_tail = (kpml_data->dig_tail + 1) % MAX_DIALSTRING;
+}
+
+
+/*
+ *  Function: kpml_flush_quarantine_buffer()
+ *
+ *  Parameters: line - line number
+ *              call_id - call id of the dialog
+ *
+ *  Description: This function will be called to flush any
+ *       quarantined buffers.
+ *
+ *  Returns: None
+ */
+void
+kpml_flush_quarantine_buffer (line_t line, callid_t call_id)
+{
+    static const char fname[] = "kpml_flush_quarantine_buffer";
+    kpml_data_t *kpml_data;
+    kpml_key_t kpml_key;
+
+    if (kpml_get_config_value() == KPML_NONE) {
+        return;
+    }
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"Flush buffer\n", DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname));
+
+    kpml_create_sm_key(&kpml_key, line, call_id, NULL);
+
+    kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
+
+    if (kpml_data) {
+
+        if (!kpml_data->pending_sub) {
+
+            kpml_data->dig_head = kpml_data->dig_tail = 0;
+            (void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
+        }
+
+    }
+
+}
+
+/*
+ *  Function: kpml_update_quarantined_digits()
+ *
+ *  Parameters: kpml_data_t : subscription type
+ *
+ *  Description: This function will be called to update any
+ *       quarantined buffer through KPML patter map.
+ *
+ *  Returns: None
+ */
+static void
+kpml_update_quarantined_digits (kpml_data_t *kpml_data)
+{
+    static const char fname[] = "kpml_update_quarantined_digits";
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"Update quarantined digits\n",
+               DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
+
+    while (kpml_data->dig_head != kpml_data->dig_tail) {
+
+        kpml_update_dialed_digits(kpml_data->line,
+                                          kpml_data->call_id,
+                                          kpml_data->q_digits[kpml_data->dig_head]);
+
+        kpml_data->dig_head = (kpml_data->dig_head + 1) % MAX_DIALSTRING;
+    }
+}
+
+/*
+ *  Function: kpml_digit_timer_callback()
+ *
+ *  Parameters: timer - pointer to timer
+ *              line - Line number
+ *              call_id - call id
+ *
+ *  Description: Handle different digit timer events. At this time it
+ *          hanles only inter digit timer.
+ *
+ *  Returns: None
+ */
+void
+kpml_inter_digit_timer_callback (void *kpml_key_p)
+{
+    (void) app_send_message(&kpml_key_p, sizeof(void *), CC_SRC_GSM,
+                            SUB_MSG_KPML_DIGIT_TIMER);
+}
+
+kpml_data_t *kpml_get_kpml_data_from_kpml_id(uint32_t kpml_id)
+{
+    kpml_data_t *kpml_data;
+
+    kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
+
+    while (kpml_data != NULL && kpml_data->kpml_id != kpml_id) {
+
+        kpml_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
+    }
+    return(kpml_data);
+}
+
+/*
+ *  Function: digit_timer_callback()
+ *
+ *  Parameters: timer - pointer to timer
+ *              line - Line number
+ *              call_id - call id
+ *
+ *  Description: Handle different digit timer events. At this time it
+ *          hanles only inter digit timer.
+ *
+ *  Returns: None
+ */
+static void
+kpml_inter_digit_timer_event (void **kpml_key_p)
+{
+    const char fname[] = "kpml_inter_digit_timer_callback";
+    kpml_data_t *kpml_data;
+
+    KPML_DEBUG("%s: kpml_id=%d \n ",
+               fname, (long)*kpml_key_p);
+
+
+    kpml_data = kpml_get_kpml_data_from_kpml_id((long)*kpml_key_p);
+
+    if (kpml_data == NULL) {
+        KPML_ERROR(KPML_F_PREFIX"KPML data not found.\n", fname);
+        return;
+    }
+
+        KPML_DEBUG(": Interdigit Timer\n");
+
+        kpml_generate_notify(kpml_data, FALSE, KPML_TIMER_EXPIRE,
+                             KPML_TIMER_EXPIRE_STR);
+
+        /* Timer expired so clear the KPML data */
+        (void) kpml_clear_data(kpml_data, kpml_data->persistent);
+}
+
+/*
+ *  Function: kpml_start_subscription_timer()
+ *
+ *  Parameters: kpml_data_t - subscription data
+ *
+ *  Description: Allocate and start subscription timer
+ *
+ *  Returns: Pointer to timer
+ */
+static void *
+kpml_start_subscription_timer (kpml_data_t * kpml_data, unsigned long duration)
+{
+    static const char fname[] = "kpml_start_subscription_timer";
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"duration=%u\n",
+               DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname), duration);
+
+    kpml_data->sub_timer = cprCreateTimer("sub timer",
+                                          GSM_KPML_SUBSCRIPTION_TIMER,
+                                          TIMER_EXPIRATION, gsm_msg_queue);
+
+    kpml_data->sub_duration = duration;
+
+    kpml_create_sm_key(&(kpml_data->subtimer_key), kpml_data->line,
+                       kpml_data->call_id, kpml_data->sub_timer);
+
+    /* Start subscription duration timer */
+    kpml_start_timer(kpml_data->line,
+                     kpml_data->call_id,
+                     kpml_data->sub_timer,
+                     kpml_data->sub_duration * 1000,
+                     kpml_data->kpml_id);
+
+    return (kpml_data->sub_timer);
+}
+
+
+/*
+ *  Function: kpml_subscription_timer_callback()
+ *
+ *  Parameters: timer - timer pointer
+ *
+ *  Description: This routine called by timer callback once the subscription
+ *          expires.
+ *
+ *  Returns: None
+ */
+void
+kpml_subscription_timer_callback (void *kpml_key_p)
+{
+    (void) app_send_message(&kpml_key_p, sizeof(void *), CC_SRC_GSM,
+                            SUB_MSG_KPML_SUBSCRIBE_TIMER);
+}
+
+/*
+ *  Function: kpml_subscription_timer_event()
+ *
+ *  Parameters: timer - timer pointer
+ *
+ *  Description: This routine called by timer callback once the subscription
+ *          expires.
+ *
+ *  Returns: None
+ */
+static void
+kpml_subscription_timer_event (void **kpml_key_p)
+{
+    static const char fname[] = "kpml_subscription_timer_event";
+    kpml_data_t *kpml_data;
+
+    KPML_DEBUG("%s: kpml_id=%d \n ",
+               fname, (long)*kpml_key_p);
+
+    kpml_data = kpml_get_kpml_data_from_kpml_id((long)*kpml_key_p);
+
+    /* Evaluate if we need to send empty notify */
+    if (kpml_data) {
+        kpml_generate_notify(kpml_data, FALSE, KPML_SUB_EXPIRE,
+                             KPML_SUB_EXPIRE_STR);
+
+        (void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
+    }
+}
+
+/*
+ *  Function: kpml_match_requested_digit()
+ *
+ *  Parameters: input - single char
+ *
+ *  Description: Determine if the char is 0-9 or *
+ *               # is not matched here since it is
+ *               primarily used as "dial immediately"
+ *
+ *  Returns: boolean
+ */
+static boolean
+kpml_match_requested_digit (kpml_regex_match_t *regex_match, char input)
+{
+    uint32_t *bitmask;
+    uint32_t result = 0;
+
+    bitmask = (uint32_t *) &(regex_match->u.single_digit_bitmask);
+
+    switch (input) {
+    case '1':
+        result = (*bitmask & REGEX_1);
+        break;
+    case '2':
+        result = (*bitmask & REGEX_2);
+        break;
+    case '3':
+        result = (*bitmask & REGEX_3);
+        break;
+    case '4':
+        result = (*bitmask & REGEX_4);
+        break;
+    case '5':
+        result = (*bitmask & REGEX_5);
+        break;
+    case '6':
+        result = (*bitmask & REGEX_6);
+        break;
+    case '7':
+        result = (*bitmask & REGEX_7);
+        break;
+    case '8':
+        result = (*bitmask & REGEX_8);
+        break;
+    case '9':
+        result = (*bitmask & REGEX_9);
+        break;
+    case '0':
+        result = (*bitmask & REGEX_0);
+        break;
+    case '*':
+        result = (*bitmask & REGEX_STAR);
+        break;
+    case '#':
+        result = (*bitmask & REGEX_POUND);
+        break;
+    case 'A':
+        result = (*bitmask & REGEX_A);
+        break;
+    case 'B':
+        result = (*bitmask & REGEX_B);
+        break;
+    case 'C':
+        result = (*bitmask & REGEX_C);
+        break;
+    case 'D':
+        result = (*bitmask & REGEX_D);
+        break;
+    case '+':
+        result = (*bitmask & REGEX_PLUS);
+        break;
+    default:
+        result = 0;
+        break;
+    }
+
+    if (result) {
+        return (TRUE);
+    }
+
+    return (FALSE);
+}
+
+/*
+ *  Function: kpml_match_pattern
+ *
+ *  Parameters: kpml_data_t - Data strcuture holding digits and KPML
+ *          parameters
+ *
+ *  Description: At this time only one digit reporting is supported. So
+ *        match the incoming digit to 1st template
+ *
+ *  Returns: kpml_match_action_e
+ */
+static kpml_match_action_e
+kpml_match_pattern (kpml_data_t *ptempl)
+{
+    char *pinput;
+    int regex_index = 0;
+
+    pinput = &ptempl->kpmlDialed[0];
+
+    if (strchr(pinput, '#') && ptempl->enterkey) {
+
+        return (KPML_IMMEDIATELY);
+    }
+
+    if (kpml_match_requested_digit
+        (&(ptempl->regex_match[regex_index]), pinput[0])) {
+        return (KPML_FULLPATTERN);
+    }
+
+    return (KPML_NOMATCH);
+}
+
+/*
+ *  Function: kpml_is_subscribed()
+ *
+ *  Parameters: line - line number on which SUB created
+ *              call_id - call id of the call
+ *
+ *  Description: checks if kpml is susbcribed for this call.
+ *
+ *  Returns: status - TRUE - if there is valid subscription
+ *                    FALSE - if there is no valid subscription
+ */
+boolean kpml_is_subscribed (callid_t call_id, line_t line)
+{
+    static const char fname[] = "kpml_is_subscribed";
+    kpml_data_t *kpml_data, *kpml_next_data;
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname));
+
+    kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
+    while (kpml_data) {
+        kpml_next_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
+        if (kpml_data->pending_sub &&
+            kpml_data->line == line &&
+            kpml_data->call_id == call_id) {
+            return TRUE;
+        }
+        kpml_data = kpml_next_data;
+    }
+    return FALSE;
+}
+
+/*
+ *  Function: kpml_update_dialed_digits()
+ *
+ *  Parameters: line - line number on which SUB created
+ *              call_id - call id of the call
+ *              digits - digit collected (it accepts only one digit now)
+ *              len - length of the digit string =1
+ *
+ *  Description: Routine called by UI to update any collected digits. At this time
+ *          this routine handles only one digits at a time. Any given pattern will be
+ *          matched with incoming KPML patter. if there is a match notify will be
+ *          sent out. If there is no match digits are buffered for the future use.
+ *
+ *  Returns: status - TRUE - if there is valid subscription
+ *                    FALSE - if there is no valis subscription
+ */
+kpml_state_e
+kpml_update_dialed_digits (line_t line, callid_t call_id, char digit)
+{
+    static const char fname[] = "kpml_update_dialed_digits";
+    kpml_data_t *kpml_data, *kpml_next_data;
+    kpml_match_action_e result = KPML_NOMATCH;
+    int dial_len = 0;
+    kpml_state_e state = NO_SUB_DATA;
+
+
+    if (kpml_get_config_value() == KPML_NONE) {
+        return (state);
+    }
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"digits=0x%x\n",
+               DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname), digit);
+
+    kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
+
+    while (kpml_data) {
+
+        kpml_next_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
+
+        if (kpml_data->pending_sub &&
+            kpml_data->line == line &&
+            kpml_data->call_id == call_id) {
+
+            state = SUB_DATA_FOUND;
+
+            /* update the digit string */
+            dial_len = strlen(kpml_data->kpmlDialed);
+            if (dial_len >= MAX_DIALSTRING-1)
+            {  // not enough room
+                KPML_ERROR(DEB_L_C_F_PREFIX"dial_len = [%d] too large\n", DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname), dial_len);
+                return (state);
+            }
+
+            //If DIAL softkey is pressed, just send the NOTIFY with 423
+            //notfying the CCM that user is done dialing
+            if (digit == (char)0x82) {
+                kpml_generate_notify(kpml_data, FALSE, KPML_TIMER_EXPIRE,
+                             KPML_TIMER_EXPIRE_STR);
+                memset(&kpml_data->kpmlDialed[0], 0, MAX_DIALSTRING);
+                (void) kpml_clear_data(kpml_data, kpml_data->persistent);
+                state = NOTIFY_SENT;
+                kpml_data = kpml_next_data;
+                continue;
+            }
+
+            if (digit == 0x0F) {
+
+                kpml_data->kpmlDialed[dial_len] = '#';
+
+            } else if (digit == 0x0E) {
+
+                kpml_data->kpmlDialed[dial_len] = '*';
+
+            } else {
+
+                kpml_data->kpmlDialed[dial_len] = digit;
+            }
+
+            kpml_data->kpmlDialed[dial_len + 1] = 00;
+
+            if (digit == BKSPACE_KEY) {
+
+                kpml_data->last_dig_bkspace = TRUE;
+
+                sstrncpy(kpml_data->kpmlDialed, "bs", sizeof(kpml_data->kpmlDialed));
+
+                result = KPML_FULLPATTERN;
+
+            } else {
+
+                result = kpml_match_pattern(kpml_data);
+            }
+
+            switch (result) {
+
+            case KPML_FULLPATTERN:
+
+                kpml_generate_notify(kpml_data, FALSE, KPML_SUCCESS,
+                                     KPML_SUCCESS_STR);
+
+                dp_store_digits(line, call_id,
+                                (unsigned char)((digit == (char) BKSPACE_KEY) ?
+                                                 BKSPACE_KEY : kpml_data->
+                                                 kpmlDialed[dial_len]));
+                /* if not persistent request clear the subscription */
+                memset(&kpml_data->kpmlDialed[0], 0, MAX_DIALSTRING);
+
+                (void) kpml_clear_data(kpml_data, kpml_data->persistent);
+
+                state = NOTIFY_SENT;
+                break;
+
+            case KPML_IMMEDIATELY:
+                /* Do not sent out '#' key remove that from the buffer */
+                kpml_data->kpmlDialed[dial_len] = 00;
+
+                kpml_generate_notify(kpml_data, FALSE, KPML_USER_TERM_NOMATCH,
+                                     KPML_USER_TERM_NOMATCH_STR);
+
+                dp_store_digits(line, call_id,
+                                (unsigned char)((digit == (char) BKSPACE_KEY) ?
+                                                 BKSPACE_KEY : kpml_data->
+                                                 kpmlDialed[dial_len]));
+
+                memset(&kpml_data->kpmlDialed[0], 0, MAX_DIALSTRING);
+
+                /* if not persistent request clear the subscription */
+                (void) kpml_clear_data(kpml_data, kpml_data->persistent);
+
+                state = NOTIFY_SENT;
+                break;
+
+            default:
+                memset(&kpml_data->kpmlDialed[0], 0, MAX_DIALSTRING);
+                /* Restart the digit timers */
+                kpml_restart_timers(kpml_data);
+                break;
+            }
+        }
+
+        /* if there is only one kpml_data then we should stop here.
+         * Memory for this is already released in kpml_generate_notify function
+         */
+        kpml_data = kpml_next_data;
+    }
+
+    return (state);
+}
+
+/*
+ *  Function: kpml_set_subscription_reject()
+ *
+ *  Parameters: line, call_id to indentify line and call_id
+ *
+ *  Description:
+ *      Set variable to specify that subscription should be
+ *  rejected on this line. There are error cases wehere INV
+ *  has been sent premeturaly because of error condition, then
+ *  subscription on such a line shoule be rejected.
+ *
+ *  Returns: none
+ */
+void
+kpml_set_subscription_reject (line_t line, callid_t call_id)
+{
+    static const char fname[] = "kpml_set_subscription_reject";
+    kpml_data_t *kpml_data;
+    kpml_key_t kpml_key;
+
+    if (kpml_get_config_value() == KPML_NONE) {
+        return;
+    }
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"Reject\n", DEB_L_C_F_PREFIX_ARGS(KPML_INFO, line, call_id, fname));
+
+    kpml_create_sm_key(&kpml_key, line, call_id, NULL);
+
+    kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
+
+    if (kpml_data == NULL) {
+
+        kpml_data = kpml_get_new_data();
+
+        if (kpml_data == NULL) {
+            KPML_ERROR(KPML_F_PREFIX"No memory for subscription data\n",
+                        fname);
+            return;
+        }
+
+        (void) sll_append(s_kpml_list, kpml_data);
+
+        kpml_data->line = line;
+
+        kpml_data->call_id = call_id;
+
+        kpml_data->pending_sub = FALSE;
+
+        kpml_data->dig_head = kpml_data->dig_tail = 0;
+    }
+
+    kpml_data->sub_reject = TRUE;
+}
+
+/*
+ *  Function: check_subcription_create_error()
+ *
+ *  Parameters: Kpml - kpml related data
+ *
+ *  Description: Check subscription state to see if the subscription can be
+ *          created.
+ *
+ *  Returns: Kpml response code
+ *
+ */
+
+static kpml_resp_code_e
+check_subcription_create_error (kpml_data_t *kpml_data)
+{
+    static const char fname[] = "check_subcription_create_error";
+    lsm_states_t lsm_state;
+
+    lsm_state = lsm_get_state(kpml_data->call_id);
+
+    if (lsm_state == LSM_S_NONE) {
+
+        KPML_ERROR(KPML_L_C_F_PREFIX"NO call with id\n",
+                    kpml_data->line, kpml_data->call_id, fname);
+        return (KPML_BAD_EVENT);
+    }
+
+    if ((lsm_state < LSM_S_CONNECTED) && kpml_data->sub_reject) {
+
+        KPML_ERROR(KPML_L_C_F_PREFIX"Call not in connected state\n",
+                    kpml_data->line, kpml_data->call_id, fname);
+        return (KPML_BAD_EVENT);
+    }
+
+    /* Call is in connected state. So do not reject the subscription */
+    kpml_data->sub_reject = FALSE;
+
+    return (KPML_SUCCESS);
+}
+
+/*
+ *  Function: check_if_kpml_attributes_supported()
+ *
+ *  Parameters: Kpml - kpml related data
+ *
+ *  Description: check if the attribute supported in the KPML request
+ *
+ *  Returns: Kpml response code
+ *
+ */
+static kpml_resp_code_e
+check_if_kpml_attributes_supported (KPMLRequest *kpml_sub_data)
+{
+
+    if (kpml_sub_data == NULL) {
+
+        return (KPML_BAD_DOC);
+    }
+
+    /* 501 : bad document pre
+     * header is not supported. If the request carry this reject it
+     */
+    if ((kpml_sub_data->pattern.longhold != 0) ||
+        (kpml_sub_data->pattern.longrepeat != 0) ||
+        (kpml_sub_data->pattern.nopartial != 0)) {
+        return (KPML_BAD_DOC);
+    }
+
+    return (KPML_SUCCESS);
+}
+
+/*
+ *  Function: check_attributes_range()
+ *
+ *  Parameters: Kpml - kpml related data
+ *
+ *  Description: check attribute range
+ *
+ *  Returns: kpml response code
+ */
+static kpml_resp_code_e
+check_attributes_range (KPMLRequest * kpml_data)
+{
+    return (KPML_SUCCESS);
+}
+
+/*
+ *  Function: check_kpml_config()
+ *
+ *  Parameters: line and call_id
+ *
+ *  Description: check kpml config
+ *
+ *  Returns: kpml response code
+ */
+static kpml_resp_code_e
+check_kpml_config (line_t line, callid_t call_id)
+{
+    static const char fname[] = "check_kpml_config";
+    lsm_states_t lsm_state;
+
+    lsm_state = lsm_get_state(call_id);
+
+    if (lsm_state == LSM_S_NONE) {
+        KPML_ERROR(KPML_L_C_F_PREFIX"NO call\n",
+                    line, call_id, fname);
+        return (KPML_BAD_EVENT);
+    }
+
+    /* Get kpml configuration */
+    config_get_value(CFGID_KPML_ENABLED, &s_kpml_config, sizeof(s_kpml_config));
+
+    switch (lsm_state) {
+    case LSM_S_OFFHOOK:
+    case LSM_S_PROCEED:
+
+        if ((s_kpml_config == KPML_SIGNAL_ONLY) || (s_kpml_config == KPML_BOTH)) {
+            return (KPML_SUCCESS);
+        }
+
+        break;
+
+    case LSM_S_RINGOUT:
+    case LSM_S_CONNECTED:
+    case LSM_S_HOLDING:
+
+        if ((s_kpml_config == KPML_DTMF_ONLY) || (s_kpml_config == KPML_BOTH)) {
+            return (KPML_SUCCESS);
+        }
+
+        break;
+    default:
+
+        break;
+    }
+
+    KPML_ERROR(KPML_L_C_F_PREFIX"KPML disabled - Check your conifg 0- None, \
+            1-signaling, 2-dtmf 3-both\n", line, call_id, fname);
+    return (KPML_BAD_EVENT);
+}
+
+/*
+ *  Function: kpml_treat_enterkey()
+ *
+ *  Parameters: Kpml - kpml related data
+ *
+ *  Description: Currently we support only '#' in the enter key string.
+ *             If there is anything apart from '#' reject the subscription
+ *
+ *
+ *  Returns: kpml_resp_code_e
+ */
+static kpml_resp_code_e
+kpml_treat_enterkey (kpml_data_t *kpml_data, char *enter_str)
+{
+    /* Enterkey can have only 2 values empty or "#" */
+
+    if (enter_str[0] == NUL) {
+
+        kpml_data->enterkey = FALSE;
+
+    } else if (!strcmp(enter_str, KPML_ENTER_STR)) {
+
+        kpml_data->enterkey = TRUE;
+
+    } else {
+
+        return (KPML_BAD_DOC);
+
+    }
+
+    return (KPML_SUCCESS);
+}
+
+/*
+ *  Function: kpml_treat_regex()
+ *
+ *  Parameters: Kpml - kpml related data
+ *
+ *  Description: Skip white space, and check content of regex
+ *             Check for backspace presence as well. Regular
+ *             expression can contain only sequence of 'x', space,
+ *             '|' and 'bs' (for backspace).
+ *  Example : x | bs, xxx|bs, x|bs etc.
+ *
+ *  Returns: kpml_resp_code_e
+ */
+static kpml_resp_code_e
+kpml_treat_regex (kpml_data_t *kpml_data)
+{
+    static const char fname[] = "kpml_treat_regex";
+    short indx = 0, char_inx, i, regex_idx = 0;
+    char regex_temp[32];
+
+    kpml_data->enable_backspace = FALSE;
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"regex=%u\n",
+               DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname),
+               kpml_data->regex[indx].regexData);
+
+    /* skip white space and check for backspace */
+    while (indx < NUM_OF_REGX) {
+        char_inx = 0;
+        i = 0;
+        regex_idx = 0;
+
+        while (kpml_data->regex[indx].regexData[char_inx]) {
+            switch (kpml_data->regex[indx].regexData[char_inx]) {
+            case ' ':
+                break;
+            case '|':
+                break;
+            case 'b':
+                if (kpml_data->regex[indx].regexData[char_inx + 1] == 's') {
+                    char_inx++;
+                    kpml_data->enable_backspace = TRUE;
+                } else {
+                    return (KPML_BAD_DOC);
+                }
+
+                break;
+            case 'x':
+            default:
+                regex_temp[i++] = kpml_data->regex[indx].regexData[char_inx];
+                break;
+            }
+            char_inx++;
+        }
+
+        regex_temp[i] = NUL;
+
+        /* parse regex string */
+        if (kpml_parse_regex_str(&(regex_temp[0]),
+                                 &(kpml_data->regex_match[indx])) !=
+            KPML_STATUS_OK) {
+            KPML_ERROR(KPML_F_PREFIX"Regex parse error.\n",fname);
+            return (KPML_BAD_DOC);
+        }
+
+        /* create digit map string such as xx to match the digits */
+        while (regex_idx < kpml_data->regex_match[indx].num_digits) {
+            kpml_data->regex[indx].regexData[regex_idx++] = 'x';
+        }
+
+        kpml_data->regex[indx].regexData[regex_idx] = NUL;
+
+
+        /* Check for next <regex> string */
+        indx++;
+    }
+    return (KPML_SUCCESS);
+}
+
+/*
+ *  Function: kpml_update_data()
+ *
+ *  Parameters: Kpml - kpml related data from incoming subscribe
+ *
+ *  Description: Add a dial template to the known list of templates
+ *
+ *  Returns: kpml_data_t
+ */
+static kpml_data_t *
+kpml_update_data (kpml_data_t *kpml_data, KPMLRequest *kpml_sub_data)
+{
+    static const char fname[] = "kpml_update_data";
+
+    if ((kpml_sub_data == NULL) || (kpml_data == NULL)) {
+        return (kpml_data);
+    }
+
+    memcpy((char *) &(kpml_data->regex),
+           (char *) &(kpml_sub_data->pattern.regex),
+           sizeof(Regex) * NUM_OF_REGX);
+
+    kpml_data->persistent = kpml_sub_data->pattern.persist;
+
+    kpml_data->inttimeout = kpml_sub_data->pattern.interdigittimer;
+    kpml_data->crittimeout = kpml_sub_data->pattern.criticaldigittimer;
+    kpml_data->extratimeout = kpml_sub_data->pattern.extradigittimer;
+
+    kpml_data->flush = kpml_sub_data->pattern.flush;
+
+    /* clear all collected digits, SUB requested with flush */
+    if (kpml_sub_data->pattern.flush) {
+
+        kpml_data->kpmlDialed[0] = 00;
+    }
+
+    kpml_data->longhold = kpml_sub_data->pattern.longhold;
+
+    kpml_data->longrepeat = kpml_sub_data->pattern.longrepeat;
+
+    kpml_data->nopartial = kpml_sub_data->pattern.nopartial;
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"regex=%u"
+               "persistent=%d int-timer=%u critic-timer=%u, extra-timer=%u"
+               "flush=%d longhold=%d longrepeat=%d nopartial=%d\n",
+               DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname),
+                          kpml_data->regex, kpml_data->persistent, kpml_data->inttimeout,
+               kpml_data->crittimeout, kpml_data->extratimeout,
+               kpml_data->flush, kpml_data->longhold,
+               kpml_data->longrepeat, kpml_data->nopartial);
+
+    return (kpml_data);
+}
+
+/*
+ *  Function: kpml_terminate_subscription()
+ *
+ *  Parameters: ccsip_sub_not_data_t - msg passed from SIP stack
+ *
+ *  Description: Received terminate event from sip stack, probably
+ *        something wrong and wants to terminate the subscription.
+ *
+ *  Returns: None
+ */
+static void
+kpml_terminate_subscription (ccsip_sub_not_data_t *msg)
+{
+    static const char fname[] = "kpml_terminate_subscribe";
+    kpml_data_t *kpml_data = NULL;
+    boolean     normal_terminate;
+    lsm_lcb_t *lcb;
+
+    KPML_DEBUG(DEB_F_PREFIX"entered.\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
+
+    if (kpml_get_config_value() == KPML_NONE) {
+        return;
+    }
+
+    if (msg->sub_id == (unsigned int)-1) {
+        KPML_ERROR(KPML_L_C_F_PREFIX"Invalid sub_id=%d\n", msg->line_id,
+                   msg->gsm_id, fname, msg->sub_id);
+        return;
+    }
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"sub_id=%d, reason=%d\n",
+                DEB_L_C_F_PREFIX_ARGS(KPML_INFO, msg->line_id, msg->gsm_id, fname),
+                msg->sub_id, msg->reason_code);
+    /*
+     * If the terminate reason is caused by local action,
+     * then there is no need to send any notify to the network.
+     */
+    switch (msg->reason_code) {
+    case SM_REASON_CODE_SHUTDOWN:
+    case SM_REASON_CODE_ROLLOVER:
+    case SM_REASON_CODE_RESET_REG:
+        /*
+         * These errors are caused by failure or system being shutting down.
+         * Subscription manager will automatically clean up the subscription
+         * The application just needs to clean up the associated data
+         * strutures.
+         */
+        normal_terminate = FALSE;
+        break;
+    default:
+        normal_terminate = TRUE;
+        break;
+    }
+
+    //Acquire kpml lock
+    cprGetMutex(kpml_mutex);
+
+    kpml_data = kpml_data_for_subid(msg->sub_id);
+
+    if (kpml_data) {
+
+        kpml_data->persistent = KPML_ONE_SHOT;
+
+        /*
+         * For ignore generating notify and release in the case
+         * of non normal terminate as determined above.
+         */
+        if (normal_terminate) {
+            kpml_generate_notify(kpml_data, FALSE,
+                                 KPML_SUB_EXPIRE,
+                                 KPML_SUB_EXPIRE_STR);
+
+            /* Tell call control to generate reorder */
+            lcb = lsm_get_lcb_by_call_id(kpml_data->call_id);
+            if (lcb && lcb->state < LSM_S_RINGOUT) {
+                cc_release(CC_SRC_GSM, kpml_data->call_id, kpml_data->line,
+                           CC_CAUSE_CONGESTION, NULL, NULL);
+            }
+        }
+
+        (void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
+    }
+
+    //Release kpml lock
+    cprReleaseMutex(kpml_mutex);
+
+    /*
+     * For ignore generating notify and release in the case
+     * of non normal terminate as determined above.
+     */
+    if (normal_terminate) {
+        (void) sub_int_subscribe_term(msg->sub_id, TRUE,
+                                      msg->request_id, msg->event);
+    }
+    KPML_DEBUG(DEB_F_PREFIX"exit.\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
+
+}
+
+/*
+ *  Function: kpml_receive_subscribe()
+ *
+ *  Parameters: ccsip_sub_not_data_t - msg passed from SIP stack
+ *
+ *  Description: Responsible for handling incoming SUBSCRIBE with KPML
+ *              Checks if the subscription expired or it is a re-subscribe
+ *              sents out intial notification, starts all the timers
+ *
+ *  Returns: None
+ */
+static void
+kpml_receive_subscribe (ccsip_sub_not_data_t *msg)
+{
+    static const char fname[] = "kpml_receive_subscribe";
+    kpml_data_t *kpml_data;
+    kpml_key_t kpml_key;
+    kpml_resp_code_e resp_code = KPML_SUCCESS;
+    KPMLRequest *kpml_sub_data = NULL;
+    lsm_states_t lsm_state;
+    char      *regx_prnt = NULL;
+    boolean is_empty_resubscribe = FALSE;
+
+    if (kpml_get_config_value() == KPML_NONE) {
+        KPML_DEBUG(DEB_L_C_F_PREFIX"KPML disabled in config.\n",
+                   DEB_L_C_F_PREFIX_ARGS(KPML_INFO, msg->line_id, msg->gsm_id, fname));
+        return;
+    }
+
+    if (msg->line_id == 0 || msg->gsm_id == 0) {
+
+        KPML_ERROR(KPML_L_C_F_PREFIX"Line or call_id not correct\n",
+                    msg->line_id, msg->gsm_id, fname);
+        (void) sub_int_subscribe_ack(CC_SRC_GSM, CC_SRC_SIP, msg->sub_id,
+                                     KPML_BAD_EVENT, msg->sub_duration);
+        return;
+    }
+
+    kpml_create_sm_key(&kpml_key, (line_t) msg->line_id, (callid_t) msg->gsm_id,
+                       NULL);
+
+    kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
+
+    if (msg->u.subs_ind_data.eventData) {
+
+        kpml_sub_data = &(msg->u.subs_ind_data.eventData->u.kpml_request);
+    }
+
+
+    /* There is active KPML subscription on this dialog
+     * so check if the existing subscription need to be
+     * terminated or refreshed
+     */
+    if (kpml_data) {
+        if (kpml_data->pending_sub == TRUE) {
+
+            kpml_data->sub_duration = msg->sub_duration;
+            /* generate SUBSCRIBE response */
+            kpml_generate_subscribe_response(kpml_data, KPML_SUCCESS);
+
+            /* KPML receives a new subscription for the same dialog.
+             * Terminate the current subscription if the new subscription
+             * is not related
+             */
+            if (kpml_data->sub_id != msg->sub_id) {
+
+                KPML_DEBUG(DEB_L_C_F_PREFIX"Terminate previous subscription \
+                           sub_id = %x\n",
+                                                  DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname),
+                                                  kpml_data->sub_id);
+
+                kpml_generate_notify(kpml_data, FALSE,
+                                     KPML_SUB_EXPIRE,
+                                     KPML_SUB_EXPIRE_STR);
+
+                (void) sub_int_subscribe_term(kpml_data->sub_id, TRUE,
+                                              msg->request_id, msg->event);
+            }
+
+            KPML_DEBUG(DEB_L_C_F_PREFIX"Refresh Subscription\n",
+                       DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
+            /* Refresh subscription without kpml body. This is CCM current behavior
+             * which seems not to follow rfc4730. We support this for compatibility
+             * purpose, and intepret the body to be exactly the same as previous
+             * subscription. Only new subscription time changes.
+             */
+            if (kpml_sub_data == NULL) {
+                 kpml_clear_timers(kpml_data);
+                 is_empty_resubscribe = TRUE;
+            } else if (kpml_clear_data(kpml_data, KPML_ONE_SHOT)) {
+                kpml_data = NULL;
+            }
+        } else {
+
+            /* Already has subscription data created to quaratining
+             * digits, but there is no active SIP subscription.
+             */
+
+            kpml_data = kpml_update_data(kpml_data, kpml_sub_data);
+
+            KPML_DEBUG(DEB_L_C_F_PREFIX"Activate Subscription\n",
+                       DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
+        }
+
+    }
+
+    if (kpml_sub_data) {
+        regx_prnt = kpml_sub_data->pattern.regex.regexData;
+    }
+
+    DEF_DEBUG(DEB_L_C_F_PREFIX"Regex=%s\n",
+            DEB_L_C_F_PREFIX_ARGS(KPML_INFO, msg->line_id, msg->gsm_id, fname), regx_prnt);
+
+    /* Above block can terminate existing subscription */
+    if (!kpml_data) {
+
+        /* New subscription - allocate data */
+        kpml_data = kpml_get_new_data();
+
+        if (kpml_data == NULL) {
+            KPML_ERROR(KPML_L_C_F_PREFIX"No memory for subscription data\n",
+                    msg->line_id, msg->gsm_id, fname);
+            return;
+        }
+
+        (void) kpml_update_data(kpml_data, kpml_sub_data);
+
+        (void) sll_append(s_kpml_list, kpml_data);
+
+    }
+
+    kpml_data->call_id = msg->gsm_id;
+
+    kpml_data->line = msg->line_id;
+
+    kpml_data->sub_id = msg->sub_id;
+
+    /* Terminate the subcription */
+    if (msg->sub_duration == 0) {
+
+        /* Update KPML document in the record */
+        kpml_data = kpml_update_data(kpml_data, kpml_sub_data);
+
+        KPML_DEBUG(DEB_L_C_F_PREFIX"Terminate Subscription.\n",
+                   DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
+
+        /* check the regular expression and backspace */
+        (void) kpml_treat_regex(kpml_data);
+
+        /* Remove backspace softkey */
+        ui_control_featurekey_bksp(msg->line_id, msg->gsm_id,
+                                   kpml_data->last_dig_bkspace);
+
+        /* Set call to proceeding state */
+        cc_proceeding(CC_SRC_SIP, kpml_data->call_id, kpml_data->line, NULL);
+
+        kpml_data->persistent = KPML_ONE_SHOT;
+
+        kpml_generate_notify(kpml_data, FALSE,
+                             KPML_SUB_EXPIRE,
+                             KPML_SUB_EXPIRE_STR);
+        /* Empty all the digits present in the list */
+        kpml_data->dig_head = kpml_data->dig_tail = 0;
+
+        (void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
+
+        /* free event data allocated by SIP stack */
+        if (msg->u.subs_ind_data.eventData) {
+            cpr_free(msg->u.subs_ind_data.eventData);
+        }
+
+        return;
+
+    }
+
+    kpml_data->pending_sub = TRUE;
+
+    (void) kpml_start_subscription_timer(kpml_data, msg->sub_duration);
+
+    kpml_start_timers(kpml_data);
+
+    /* check config to send appropriate response. Check subscription reject
+     * status.
+     */
+    if (((resp_code = check_kpml_config(msg->line_id, msg->gsm_id)) != KPML_SUCCESS) ||
+        ((resp_code = check_subcription_create_error(kpml_data)) != KPML_SUCCESS)) {
+
+        kpml_generate_subscribe_response(kpml_data, resp_code);
+
+        if (kpml_clear_data(kpml_data, KPML_ONE_SHOT)) {
+            kpml_data = NULL;
+        }
+
+        if (msg->u.subs_ind_data.eventData) {
+            cpr_free(msg->u.subs_ind_data.eventData);
+        }
+        return;
+
+    } else {
+        /*  Accept the subscription with a 200 OK response */
+        kpml_generate_subscribe_response(kpml_data, SIP_SUCCESS_SETUP);
+    }
+
+    /* Error checking for incoming KPML data. If there are attributes
+     * which are not supported or if the values are not in the range
+     * or KPML document is not present then generate error
+     */
+    if (!is_empty_resubscribe && ((kpml_sub_data == NULL) ||
+        ((resp_code = check_if_kpml_attributes_supported(kpml_sub_data)) != KPML_SUCCESS) ||
+        ((resp_code = check_attributes_range(kpml_sub_data)) != KPML_SUCCESS) ||
+        ((resp_code = kpml_treat_regex(kpml_data)) != KPML_SUCCESS) ||
+        ((resp_code = kpml_treat_enterkey(kpml_data,
+                                          kpml_sub_data->pattern.enterkey)) != KPML_SUCCESS))) {
+
+
+        KPML_ERROR(KPML_F_PREFIX"Error Resp code = %d\n", fname, resp_code);
+
+        kpml_generate_notify(kpml_data, FALSE, resp_code,
+                             KPML_ATTR_NOT_SUPPORTED_STR);
+
+        if (kpml_clear_data(kpml_data, KPML_ONE_SHOT)) {
+            kpml_data = NULL;
+        }
+
+        cpr_free(msg->u.subs_ind_data.eventData);
+
+        return;
+    } else {
+
+        lsm_state = lsm_get_state(kpml_data->call_id);
+
+        /* When GSM receives FEATURE event transitions its state. To avoid
+         * transition during DTMF phase do not send out the event if the
+         * lsm state is > RINGOUT state. GSM has to know about the subscription
+         * only to extend the collect info (KPML_COLLECT_INFO)
+         */
+
+        if ((lsm_state != LSM_S_NONE) && (lsm_state < LSM_S_RINGOUT)) {
+
+            cc_feature(CC_SRC_GSM, kpml_data->call_id, kpml_data->line,
+                       CC_FEATURE_SUBSCRIBE, NULL);
+        }
+
+        kpml_generate_notify(kpml_data, TRUE, KPML_SUCCESS, KPML_TRYING_STR);
+
+        kpml_update_quarantined_digits(kpml_data);
+    }
+
+    /* If the backspace key request is set then enable backspace key */
+    ui_control_featurekey_bksp(msg->line_id, msg->gsm_id,
+                               kpml_data->enable_backspace);
+
+    /* free event data allocated by SIP stack */
+    if (msg->u.subs_ind_data.eventData) {
+    cpr_free(msg->u.subs_ind_data.eventData);
+    }
+}
+
+/*
+ *  Function: kpml_generate_subscribe_response()
+ *
+ *  Parameters: ccsip_sub_not_data_t - msg data from SIP
+ *
+ *  Description: This routine is called to generate 200 Ok for incoming
+ *              KPML SUBSCRIBE.
+ *
+ *  Returns: None
+ */
+static void
+kpml_generate_subscribe_response (kpml_data_t * kpml_data, int resp_code)
+{
+    static const char fname[] = "kpml_generate_subscribe_response";
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"SUB response\n",
+                      DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname));
+
+    (void) sub_int_subscribe_ack(CC_SRC_GSM, CC_SRC_SIP, kpml_data->sub_id,
+                                 (uint16_t) resp_code, kpml_data->sub_duration);
+}
+
+/*
+ *  Function: kpml_receive_notify_response()
+ *
+ *  Parameters: ccsip_sub_not_data_t - msg data from SIP
+ *
+ *  Description: This routine is called when there is a NOTIFY
+ *              response from subscription manager.
+ *
+ *  Returns: None
+ */
+void
+kpml_receive_notify_response (ccsip_sub_not_data_t *msg)
+{
+    static const char fname[] = "kpml_receive_notify_response";
+    kpml_data_t *kpml_data;
+    kpml_key_t kpml_key;
+
+    KPML_DEBUG(DEB_L_C_F_PREFIX"Notify response\n",
+               DEB_L_C_F_PREFIX_ARGS(KPML_INFO, msg->line_id, msg->gsm_id, fname));
+
+    kpml_create_sm_key(&kpml_key, (line_t) msg->line_id, (callid_t) msg->gsm_id,
+                       NULL);
+
+    kpml_data = (kpml_data_t *) sll_find(s_kpml_list, &kpml_key);
+
+
+    /* Do not terminate subscription if the subscription is
+     *  persistent or result is not error
+     */
+
+    if (kpml_data) {
+        if (kpml_data->last_dig_bkspace &&
+            msg->u.notify_result_data.status_code == SIP_SUCCESS_SETUP) {
+            /* remove last digit which is backspace */
+            dp_delete_last_digit(msg->line_id, msg->gsm_id);
+            kpml_data->last_dig_bkspace = FALSE;
+        } else if (msg->u.notify_result_data.status_code == REQUEST_TIMEOUT) {
+
+            (void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
+            (void) sub_int_subscribe_term(msg->sub_id, TRUE, msg->request_id,
+                                          msg->event);
+            return;
+        }
+
+        /* See if there are digits collected before the subscription
+         * if so then update the kpml_digit buffer and match the pattern
+         */
+        kpml_update_quarantined_digits(kpml_data);
+    } else {
+        (void) sub_int_subscribe_term(msg->sub_id, TRUE, msg->request_id,
+                                      msg->event);
+    }
+}
+
+/*
+ *  Function: kpml_generate_notify()
+ *
+ *  Parameters: kpml_data - subscription data
+ *              resp_code - response code for the NOTIFY
+ *              resp_text - response text
+ *
+ *  Description: Notify KPML response data. This can be sucessful or error.
+ *
+ *  Returns: None
+ */
+static void
+kpml_generate_notify (kpml_data_t *kpml_data, boolean no_body,
+                      unsigned int resp_code, char *resp_text)
+{
+    static const char fname[] = "kpml_generate_notify";
+    char resp_str[10];
+    ccsip_event_data_t *peventData = NULL;
+
+    DEF_DEBUG(DEB_L_C_F_PREFIX"RESP %u: \n",
+        DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname), resp_code);
+
+    if (no_body == FALSE) {
+        // allocate memory to hold events and that will be freed in the stack
+        peventData = (ccsip_event_data_t *)
+            cpr_malloc(sizeof(ccsip_event_data_t));
+
+        if (peventData == NULL) {
+            KPML_ERROR(KPML_L_C_F_PREFIX"No memory for eventdata\n",
+                kpml_data->line, kpml_data->call_id, fname);
+            return;
+        }
+
+        memset(peventData, 0, sizeof(ccsip_event_data_t));
+
+        sstrncpy(peventData->u.kpml_response.version, KPML_VER_STR, sizeof(peventData->u.kpml_response.version));
+
+        snprintf(resp_str, 10, "%d", resp_code);
+        sstrncpy(peventData->u.kpml_response.code, resp_str, sizeof(peventData->u.kpml_response.code));
+
+        if (resp_code == KPML_SUCCESS) {
+
+            sstrncpy(&(peventData->u.kpml_response.digits[0]),
+                    &(kpml_data->kpmlDialed[0]), sizeof(peventData->u.kpml_response.digits));
+        }
+
+        if (kpml_data->flush == FALSE) {
+            sstrncpy(peventData->u.kpml_response.forced_flush, "false",
+                    sizeof(peventData->u.kpml_response.forced_flush));
+        } else {
+            sstrncpy(peventData->u.kpml_response.forced_flush, "true",
+                    sizeof(peventData->u.kpml_response.forced_flush));
+        }
+
+        sstrncpy(peventData->u.kpml_response.tag,
+                &(kpml_data->regex->tag[0]), sizeof(peventData->u.kpml_response.tag));
+
+        sstrncpy(peventData->u.kpml_response.text,
+                resp_text, sizeof(peventData->u.kpml_response.text));
+
+        peventData->type = EVENT_DATA_KPML_RESPONSE;
+        peventData->next = NULL;
+    }
+
+    (void) sub_int_notify(CC_SRC_GSM, CC_SRC_SIP, kpml_data->sub_id,
+                          /* kpml_receive_notify_response */ NULL,
+                          SUB_MSG_KPML_NOTIFY_ACK, peventData,
+                          (kpml_data->persistent ==
+                           KPML_ONE_SHOT ? SUBSCRIPTION_TERMINATE :
+                           SUBSCRIPTION_NULL));
+}
+
+/**
+ *
+ *  Function to return the message command name for KPML module
+ *
+ * @param uint32_t command
+ *
+ * @return  char * pointer to command name
+ *
+ * @pre     (none)
+ */
+
+char *
+kpml_get_msg_string (uint32_t cmd)
+{
+    switch (cmd) {
+
+    case SUB_MSG_KPML_SUBSCRIBE:
+        return("KPML_SUB");
+    case SUB_MSG_KPML_TERMINATE:
+        return("KPML_TERMINATE");
+    case SUB_MSG_KPML_NOTIFY_ACK:
+        return("KPML_NOT_ACK");
+    case SUB_MSG_KPML_SUBSCRIBE_TIMER:
+        return("KPML_SUB_TIMER");
+    case SUB_MSG_KPML_DIGIT_TIMER:
+        return("KPML_DIGIT_TIMER");
+    default:
+        return ("KPML_UNKNOWN_CMD");
+    }
+}
+
+void
+kpml_process_msg (uint32_t cmd, void *msg)
+{
+    static const char fname[] = "kpml_process_msg";
+
+    KPML_DEBUG(DEB_F_PREFIX"cmd= %s\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname), kpml_get_msg_string(cmd));
+
+    if (s_kpml_list == NULL) {
+        /* KPML is down do not process any message */
+        KPML_DEBUG(DEB_F_PREFIX"KPML is down.\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname));
+        return;
+    }
+
+    switch (cmd) {
+
+    case SUB_MSG_KPML_SUBSCRIBE:
+        kpml_receive_subscribe((ccsip_sub_not_data_t *) msg);
+        break;
+
+    case SUB_MSG_KPML_TERMINATE:
+        kpml_terminate_subscription((ccsip_sub_not_data_t *) msg);
+        break;
+
+    case SUB_MSG_KPML_NOTIFY_ACK:
+        kpml_receive_notify_response((ccsip_sub_not_data_t *) msg);
+        break;
+
+    case SUB_MSG_KPML_SUBSCRIBE_TIMER:
+        kpml_subscription_timer_event((void **) msg);
+        break;
+
+    case SUB_MSG_KPML_DIGIT_TIMER:
+        kpml_inter_digit_timer_event((void **) msg);
+        break;
+
+    default:
+        KPML_ERROR(KPML_F_PREFIX"Bad Cmd received: 0x%x.\n", fname, cmd);
+        break;
+    }
+}
+
+/*
+ *  Function: kpmlmap_show
+ *
+ *  Parameters:  standard args
+ *
+ *  Description:  Display the current dialplan (if any)
+ *
+ *  Returns:
+ *
+ */
+static void
+kpmlmap_show (void)
+{
+    static const char *fname="kpmlmap_show";
+    kpml_data_t *kpml_data;
+    int counter;
+
+    kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
+
+    while (kpml_data != NULL) {
+
+
+        KPML_DEBUG(DEB_L_C_F_PREFIX"Pending sub duration=%-8d",
+                   DEB_L_C_F_PREFIX_ARGS(KPML_INFO, kpml_data->line, kpml_data->call_id, fname),
+                   kpml_data->sub_duration);
+
+        for (counter = 0; counter < NUM_OF_REGX; counter++) {
+            KPML_DEBUG(DEB_F_PREFIX"%-4s  %-10s  %-5s\n", DEB_F_PREFIX_ARGS(KPML_INFO, fname),
+                       kpml_data->regex[counter].regexData,
+                       kpml_data->regex->tag, kpml_data->kpmlDialed);
+        }
+
+        kpml_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
+    }
+}
+
+
+/*
+ *  Function: show_kpmlmap_cmd
+ *
+ *  Parameters:  standard args
+ *
+ *  Description:  Display the current kpml subscription details (if any)
+ *
+ *  Returns:
+ *
+ */
+cc_int32_t
+show_kpmlmap_cmd (cc_int32_t argc, const char *argv[])
+{
+    kpml_data_t *kpml_data;
+    int counter;
+
+    debugif_printf("Pending KPML requests are....\n");
+
+    kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
+
+    debugif_printf("\n--------------- KPML SUBSCRIPTIONS-------------------");
+    debugif_printf("\nLine    Call_Id    Expire   Regx    Tag       Digits ");
+    debugif_printf
+        ("\n------------------------------------------------------\n");
+
+    while (kpml_data != NULL) {
+
+        debugif_printf("%-4d %-5d  %-8lu ",
+                       kpml_data->line, kpml_data->call_id,
+                       kpml_data->sub_duration);
+
+        for (counter = 0; counter < NUM_OF_REGX; counter++) {
+            debugif_printf("%-4s  %-10s  %-5s\n",
+                           kpml_data->regex[counter].regexData,
+                           kpml_data->regex->tag, kpml_data->kpmlDialed);
+        }
+
+        kpml_data = (kpml_data_t *) sll_next(s_kpml_list, kpml_data);
+    }
+    return (0);
+}
+
+
+/*
+ *  Function: kpml_init()
+ *
+ *  Parameters: none
+ *
+ *  Description: Register with Subscription manager for any imcoming
+ *              KPML subscribe messages
+ *
+ *  Returns: None
+ */
+void
+kpml_init (void)
+{
+    KPML_DEBUG(DEB_F_PREFIX"entered.\n", DEB_F_PREFIX_ARGS(KPML_INFO, "kpml_init"));
+
+    if (!kpml_mutex) {
+        kpml_mutex = cprCreateMutex("kpml lock");
+        if (!kpml_mutex) {
+            KPML_ERROR(DEB_F_PREFIX"unable to create kpml lock \n", "kpml_init");
+        }
+    }
+
+    (void) sub_int_subnot_register(CC_SRC_GSM, CC_SRC_SIP,
+                                   CC_SUBSCRIPTIONS_KPML,
+                                   /*kpml_receive_subscribe */ NULL, CC_SRC_GSM,
+                                   SUB_MSG_KPML_SUBSCRIBE,
+                                   NULL, SUB_MSG_KPML_TERMINATE, 0, 0);
+
+    /* allocate and initialize kpml list */
+    s_kpml_list = sll_create((sll_match_e(*)(void *, void *))
+                             kpml_match_line_call_id);
+
+}
+
+/*
+ *  Function: kpml_shutdown()
+ *
+ *  Parameters: none
+ *
+ *  Description: The function shutdowns KPML and cleans up data.
+ *
+ *  NOTE:
+ *
+ *  The assumption is existing subscriptions with SUB/NOT are
+ *  terminated and released elsewhere i.e. kpml shutdown does not
+ *  support shuting down alone while the rest of the SIP/GSM
+ *  components are up. Therefore the shutdown function will not
+ *  send any subscription termination to SUB/NOT which it will
+ *  be droped by SIP stack since it is also being shutdown.
+ *
+ *  Returns: None
+ */
+void
+kpml_shutdown (void)
+{
+    kpml_data_t *kpml_data;
+    KPML_DEBUG(DEB_F_PREFIX"entered.\n", DEB_F_PREFIX_ARGS(KPML_INFO, "kpml_shutdown"));
+
+    //acquire kpml lock
+    (void) cprGetMutex(kpml_mutex);
+
+
+    kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
+
+    while (kpml_data != NULL) {
+
+        /*
+         * Clean up, remove from the list and deallocate the kpml_data
+         */
+        (void) kpml_clear_data(kpml_data, KPML_ONE_SHOT);
+
+        /*
+         * The kpml_data is already freed above, get the next one
+         * from the list's head
+         */
+        kpml_data = (kpml_data_t *) sll_next(s_kpml_list, NULL);
+    }
+
+    sll_destroy(s_kpml_list);
+
+    s_kpml_list = NULL;
+    (void) cprReleaseMutex(kpml_mutex);
+
+    KPML_DEBUG(DEB_F_PREFIX"exit.\n", DEB_F_PREFIX_ARGS(KPML_INFO, "kpml_shutdown"));
+}
diff --git a/libs/sipcc/core/src-common/md5.c b/libs/sipcc/core/src-common/md5.c
new file mode 100644 (file)
index 0000000..71a3abf
--- /dev/null
@@ -0,0 +1,438 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * the md5.c source code that appears in RFC 1321 is badly indented,
+ * presumably as a result of the RFC editing process.  This version
+ * has been re-indented for clarity.  All other local modifications
+ * are deliniated with the CISCO_MD5_MODS symbol
+ */
+
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+   rights reserved.
+
+   License to copy and use this software is granted provided that it
+   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+   Algorithm" in all material mentioning or referencing this software
+   or this function.
+
+   License is also granted to make and use derivative works provided
+   that such works are identified as "derived from the RSA Data
+   Security, Inc. MD5 Message-Digest Algorithm" in all material
+   mentioning or referencing the derived work.
+
+   RSA Data Security, Inc. makes no representations concerning either
+   the merchantability of this software or the suitability of this
+   software for any particular purpose. It is provided "as is"
+   without express or implied warranty of any kind.
+
+   These notices must be retained in any copies of any part of this
+   documentation and/or software.
+ */
+
+/*
+ * RFC 1321 version #includes global.h, but md5.h has been locally modified
+ * to contain all the information that RFC 1321's global.h contains.
+ */
+
+#include "md5.h"
+
+#if defined(CISCO_MD5_MODS)
+
+#include "cpr_string.h"
+
+#endif /* defined(CISCO_MD5_MODS) */
+
+/* Constants for MD5Transform routine.
+ */
+
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(unsigned long int[4], unsigned char[64]);
+static void Encode(unsigned char *, unsigned long int *, unsigned int);
+static void Decode(unsigned long int *, unsigned char *, unsigned int);
+
+#if defined(CISCO_MD5_MODS)
+
+/* change the MD5_memcpy function into a bcopy call */
+//#define MD5_memcpy(out,in,len) bcopy(in, out, len)
+
+/*sam use standard memcpy */
+#define MD5_memcpy(out,in,len) memcpy(out, in, len)
+
+/* change the MD5_memset function into a memset call */
+#define MD5_memset(ptr,val,len) memset(ptr, val, len)
+
+#else /* defined(CISCO_MD5_MODS) */
+
+static void MD5_memcpy(unsigned char *, unsigned char *, unsigned int);
+static void MD5_memset(unsigned char *, int, unsigned int);
+
+#endif /* defined(CISCO_MD5_MODS) */
+
+static unsigned char PADDING[64] = {
+    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (unsigned long int )(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (unsigned long int )(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (unsigned long int )(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (unsigned long int )(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void
+MD5Init (MD5_CTX *context)
+{                               /* context */
+    context->count[0] = context->count[1] = 0;
+    /*
+     * Load magic initialization constants.
+     */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xefcdab89;
+    context->state[2] = 0x98badcfe;
+    context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+   operation, processing another message block, and updating the
+   context.
+   */
+void
+MD5Update (MD5_CTX *context,    /* context */
+           unsigned char *input, /* input block */
+           unsigned int inputLen)
+{                               /* length of input block */
+    unsigned int i, idx, partLen;
+
+    /* Compute number of bytes mod 64 */
+    idx = (unsigned int) ((context->count[0] >> 3) & 0x3F);
+
+    /* Update number of bits */
+    if ((context->count[0] += ((unsigned long int) inputLen << 3))
+        < ((unsigned long int) inputLen << 3))
+        context->count[1]++;
+    context->count[1] += ((unsigned long int) inputLen >> 29);
+
+    partLen = 64 - idx;
+
+    /*
+     * Transform as many times as possible.
+     */
+    if (inputLen >= partLen) {
+        MD5_memcpy
+            ((unsigned char *) &context->buffer[idx], (unsigned char *) input,
+             partLen);
+        MD5Transform(context->state, context->buffer);
+
+        for (i = partLen; i + 63 < inputLen; i += 64)
+            MD5Transform(context->state, &input[i]);
+
+        idx = 0;
+    } else
+        i = 0;
+
+    /* Buffer remaining input */
+    MD5_memcpy
+        ((unsigned char *) &context->buffer[idx], (unsigned char *) &input[i],
+         inputLen - i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+   the message digest and zeroizing the context.
+   */
+void
+MD5Final (unsigned char *digest, /* message digest */
+          MD5_CTX * context)
+{                               /* context */
+    unsigned char bits[8];
+    unsigned int idx, padLen;
+
+    /* Save number of bits */
+    Encode(bits, context->count, 8);
+
+    /*
+     * Pad out to 56 mod 64.
+     */
+    idx = (unsigned int) ((context->count[0] >> 3) & 0x3f);
+    padLen = (idx < 56) ? (56 - idx) : (120 - idx);
+    MD5Update(context, PADDING, padLen);
+
+    /* Append length (before padding) */
+    MD5Update(context, bits, 8);
+
+    /* Store state in digest */
+    Encode(digest, context->state, 16);
+
+    /*
+     * Zeroize sensitive information.
+     */
+    MD5_memset((unsigned char *) context, 0, sizeof(*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void
+MD5Transform (unsigned long int *state, unsigned char *block)
+{
+    unsigned long int a = state[0], b = state[1], c = state[2], d = state[3],
+        x[16];
+
+    Decode(x, block, 64);
+
+    /* Round 1 */
+    FF(a, b, c, d, x[0], S11, 0xd76aa478);  /* 1 */
+    FF(d, a, b, c, x[1], S12, 0xe8c7b756);  /* 2 */
+    FF(c, d, a, b, x[2], S13, 0x242070db);  /* 3 */
+    FF(b, c, d, a, x[3], S14, 0xc1bdceee);  /* 4 */
+    FF(a, b, c, d, x[4], S11, 0xf57c0faf);  /* 5 */
+    FF(d, a, b, c, x[5], S12, 0x4787c62a);  /* 6 */
+    FF(c, d, a, b, x[6], S13, 0xa8304613);  /* 7 */
+    FF(b, c, d, a, x[7], S14, 0xfd469501);  /* 8 */
+    FF(a, b, c, d, x[8], S11, 0x698098d8);  /* 9 */
+    FF(d, a, b, c, x[9], S12, 0x8b44f7af);  /* 10 */
+    FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+    FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+    FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+    FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+    FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+    FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+    /* Round 2 */
+    GG(a, b, c, d, x[1], S21, 0xf61e2562);  /* 17 */
+    GG(d, a, b, c, x[6], S22, 0xc040b340);  /* 18 */
+    GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+    GG(b, c, d, a, x[0], S24, 0xe9b6c7aa);  /* 20 */
+    GG(a, b, c, d, x[5], S21, 0xd62f105d);  /* 21 */
+    GG(d, a, b, c, x[10], S22, 0x2441453);  /* 22 */
+    GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+    GG(b, c, d, a, x[4], S24, 0xe7d3fbc8);  /* 24 */
+    GG(a, b, c, d, x[9], S21, 0x21e1cde6);  /* 25 */
+    GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+    GG(c, d, a, b, x[3], S23, 0xf4d50d87);  /* 27 */
+    GG(b, c, d, a, x[8], S24, 0x455a14ed);  /* 28 */
+    GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+    GG(d, a, b, c, x[2], S22, 0xfcefa3f8);  /* 30 */
+    GG(c, d, a, b, x[7], S23, 0x676f02d9);  /* 31 */
+    GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+    /* Round 3 */
+    HH(a, b, c, d, x[5], S31, 0xfffa3942);  /* 33 */
+    HH(d, a, b, c, x[8], S32, 0x8771f681);  /* 34 */
+    HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+    HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+    HH(a, b, c, d, x[1], S31, 0xa4beea44);  /* 37 */
+    HH(d, a, b, c, x[4], S32, 0x4bdecfa9);  /* 38 */
+    HH(c, d, a, b, x[7], S33, 0xf6bb4b60);  /* 39 */
+    HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+    HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+    HH(d, a, b, c, x[0], S32, 0xeaa127fa);  /* 42 */
+    HH(c, d, a, b, x[3], S33, 0xd4ef3085);  /* 43 */
+    HH(b, c, d, a, x[6], S34, 0x4881d05);   /* 44 */
+    HH(a, b, c, d, x[9], S31, 0xd9d4d039);  /* 45 */
+    HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+    HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+    HH(b, c, d, a, x[2], S34, 0xc4ac5665);  /* 48 */
+
+    /* Round 4 */
+    II(a, b, c, d, x[0], S41, 0xf4292244);  /* 49 */
+    II(d, a, b, c, x[7], S42, 0x432aff97);  /* 50 */
+    II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+    II(b, c, d, a, x[5], S44, 0xfc93a039);  /* 52 */
+    II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+    II(d, a, b, c, x[3], S42, 0x8f0ccc92);  /* 54 */
+    II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+    II(b, c, d, a, x[1], S44, 0x85845dd1);  /* 56 */
+    II(a, b, c, d, x[8], S41, 0x6fa87e4f);  /* 57 */
+    II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+    II(c, d, a, b, x[6], S43, 0xa3014314);  /* 59 */
+    II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+    II(a, b, c, d, x[4], S41, 0xf7537e82);  /* 61 */
+    II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+    II(c, d, a, b, x[2], S43, 0x2ad7d2bb);  /* 63 */
+    II(b, c, d, a, x[9], S44, 0xeb86d391);  /* 64 */
+
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+
+    /*
+     * Zeroize sensitive information.
+     */
+    MD5_memset((unsigned char *) x, 0, sizeof(x));
+}
+
+/* Encodes input (unsigned long int ) into output (unsigned char). Assumes len is
+   a multiple of 4.
+   */
+static void
+Encode (unsigned char *output,
+        unsigned long int *input,
+        unsigned int len)
+{
+    unsigned int i, j;
+
+    for (i = 0, j = 0; j < len; i++, j += 4) {
+        output[j] = (unsigned char) (input[i] & 0xff);
+        output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff);
+        output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff);
+        output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff);
+    }
+}
+
+/* Decodes input (unsigned char) into output (unsigned long int ). Assumes len is
+   a multiple of 4.
+   */
+static void
+Decode (unsigned long int *output,
+        unsigned char *input,
+        unsigned int len)
+{
+    unsigned int i, j;
+
+    for (i = 0, j = 0; j < len; i++, j += 4)
+        output[i] = ((unsigned long int) input[j]) |
+                    (((unsigned long int) input[j + 1]) << 8) |
+                    (((unsigned long int) input[j + 2]) << 16) |
+                    (((unsigned long int) input[j + 3]) << 24);
+}
+
+#if !defined(CISCO_MD5_MODS)
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+static void
+MD5_memcpy (unsigned char *output,
+            unsigned char *input,
+            unsigned int len)
+{
+    unsigned int i;
+
+    for (i = 0; i < len; i++)
+        output[i] = input[i];
+}
+
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void
+MD5_memset (unsigned char *output,
+            int value,
+            unsigned int len)
+{
+    unsigned int i;
+
+    for (i = 0; i < len; i++)
+        ((char *) output)[i] = (char) value;
+}
+
+#endif /* !defined(CISCO_MD5_MODS) */
+
+#if defined(MD5TESTSUITE)
+#define MD 5
+#define MD_CTX MD5_CTX
+#define MDInit MD5Init
+#define MDUpdate MD5Update
+#define MDFinal MD5Final
+
+/* Prints a message digest in hexadecimal.
+ */
+void
+MDPrint (char *digest)
+{
+    unsigned int i;
+
+    for (i = 0; i < 16; i++)
+        printf("%02x", digest[i]);
+}
+
+/* Digests a string and prints the result.
+ */
+void
+MDString (char *string)
+{
+    MD_CTX context;
+    unsigned char digest[16];
+    unsigned int len = strlen(string);
+
+    MDInit(&context);
+    MDUpdate(&context, (unsigned char *)string, len);
+    MDFinal(digest, &context);
+
+    printf("MD%d (\"%s\") = ", MD, string);
+    MDPrint((char *)digest);
+    printf("\n");
+}
+
+
+/* Digests a reference suite of strings and prints the results.
+ */
+void
+MDTestSuite ()
+{
+    printf("MD%d test suite:\n", MD);
+
+    MDString("");
+    MDString("a");
+    MDString("abc");
+    MDString("message digest");
+    MDString("abcdefghijklmnopqrstuvwxyz");
+    MDString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+    MDString("1234567890123456789012345678901234567890\
+1234567890123456789012345678901234567890");
+}
+
+#endif
diff --git a/libs/sipcc/core/src-common/misc_apps_task.c b/libs/sipcc/core/src-common/misc_apps_task.c
new file mode 100755 (executable)
index 0000000..9a69fb8
--- /dev/null
@@ -0,0 +1,150 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_memory.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_ipc.h"
+#include "cpr_errno.h"
+#include "phone.h"
+#include "phntask.h"
+#include "phone_debug.h"
+#include "debug.h"
+#include "subapi.h"
+#include "misc_apps_task.h"
+#include "pres_sub_not_handler.h"
+#include "configapp.h"
+#include "platform_api.h"
+
+#define MISC_ERROR err_msg
+
+cprMsgQueue_t s_misc_msg_queue;
+void destroy_misc_app_thread(void);
+extern cprThread_t misc_app_thread;
+
+cpr_status_e
+MiscAppTaskSendMsg (uint32_t cmd, cprBuffer_t buf, uint16_t len)
+{
+    phn_syshdr_t *syshdr_p;
+
+    syshdr_p = (phn_syshdr_t *) cprGetSysHeader(buf);
+    if (!syshdr_p)
+    {
+        return CPR_FAILURE;
+    }
+    syshdr_p->Cmd = cmd;
+    syshdr_p->Len = len;
+
+    if (cprSendMessage(s_misc_msg_queue, buf, (void **)&syshdr_p) == CPR_FAILURE)
+    {
+        cprReleaseSysHeader(syshdr_p);
+        return CPR_FAILURE;
+    }
+    return CPR_SUCCESS;
+}
+
+void MiscAppTask (void *arg)
+{
+    static const char fname[] = "MiscAppTask";
+    void *msg_p;
+    phn_syshdr_t *syshdr_p;
+
+    /*
+     * Get the misc apps message queue handle
+     */
+    s_misc_msg_queue  = (cprMsgQueue_t)arg;
+    if (!s_misc_msg_queue) {
+        MISC_ERROR(MISC_F_PREFIX"invalid input, exiting\n", fname);
+        return;
+    }
+
+    if (platThreadInit("MiscAppTask") != 0) {
+        MISC_ERROR(MISC_F_PREFIX"Failed to Initialize the thread, exiting\n",
+                fname);
+        return;
+    }
+
+    /*
+     * Create retry-after timers.
+     */
+    if (pres_create_retry_after_timers() == CPR_FAILURE) {
+        MISC_ERROR(MISC_F_PREFIX"failed to create retry-after Timers, exiting\n",
+               fname);
+        return;
+    }
+
+    while (1)
+    {
+        msg_p = cprGetMessage(s_misc_msg_queue, TRUE, (void **)&syshdr_p);
+        if (msg_p)
+        {
+            switch(syshdr_p->Cmd) {
+            case SUB_MSG_PRESENCE_SUBSCRIBE_RESP:
+            case SUB_MSG_PRESENCE_NOTIFY:
+            case SUB_MSG_PRESENCE_UNSOLICITED_NOTIFY:
+            case SUB_MSG_PRESENCE_TERMINATE:
+            case SUB_MSG_PRESENCE_GET_STATE:
+            case SUB_MSG_PRESENCE_TERM_REQ:
+            case SUB_MSG_PRESENCE_TERM_REQ_ALL:
+            case TIMER_EXPIRATION:
+            case SUB_HANDLER_INITIALIZED:
+                pres_process_msg_from_msgq(syshdr_p->Cmd, msg_p);
+                break;
+
+            case SUB_MSG_CONFIGAPP_SUBSCRIBE:
+            case SUB_MSG_CONFIGAPP_TERMINATE:
+            case SUB_MSG_CONFIGAPP_NOTIFY_ACK:
+                configapp_process_msg(syshdr_p->Cmd, msg_p);
+                break;
+
+            case THREAD_UNLOAD:
+                destroy_misc_app_thread();
+                break;
+
+            default:
+                MISC_ERROR(MISC_F_PREFIX"invalid msg <%d> received\n",
+                        fname, syshdr_p->Cmd);
+                break;
+            }
+
+            cprReleaseSysHeader(syshdr_p);
+            cpr_free(msg_p);
+        }
+    }
+}
+
+/**
+ *
+ * Perform cleaning up resoruces of misc. application task.
+ *
+ * @param none
+ *
+ * @return none
+ *
+ * @pre     none
+ */
+void MiscAppTaskShutdown (void)
+{
+    /* Destroy retry after timers */
+    pres_destroy_retry_after_timers();
+}
+
+/*
+ *  Function: destroy_misc_thread
+ *  Description:  shutdown and kill misc app thread
+ *  Parameters:   none
+ *  Returns: none
+ */
+void destroy_misc_app_thread()
+{
+    static const char fname[] = "destroy_misc_app_thread";
+    DEF_DEBUG(DEB_F_PREFIX"Unloading Misc app and destroying Misc app thread\n",
+        DEB_F_PREFIX_ARGS(SIP_CC_INIT, fname));
+    configapp_shutdown();
+    MiscAppTaskShutdown();
+    (void)cprDestroyThread(misc_app_thread);
+}
+
+/************************************* THE END ******************************************/
diff --git a/libs/sipcc/core/src-common/pres_sub_not_handler.c b/libs/sipcc/core/src-common/pres_sub_not_handler.c
new file mode 100755 (executable)
index 0000000..1c2db74
--- /dev/null
@@ -0,0 +1,1392 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_strings.h"
+#include "pres_sub_not_handler.h"
+#include "singly_link_list.h"
+#include "ccsip_subsmanager.h"
+#include "ccsip_messaging.h"
+#include "ccapi.h"
+#include "tnp_blf.h"
+#include "debug.h"
+#include "phone_debug.h"
+#include "phntask.h"
+#include "subapi.h"
+#include "phone_platform_constants.h"
+#include "misc_apps_task.h"
+#include "cc_blf_listener.h"
+#include "uiapi.h"
+
+cc_int32_t g_blfDebug = 0;
+
+#define DEFAULT_RETRY_AFTER_MILLISECS 5000
+typedef enum {
+    PRES_RETRYAFTER_TIMER = 1
+} pres_timers_e;
+
+#define BLF_PICKUP_FEATURE   0x1
+typedef struct {
+    int      request_id;        /* to match responses with requests */
+    sub_id_t sub_id;            /* provided by the subscription manager */
+    int      duration;          /* subscription duration */
+    char     presentity[CC_MAX_DIALSTRING_LEN];
+    /* the entity whose presence status is requested */
+    char     watcher[CC_MAX_DIALSTRING_LEN];
+    /* the entity who is requesting */
+    int      app_id;            /*indicates either calllists or line number */
+    uint32_t highest_cseq;      /* the last highest Cseq of NOTIFYs */
+    int      feature_mask;
+    int      blf_state; // cache the BLF state.
+} pres_subscription_req_t;
+
+typedef struct {
+    char                presentity[CC_MAX_DIALSTRING_LEN];
+    ccsip_event_data_t *event_data_p;
+} pres_pending_notify_t;
+
+static void subscribe_response_ind(ccsip_sub_not_data_t *msg_data);
+static void notify_ind_cb(ccsip_sub_not_data_t *msg_data);
+static void terminate_cb(ccsip_sub_not_data_t *msg_data);
+static int extract_blf_state(Presence_ext_t *event_body_p, int feature_mask);
+static void free_sub_request(pres_subscription_req_t *sup_req_p);
+static void process_timer_expiration(void *msg_p);
+static boolean apply_presence_state_to_matching_feature_keys(char *presentity,
+                                                             Presence_ext_t *event_body_p);
+static void append_notification_to_pending_queue(ccsip_event_data_t *event_body_p);
+static void sub_handler_initialized(void);
+
+static sll_handle_t s_pres_req_list = NULL; /* subscriptions list */
+static sll_handle_t s_pending_notify_list = NULL;
+static boolean s_subs_hndlr_initialized = FALSE;
+
+/*
+ * retry_after_timers used  to implement the functionality of retry-after attribute
+ * in Subscription-State header. one for each line button. Though the first button must
+ * always be a DN line, we create timers equivalant to number of buttons for simplifying
+ * coding logic.
+ */
+static cprTimer_t s_retry_after_timers[MAX_REG_LINES];
+
+/*
+ * Function: send_subscribe_ev_to_sip()
+ *
+ * Parameters: sup_req_p - pointer to pres_subscription_req_t (input parameter)
+ *
+ * Description: posts SIPSPI_EV_CC_SUBSCRIBE to SIP task message queue.
+ *
+ * Returns: CC_RC_ERROR - failed to post msg.
+ *          CC_RC_SUCCESS - successful posted msg.
+ */
+cc_rcs_t
+send_subscribe_ev_to_sip_task (pres_subscription_req_t *sub_req_p)
+{
+    sipspi_msg_t subscribe_msg;
+
+    /*
+     * post SIPSPI_EV_CC_SUBSCRIBE to SIP stack
+     */
+    memset(&subscribe_msg, 0, sizeof(sipspi_msg_t));
+    subscribe_msg.msg.subscribe.eventPackage = CC_SUBSCRIPTIONS_PRESENCE;
+    subscribe_msg.msg.subscribe.sub_id = sub_req_p->sub_id;
+    subscribe_msg.msg.subscribe.auto_resubscribe = TRUE;
+    subscribe_msg.msg.subscribe.request_id = sub_req_p->request_id;
+    subscribe_msg.msg.subscribe.duration = sub_req_p->duration;
+    sstrncpy(subscribe_msg.msg.subscribe.subscribe_uri, sub_req_p->presentity,
+             CC_MAX_DIALSTRING_LEN);
+    sstrncpy(subscribe_msg.msg.subscribe.subscriber_uri, sub_req_p->watcher,
+             CC_MAX_DIALSTRING_LEN);
+    subscribe_msg.msg.subscribe.dn_line =
+        get_dn_line_from_dn(sub_req_p->watcher);
+    subscribe_msg.msg.subscribe.subsNotCallbackTask = CC_SRC_MISC_APP;
+    subscribe_msg.msg.subscribe.subsResCallbackMsgID =
+        SUB_MSG_PRESENCE_SUBSCRIBE_RESP;
+    subscribe_msg.msg.subscribe.subsNotIndCallbackMsgID =
+        SUB_MSG_PRESENCE_NOTIFY;
+    subscribe_msg.msg.subscribe.subsTermCallbackMsgID =
+        SUB_MSG_PRESENCE_TERMINATE;
+    return (sub_int_subscribe(&subscribe_msg));
+}
+
+/*
+ * Function: find_matching_node()
+ *
+ * Parameters: key - key to find the matching node.
+ *             data - node data.
+ *
+ * Descriotion: is invoked by sll_find() to find the matching node based on the key.
+ *
+ * Returns: either SLL_MATCH_FOUND or SLL_MATCH_NOT_FOUND.
+ */
+static sll_match_e
+find_matching_node (void *key, void *data)
+{
+    int request_id = *((int *)key);
+    pres_subscription_req_t *req_p = (pres_subscription_req_t *) data;
+
+    if (request_id == req_p->request_id) {
+        return SLL_MATCH_FOUND;
+    }
+
+    return SLL_MATCH_NOT_FOUND;
+}
+
+typedef struct {
+    int request_id;
+    int duration;
+    char watcher[CC_MAX_DIALSTRING_LEN];
+    char presentity[CC_MAX_DIALSTRING_LEN];
+    int app_id;
+    int feature_mask;
+} pres_req_msg;
+
+/*
+ *  Function: pres_get_state()
+ *
+ *  Parameters: request_id - unique id assigned by the platform to this subscription. Platform
+ *                           uses this to track the status updates and to make subsequent termination
+ *                           request.
+ *              duration - how long the subscription is requested to be valid.
+ *              watcher - entity that is requesting the presence state.
+ *              presentity - entity whose presence state is requested.
+ *              app_id - application that is making the subscription.
+ *                       0: indicates call list blf application.
+ *                       1..n: indicates the speeddial/blf associated with (1..n)th line button.
+ *              feature_mask - indicates the additional features enabled.
+ *
+ *  Description:  is invoked by platform side whenever it needs to susbcribe
+ *                for presence state of a presentity. This posts  SUB_MSG_PRESENCE_GET_STATE
+ *                event to misc app task.
+ *
+ *  Returns: void
+ */
+void
+pres_get_state (int request_id,
+                int duration,
+                const char *watcher,
+                const char *presentity,
+                int app_id,
+                int feature_mask)
+{
+    pres_req_msg msg;
+
+    msg.request_id = request_id;
+    msg.duration = duration;
+    sstrncpy(msg.presentity, presentity, CC_MAX_DIALSTRING_LEN);
+    sstrncpy(msg.watcher, watcher, CC_MAX_DIALSTRING_LEN);
+    msg.app_id = app_id;
+    msg.feature_mask = feature_mask;
+
+    (void) app_send_message(&msg, sizeof(pres_req_msg), CC_SRC_MISC_APP,
+                            SUB_MSG_PRESENCE_GET_STATE);
+}
+
+/*
+ *  Function: get_state()
+ *
+ *  Parameters: request_id - unique id assigned by the platform to this subscription. Platform
+ *                           uses this to track the status updates and to make subsequent termination
+ *                           request.
+ *              duration - how long the subscription is requested to be valid.
+ *              watcher - entity that is requesting the presence state.
+ *              presentity - entity whose presence state is requested.
+ *              app_id - application that is making the subscription.
+ *                       0: indicates call list blf application.
+ *                       1..n: indicates the speeddial/blf associated with (1..n)th line button.
+ *              feature_mask - indicates the additional features enabled.
+ *
+ *  Description:  is invoked by platform side whenever it needs to susbcribe
+ *                for presence state of a presentity. This stores the susbcription
+ *                data in a linked list and posts SIPSPI_EV_CC_SUBSCRIBE
+ *                to SIP stack. We need to store the subscription data so that
+ *                SUBSCRIBE response and NOTIFYs can be mapped to subscriptions.
+ *
+ *  Returns: void
+ */
+static void
+get_state (int request_id,
+           int duration,
+           const char *watcher,
+           const char *presentity,
+           int app_id,
+           int feature_mask)
+{
+    static const char fname[] = "get_state";
+    pres_subscription_req_t *sub_req_p;
+
+    DEF_DEBUG(DEB_F_PREFIX"REQ %d: TM %d: WTR %s: PRT %s: FMSK %d: APP %d\n",
+         DEB_F_PREFIX_ARGS(BLF_INFO, fname),
+         request_id, duration, watcher, presentity, feature_mask, app_id);
+    /*
+     * if there is no subscription list yet, create one.
+     */
+    if (s_pres_req_list == NULL) {
+        s_pres_req_list = sll_create(find_matching_node);
+        if (s_pres_req_list == NULL) {
+            /* let platform know that we can not continue */
+            ui_BLF_notification(request_id, CC_SIP_BLF_REJECTED, app_id);
+            BLF_ERROR(MISC_F_PREFIX"Exiting : request list creation failed\n", fname);
+            return;
+        }
+    }
+    /*
+     * check if a request is already created by walking through the list. if not, create one.
+     */
+    if ((sub_req_p = (pres_subscription_req_t *)
+                sll_find(s_pres_req_list, &request_id)) == NULL) {
+        /*
+         * populate subscription request and append it to the list.
+         */
+        sub_req_p = (pres_subscription_req_t *)
+            cpr_malloc(sizeof(pres_subscription_req_t));
+        if (sub_req_p == NULL) {
+            BLF_ERROR(MISC_F_PREFIX"Exiting : malloc failed\n", fname);
+            return;
+        }
+
+        sub_req_p->request_id = request_id;
+        sub_req_p->sub_id = CCSIP_SUBS_INVALID_SUB_ID;
+        sub_req_p->highest_cseq = 0;
+        sub_req_p->duration = duration;
+        sstrncpy(sub_req_p->presentity, presentity, CC_MAX_DIALSTRING_LEN);
+        sstrncpy(sub_req_p->watcher, watcher, CC_MAX_DIALSTRING_LEN);
+        sub_req_p->app_id = app_id;
+        sub_req_p->feature_mask = feature_mask;
+        sub_req_p->blf_state = CC_SIP_BLF_UNKNOWN;
+
+        (void) sll_append(s_pres_req_list, sub_req_p);
+    } else { /* already exists. just update the duration */
+        sub_req_p->duration = duration;
+    }
+
+    /*
+     * post SIPSPI_EV_CC_SUBSCRIBE to SIP stack
+     */
+    if (send_subscribe_ev_to_sip_task(sub_req_p) != CC_RC_SUCCESS) {
+        /*
+         * remove the node from the list of subscriptions.
+         */
+        free_sub_request(sub_req_p);
+        /* let platform know that we can not continue */
+        ui_BLF_notification(request_id, CC_SIP_BLF_REJECTED, app_id);
+        BLF_ERROR(MISC_F_PREFIX"Exiting : Unable to send SUBSCRIBE\n", fname);
+        return;
+    }
+
+    BLF_DEBUG(DEB_F_PREFIX"Exiting : request made successfully\n", DEB_F_PREFIX_ARGS(BLF, fname));
+    return;
+}
+
+/*
+ *  Function: pres_terminate_req()
+ *
+ *  Parameters: request_id - unique id of the subscription which needs to be terminated.
+ *
+ *  Description:  is invoked by platform to terminate a subscription.
+ *                it posts SUB_MSG_PRESENCE_TERM_REQ to misc app task.
+ *
+ *  Returns: void
+ */
+void
+pres_terminate_req (int request_id)
+{
+    (void) app_send_message(&request_id, sizeof(request_id), CC_SRC_MISC_APP,
+                            SUB_MSG_PRESENCE_TERM_REQ);
+}
+
+/*
+ *  Function: terminate_req()
+ *
+ *  Parameters: request_id - unique id of the subscription which needs to be terminated.
+ *
+ *  Description:  is invoked by platform to terminate a subscription.
+ *                First, it posts SIPSPI_EV_CC_SUBSCRIBE to SIP stack with duration = 0.
+ *                and then, it posts SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED to SIP stack.
+ *
+ *  Returns: void
+ */
+static void
+terminate_req (int request_id)
+{
+    static const char fname[] = "terminate_req";
+    pres_subscription_req_t *sub_req_p;
+
+    BLF_DEBUG(DEB_F_PREFIX"Entering (request_id=%d)\n",
+              DEB_F_PREFIX_ARGS(BLF, fname), request_id);
+
+    /*
+     * check if the request exists.
+     */
+    if ((sub_req_p = (pres_subscription_req_t *)
+                sll_find(s_pres_req_list, &request_id)) == NULL) {
+        BLF_ERROR(MISC_F_PREFIX"request does not exist in the list\n", fname);
+        return;
+    }
+
+    /*
+     * post SIPSPI_EV_CC_SUBSCRIBE to SIP stack with duration = 0
+     */
+    sub_req_p->duration = 0;
+    /*
+     * no point in checking return value of the subsmanager_handle_ev_app_subscribe()
+     * because we are terminating the request anyway.
+     */
+    (void) send_subscribe_ev_to_sip_task(sub_req_p);
+
+    /*
+     * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED.
+     * do not force SUB/NOT mgr to cleanup SCB immediately, because we may have to handle digest
+     * challenges to terminating SUBSCRIBE sent.
+     */
+    (void) sub_int_subscribe_term(sub_req_p->sub_id, FALSE,
+                                  sub_req_p->request_id,
+                                  CC_SUBSCRIPTIONS_PRESENCE);
+
+    /*
+     * and remove the node from the list of subscriptions.
+     */
+    free_sub_request(sub_req_p);
+
+    BLF_DEBUG(DEB_F_PREFIX"Exiting : request terminated\n", DEB_F_PREFIX_ARGS(BLF, fname));
+    return;
+}
+
+/*
+ *  Function: pres_terminate_req_all()
+ *
+ *  Parameters: none.
+ *
+ *  Description:  is invoked by platform to terminate all subscriptions.
+ *                it posts SUB_MSG_PRESENCE_TERM_REQ_ALL to misc app task.
+ *
+ *  Returns: void
+ */
+void
+pres_terminate_req_all (void)
+{
+    char dummy;
+
+    (void) app_send_message(&dummy, sizeof(dummy), CC_SRC_MISC_APP,
+                            SUB_MSG_PRESENCE_TERM_REQ_ALL);
+}
+
+/**
+ * This function will post an event - SUB_HANDLER_INITIALIZED - to MISC task
+ *
+ * @param[in] none
+ *
+ * @return none
+ */
+void
+pres_sub_handler_initialized (void)
+{
+    char dummy;
+
+    (void) app_send_message(&dummy, sizeof(dummy), CC_SRC_MISC_APP,
+                            SUB_HANDLER_INITIALIZED);
+}
+
+
+/*
+ *  Function: terminate_req_all()
+ *
+ *  Parameters: none.
+ *
+ *  Description:  terminates all out standing subscriptions
+ *
+ *  Returns: void
+ */
+static void
+terminate_req_all (void)
+{
+    static const char fname[] = "terminate_req_all";
+    pres_subscription_req_t *sub_req_p;
+
+    BLF_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(BLF, fname));
+    if (s_pres_req_list == NULL) {
+        BLF_DEBUG(DEB_F_PREFIX"Exiting : no outstanding requests\n", DEB_F_PREFIX_ARGS(BLF, fname));
+        return;
+    }
+
+    while ((sub_req_p = (pres_subscription_req_t *)
+                sll_next(s_pres_req_list, NULL)) != NULL) {
+        /*
+         * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED so that SIP stack cleans up.
+         */
+        (void) sub_int_subscribe_term(sub_req_p->sub_id, TRUE,
+                                      sub_req_p->request_id,
+                                      CC_SUBSCRIPTIONS_PRESENCE);
+
+        /*
+         * and remove the node from the list of subscriptions.
+         */
+        free_sub_request(sub_req_p);
+    }
+    /*
+     * this function call indicates the subscription handler is going
+     * out of service, set s_subs_hndlr_initialized to FALSE.
+     */
+    s_subs_hndlr_initialized = FALSE;
+    BLF_DEBUG(DEB_F_PREFIX"Exiting\n", DEB_F_PREFIX_ARGS(BLF, fname));
+}
+
+/*
+ *  Function: subscribe_response_ind()
+ *
+ *  Parameters: msg_data - the response data provoded by SIP stack.
+ *
+ *  Description: is invoked by SIP stack when it receives a response message for
+ *               the SUBSCRIBE it sent out. For most of non-2xx final responses,
+ *               it posts SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED to SIP stack and
+ *               lets the platform know that the subscription is rejected.
+ *               if the resp is 423 (interval too short), then resends the subscription
+ *               with double the previous duaration.
+ *
+ *  Returns: void
+ */
+static void
+subscribe_response_ind (ccsip_sub_not_data_t *msg_data)
+{
+    static const char fname[] = "subscribe_response_ind";
+    int status_code = msg_data->u.subs_result_data.status_code;
+    int request_id = msg_data->request_id;
+    sub_id_t sub_id = msg_data->sub_id;
+    pres_subscription_req_t *sub_req_p;
+
+    BLF_DEBUG(DEB_F_PREFIX"Entering (status_code=%d)\n",
+              DEB_F_PREFIX_ARGS(BLF, fname), status_code);
+
+    if ((s_pres_req_list == NULL) ||
+        ((sub_req_p = (pres_subscription_req_t *)
+          sll_find(s_pres_req_list, &request_id)) == NULL)) {
+        /*
+         * since we do not have subscription for this, help SIP stack clean up.
+         * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED so that SIP stack cleans up.
+         */
+        (void) sub_int_subscribe_term(sub_id, TRUE, request_id,
+                                      CC_SUBSCRIPTIONS_PRESENCE);
+        BLF_DEBUG(DEB_F_PREFIX"Exiting : subscription does not exist\n", DEB_F_PREFIX_ARGS(BLF, fname));
+        return;
+    }
+
+    /*
+     * note sub_id so that sub_id will be used in the subsequent interaction with SIP stack
+     */
+    sub_req_p->sub_id = sub_id;
+
+    /*
+     * If the status_code is 1xx or 2xx, then do nothing.
+     */
+    if ((status_code >= 100) && (status_code < 300)) {
+        /* do nothing */
+        BLF_DEBUG(DEB_F_PREFIX"Exiting :100-299 response\n", DEB_F_PREFIX_ARGS(BLF, fname));
+        return;
+    }
+
+    /*
+     * If the status_code is 423 (interval too short), resend with the new duration.
+     */
+    if (status_code == SIP_CLI_ERR_INTERVAL_TOO_SMALL) {
+        sub_req_p->duration = msg_data->u.subs_result_data.expires;
+        if (send_subscribe_ev_to_sip_task(sub_req_p) != CC_RC_SUCCESS) {
+            /* let platform know that we can not continue */
+            ui_BLF_notification(request_id, CC_SIP_BLF_REJECTED, sub_req_p->app_id);
+            /*
+             * remove the node from the list of subscriptions.
+             */
+            free_sub_request(sub_req_p);
+            BLF_ERROR(MISC_F_PREFIX"Exiting : Unable to send SUBSCRIBE\n", fname);
+            return;
+        }
+        BLF_DEBUG(DEB_F_PREFIX"Exiting : subscribed again with double duration\n", DEB_F_PREFIX_ARGS(BLF, fname));
+        return;
+    }
+
+    /*
+     * if the status_code is 481 (sub does not exist) and the app_id is non-zero (sppeddial/blf),
+     * terminate the existing one and send a new subscription.
+     */
+    if ((status_code == SIP_CLI_ERR_CALLEG) && (sub_req_p->app_id > 0)) {
+        ui_BLF_notification(request_id, CC_SIP_BLF_UNKNOWN, sub_req_p->app_id); /* until we get the current status */
+        sub_req_p->blf_state = CC_SIP_BLF_UNKNOWN;
+        /*
+         * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED so that SIP stack cleans up.
+         */
+        (void) sub_int_subscribe_term(sub_req_p->sub_id, TRUE,
+                                      sub_req_p->request_id,
+                                      CC_SUBSCRIPTIONS_PRESENCE);
+        /*
+         * send a new subscription
+         */
+        sub_req_p->sub_id = CCSIP_SUBS_INVALID_SUB_ID;
+        sub_req_p->highest_cseq = 0;
+        if (send_subscribe_ev_to_sip_task(sub_req_p) != CC_RC_SUCCESS) {
+            /* let platform know that we can not continue */
+            ui_BLF_notification(request_id, CC_SIP_BLF_REJECTED, sub_req_p->app_id);
+            /*
+             * remove the node from the list of subscriptions.
+             */
+            free_sub_request(sub_req_p);
+            BLF_ERROR(MISC_F_PREFIX"Exiting : Unable to send SUBSCRIBE\n", fname);
+            return;
+        }
+        BLF_DEBUG(DEB_F_PREFIX"Exiting : subscribed again after receiving 481\n", DEB_F_PREFIX_ARGS(BLF, fname));
+        return;
+    }
+
+    /*
+     * If the status_code is 481 (sub does not exist) and app_id is zero, terminate the subscription
+     * so that platform can make a new subscription.
+     * if the status_code is 403/603(forbidden), 489(Bad event), 401(Unauthorized) or
+     * any uninterested code, then update the presence/BLF state as REJECTED.
+     */
+    ui_BLF_notification(request_id, CC_SIP_BLF_REJECTED, sub_req_p->app_id);
+    /*
+     * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED so that SIP stack cleans up.
+     */
+    (void) sub_int_subscribe_term(sub_req_p->sub_id, TRUE,
+                                  sub_req_p->request_id,
+                                  CC_SUBSCRIPTIONS_PRESENCE);
+
+    /*
+     * and remove the node from the list of subscriptions.
+     */
+    free_sub_request(sub_req_p);
+
+    BLF_DEBUG(DEB_F_PREFIX"Exiting : request terminated\n", DEB_F_PREFIX_ARGS(BLF, fname));
+    return;
+}
+
+/*
+ *  Function: notify_ind_cb()
+ *
+ *  Parameters: msg_data - the response data provoded by SIP stack.
+ *
+ *  Description:  is invoked by SIP stack when it receives a NOTIFY message. it takes
+ *                action based on subscription_state and blf state derived from event body.
+ *
+ *  Returns: void
+ */
+static void
+notify_ind_cb (ccsip_sub_not_data_t * msg_data)
+{
+    static const char fname[] = "notify_ind_cb";
+    int sub_state = msg_data->u.notify_ind_data.subscription_state;
+    sip_subs_state_reason_e reason =
+    msg_data->u.notify_ind_data.subscription_state_reason;
+    uint32_t retry_after = msg_data->u.notify_ind_data.retry_after;
+    int request_id = msg_data->request_id;
+    sub_id_t sub_id = msg_data->sub_id;
+    pres_subscription_req_t *sub_req_p;
+    Presence_ext_t *event_body_p = NULL;
+    uint32_t cseq = msg_data->u.notify_ind_data.cseq;
+    int blf_state;
+
+    BLF_DEBUG(DEB_F_PREFIX"Entering (subscription_state=%d)\n",
+              DEB_F_PREFIX_ARGS(BLF, fname), sub_state);
+
+    /*
+     * memory for event bodies is allocated by sip stack and it is the
+     * responsibility of the user (this module) to free it when it is done with it.
+     */
+    if ((msg_data->u.notify_ind_data.eventData != NULL) &&
+        (msg_data->u.notify_ind_data.eventData->type != EVENT_DATA_PRESENCE)) {
+        BLF_ERROR(MISC_F_PREFIX"NOTIFY does not contain presence body\n", fname);
+        free_event_data(msg_data->u.notify_ind_data.eventData);
+        msg_data->u.notify_ind_data.eventData = NULL;
+    }
+
+    event_body_p = (msg_data->u.notify_ind_data.eventData == NULL) ?
+        NULL : &(msg_data->u.notify_ind_data.eventData->u.presence_rpid);
+
+
+    if ((s_pres_req_list == NULL) ||
+        ((sub_req_p = (pres_subscription_req_t *)
+          sll_find(s_pres_req_list, &request_id)) == NULL)) {
+        /*
+         * since we do not have subscription for this, help SIP stack clean up.
+         * first, post SIPSPI_EV_CC_NOTIFY_RESPONSE so that SIP stack sends 481, then
+         * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED so that SIP stack cleans up.
+         */
+        (void) sub_int_notify_ack(sub_id, SIP_CLI_ERR_CALLEG, cseq);
+
+        (void) sub_int_subscribe_term(sub_id, TRUE, request_id,
+                                      CC_SUBSCRIPTIONS_PRESENCE);
+        free_event_data(msg_data->u.notify_ind_data.eventData);
+        BLF_DEBUG(DEB_F_PREFIX"Exiting : subscription does not exist\n", DEB_F_PREFIX_ARGS(BLF, fname));
+        return;
+    }
+
+    /*
+     * post SIPSPI_EV_CC_NOTIFY_RESPONSE.
+     */
+    (void) sub_int_notify_ack(sub_id, SIP_STATUS_SUCCESS, cseq);
+
+    /*
+     * check if it is out of sequence NOTIFY, if so, do not use the presence state carried in it.
+     */
+    if (cseq < sub_req_p->highest_cseq) {
+        free_event_data(msg_data->u.notify_ind_data.eventData);
+        BLF_ERROR(MISC_F_PREFIX"Exiting : out of sequence NOTIFY received\n", fname);
+        return;
+    } else {
+        sub_req_p->highest_cseq = cseq;
+    }
+
+
+    /*
+     * If the Subscription_state is terminated, then ...
+     */
+    if (sub_state == SUBSCRIPTION_STATE_TERMINATED) {
+        /*
+         * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED to SIP stack.
+         */
+        (void) sub_int_subscribe_term(sub_id, TRUE, sub_req_p->request_id,
+                                      CC_SUBSCRIPTIONS_PRESENCE);
+        if (reason == SUBSCRIPTION_STATE_REASON_DEACTIVATED) {
+            /* if the reason is "decativated", re-subscribe. */
+            sub_req_p->sub_id = CCSIP_SUBS_INVALID_SUB_ID;
+            sub_req_p->highest_cseq = 0;
+            /*
+             * post SIPSPI_EV_CC_SUBSCRIBE to SIP stack
+             */
+            if (send_subscribe_ev_to_sip_task(sub_req_p) != CC_RC_SUCCESS) {
+                /* let platform know that we can not continue */
+                ui_BLF_notification(request_id, CC_SIP_BLF_REJECTED,
+                                 sub_req_p->app_id);
+                /*
+                 * remove the node from the list of subscriptions.
+                 */
+                free_sub_request(sub_req_p);
+            }
+        } else if ((reason == SUBSCRIPTION_STATE_REASON_TIMEOUT) ||
+                   (reason == SUBSCRIPTION_STATE_REASON_PROBATION) ||
+                   (reason == SUBSCRIPTION_STATE_REASON_GIVEUP)) {
+            /* let the app know that susbcription expired so that it can resusbcribe later */
+            ui_BLF_notification(request_id, CC_SIP_BLF_EXPIRED, sub_req_p->app_id);
+            sub_req_p->blf_state = CC_SIP_BLF_EXPIRED;
+            if (sub_req_p->app_id > 0) {
+                /*
+                 * Since it is speeddial/blf, we must send a new subscription.
+                 */
+                sub_req_p->sub_id = CCSIP_SUBS_INVALID_SUB_ID;
+                sub_req_p->highest_cseq = 0;
+                if ((reason == SUBSCRIPTION_STATE_REASON_PROBATION) ||
+                    (reason == SUBSCRIPTION_STATE_REASON_GIVEUP)) {
+                    /*
+                     * Start a timer based on retry-after value. If retry-after value is 0,
+                     * use a default value of 5 sec
+                     */
+                    if (retry_after == 0) {
+                        retry_after = DEFAULT_RETRY_AFTER_MILLISECS;
+                    } else {
+                        retry_after = (retry_after * 1000); // converting into millisecs
+                    }
+                    if ((cprCancelTimer(s_retry_after_timers[sub_req_p->app_id - 1])
+                                == CPR_SUCCESS) &&
+                        (cprStartTimer(s_retry_after_timers[sub_req_p->app_id - 1],
+                          retry_after, (void *) sub_req_p) == CPR_SUCCESS)) {
+                        /*
+                         * Timer successfully started. free up event data and return.
+                         */
+                        free_event_data(msg_data->u.notify_ind_data.eventData);
+                        BLF_DEBUG(DEB_F_PREFIX"Exiting : retry_after Timer started\n",
+                                  DEB_F_PREFIX_ARGS(BLF, fname));
+                        return;
+                    }
+
+                }
+                if (send_subscribe_ev_to_sip_task(sub_req_p) != CC_RC_SUCCESS) {
+                    /*
+                     * remove the node from the list of subscriptions.
+                     */
+                    free_sub_request(sub_req_p);
+                    BLF_ERROR(MISC_F_PREFIX"Unable to send SUBSCRIBE\n", fname);
+                }
+                BLF_DEBUG(DEB_F_PREFIX"subscribed again after expiration\n", DEB_F_PREFIX_ARGS(BLF, fname));
+            } else {
+                /*
+                 * and remove the node from the list of subscriptions.
+                 */
+                free_sub_request(sub_req_p);
+            }
+        } else {
+            ui_BLF_notification(request_id, CC_SIP_BLF_REJECTED, sub_req_p->app_id);
+            /*
+             * and remove the node from the list of subscriptions.
+             */
+            free_sub_request(sub_req_p);
+        }
+    } else {
+        /* derive the BLF state from event data */
+        blf_state = extract_blf_state(event_body_p, sub_req_p->feature_mask);
+        ui_BLF_notification(request_id, blf_state, sub_req_p->app_id);
+        sub_req_p->blf_state = blf_state;
+        /*
+         * if blf state is ALERTING,
+         * play blf alerting audible tone.
+         */
+        if (blf_state == CC_SIP_BLF_ALERTING) {
+            /*
+             * Post an event to GSM to play alerting tone.
+             */
+            cc_feature(CC_SRC_MISC_APP, CC_NO_CALL_ID, 0, CC_FEATURE_BLF_ALERT_TONE, NULL);
+        }
+        DEF_DEBUG(DEB_F_PREFIX"SUB %d: BLF %d\n",
+            DEB_F_PREFIX_ARGS(BLF_INFO, fname), sub_state, blf_state);
+    }
+
+    free_event_data(msg_data->u.notify_ind_data.eventData);
+    BLF_DEBUG(DEB_F_PREFIX"Exiting : acted based on subscription state\n", DEB_F_PREFIX_ARGS(BLF, fname));
+    return;
+}
+
+/**
+ * This function will process incoming unsolicited NOTIFY for presence event.
+ * This can only be inovked by misc app.
+ *
+ * @param[in] msg_data - pointer to ccsip_sub_not_data_t
+ *
+ * @return none
+ *
+ * @pre (msg_data != NULL)
+ */
+static void
+unsolicited_notify_ind_cb (ccsip_sub_not_data_t *msg_data)
+{
+    Presence_ext_t *event_body_p = NULL;
+    char  *presentity_url = NULL;
+    char  presentity_user[CC_MAX_DIALSTRING_LEN];
+    static const char fname[] = "unsolicited_notify_ind_cb";
+
+    BLF_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(BLF, fname));
+    /*
+     * memory for event bodies is allocated by sip stack and it is the
+     * responsibility of the user (this module) to free it when it is done with it.
+     */
+    if ((msg_data->u.notify_ind_data.eventData != NULL) &&
+        (msg_data->u.notify_ind_data.eventData->type != EVENT_DATA_PRESENCE)) {
+        BLF_ERROR(MISC_F_PREFIX"NOTIFY does not contain presence body\n", fname);
+        free_event_data(msg_data->u.notify_ind_data.eventData);
+        msg_data->u.notify_ind_data.eventData = NULL;
+    }
+
+    event_body_p = (msg_data->u.notify_ind_data.eventData == NULL) ?
+        NULL : &(msg_data->u.notify_ind_data.eventData->u.presence_rpid);
+
+    if (event_body_p == NULL) {
+        BLF_DEBUG("Exiting pres_sub_not_handler.c:%s(): no presence body", fname);
+        return;
+    }
+
+    if (s_subs_hndlr_initialized == FALSE) {
+        /*
+         * append this Notification to pending queue
+         * until subscription handler is initialized.
+         */
+        append_notification_to_pending_queue(msg_data->u.notify_ind_data.eventData);
+        BLF_DEBUG("MSC: 0/0: %s: appended presence notification to the pending queue",
+                  fname);
+        return;
+
+    }
+
+    if (s_pres_req_list == NULL) {
+        free_event_data(msg_data->u.notify_ind_data.eventData);
+        BLF_DEBUG(DEB_F_PREFIX"Exiting : no pres requests\n", DEB_F_PREFIX_ARGS(BLF, fname));
+        return;
+    }
+    /* strip of the "sip:" */
+    presentity_url = strchr(event_body_p->presence_body.entity, ':');
+    if (presentity_url == NULL)
+    {
+        BLF_ERROR("MSC:  Error parsing presentity_url", fname);
+        return;
+    }
+
+    presentity_url = presentity_url + 1;
+
+    /*
+     * look for long from (user@host) matches first. if none found, look
+     * for short form (user) matches.
+     */
+    if (apply_presence_state_to_matching_feature_keys(presentity_url, event_body_p) != TRUE) {
+        ccsip_util_extract_user(event_body_p->presence_body.entity, presentity_user);
+        if (apply_presence_state_to_matching_feature_keys(presentity_user, event_body_p) != TRUE) {
+            BLF_DEBUG("pres_sub_not_handler.c:%s(): no matching BLF feature keys found", fname);
+        }
+    }
+
+    free_event_data(msg_data->u.notify_ind_data.eventData);
+    BLF_DEBUG("Exiting pres_sub_not_handler.c:%s(): pres state processed successfully", fname);
+    return;
+}
+
+/**
+ * This function will find the matching feature keys.
+ *
+ * @param[in] presentity - pointer to presentity
+ * @param[in] event_body_p - pointer to presense body
+ *
+ * @return TRUE/FALSE
+ *
+ * @pre (presentity != NULL)
+ */
+static
+boolean apply_presence_state_to_matching_feature_keys (char *presentity,
+                                                       Presence_ext_t *event_body_p)
+{
+    pres_subscription_req_t *sub_req_p;
+    int blf_state;
+    boolean match_found = FALSE;
+
+    sub_req_p = (pres_subscription_req_t *)sll_next(s_pres_req_list, NULL);
+    while (sub_req_p != NULL) { /* apply the state to all the entries whose presentity matches */
+        if ((sub_req_p->app_id > 0) &&
+            (strncmp(sub_req_p->presentity, presentity, CC_MAX_DIALSTRING_LEN - 1) == 0)) {
+            match_found = TRUE;
+            /* derive the BLF state from event data */
+            blf_state = extract_blf_state(event_body_p, sub_req_p->feature_mask);
+            ui_BLF_notification(sub_req_p->request_id, blf_state, sub_req_p->app_id);
+            sub_req_p->blf_state = blf_state;
+            /*
+             * if blf state is ALERTING,
+             * play blf alerting audible tone.
+             */
+            if (blf_state == CC_SIP_BLF_ALERTING) {
+                /*
+                 * Post an event to GSM to play alerting tone.
+                 */
+                cc_feature(CC_SRC_MISC_APP, CC_NO_CALL_ID, 0, CC_FEATURE_BLF_ALERT_TONE, NULL);
+            }
+        }
+        sub_req_p = (pres_subscription_req_t *)sll_next(s_pres_req_list, sub_req_p);
+    }
+    return match_found;
+}
+
+/**
+ * This function will append presence notification to the pending queue.
+ *
+ * @param[in] event_data_p - pointer to event data.
+ *
+ * @return none.
+ *
+ * @pre (event_data_p != NULL)
+ */
+static void append_notification_to_pending_queue (ccsip_event_data_t *event_data_p)
+{
+    static const char fname[] = "append_notification_to_pending_queue";
+    pres_pending_notify_t *pending_notify_p;
+    Presence_ext_t *event_body_p = &(event_data_p->u.presence_rpid);
+
+    /*
+     * create pending list if it is not created yet.
+     */
+    if (s_pending_notify_list == NULL) {
+        s_pending_notify_list = sll_create(NULL);
+        if (s_pending_notify_list == NULL) {
+            err_msg("MSC: 0/0: %s: out of memory", fname);
+            free_event_data(event_data_p);
+            return;
+        }
+    }
+
+    pending_notify_p = (pres_pending_notify_t *)sll_next(s_pending_notify_list, NULL);
+    while (pending_notify_p != NULL) {
+        if (strncmp(pending_notify_p->presentity, event_body_p->presence_body.entity,
+                    CC_MAX_DIALSTRING_LEN - 1) == 0) {
+            /* replace the current state with new state */
+            free_event_data(pending_notify_p->event_data_p);
+            pending_notify_p->event_data_p = event_data_p;
+            return;
+        }
+        pending_notify_p = (pres_pending_notify_t *)sll_next(s_pending_notify_list,
+                                                             pending_notify_p);
+    }
+
+    /*
+     * To protect from DoS attacks, do not allow more than
+     * MAX_REG_LINES entries in the list.
+     */
+    if (sll_count(s_pending_notify_list) == MAX_REG_LINES) {
+        err_msg("MSC: 0/0: %s: ignoring the NOTIFY to protect from DoS attack", fname);
+        free_event_data(event_data_p);
+        return;
+    }
+    pending_notify_p = (pres_pending_notify_t *)
+                       cpr_malloc(sizeof(pres_pending_notify_t));
+    if (pending_notify_p == NULL) {
+        err_msg("MSC: 0/0: %s: out of memory", fname);
+        free_event_data(event_data_p);
+        return;
+    }
+    sstrncpy(pending_notify_p->presentity, event_body_p->presence_body.entity,
+             CC_MAX_DIALSTRING_LEN);
+    pending_notify_p->event_data_p = event_data_p;
+    (void) sll_append(s_pending_notify_list, pending_notify_p);
+    return;
+}
+
+/**
+ * This function will process if there are any pending notifications.
+ *
+ * @param none.
+ *
+ * @return none.
+ */
+static void sub_handler_initialized (void)
+{
+    static const char fname[] = "sub_handler_initialized";
+    pres_pending_notify_t *pending_notify_p;
+    char  *presentity_url = NULL;
+    char  presentity_user[CC_MAX_DIALSTRING_LEN];
+    Presence_ext_t *event_body_p = NULL;
+
+    BLF_DEBUG("MSC: 0/0: %s: invoked\n", fname);
+    s_subs_hndlr_initialized = TRUE;
+
+    if (s_pending_notify_list == NULL) {
+        BLF_DEBUG("MSC: 0/0: %s: no pending notfications\n", fname);
+        return;
+    }
+
+    /*
+     * process the pending NOTIFYs.
+     */
+    pending_notify_p = (pres_pending_notify_t *)sll_next(s_pending_notify_list, NULL);
+    while (pending_notify_p != NULL) {
+        /* strip of the "sip:" */
+        presentity_url = strchr(pending_notify_p->presentity, ':');
+        if (presentity_url == NULL)
+        {
+            BLF_ERROR("MSC:  Error parsing presentity_url", fname);
+            return;
+        }
+
+        presentity_url = presentity_url + 1;
+
+        /*
+         * look for long from (user@host) matches first. if none found, look
+         * for short form (user) matches.
+         */
+        event_body_p = &(pending_notify_p->event_data_p->u.presence_rpid);
+        if (apply_presence_state_to_matching_feature_keys(presentity_url, event_body_p)
+            != TRUE) {
+            ccsip_util_extract_user(pending_notify_p->presentity, presentity_user);
+            if (apply_presence_state_to_matching_feature_keys(presentity_user,
+                event_body_p) != TRUE) {
+                BLF_DEBUG("MSC: 0/0: %s: no matching BLF feature keys found", fname);
+            }
+        }
+        BLF_DEBUG("MSC: 0/0: %s: processed a pending notfication for %s\n",
+                  fname, presentity_url);
+        free_event_data(pending_notify_p->event_data_p);
+        (void) sll_remove(s_pending_notify_list, (void *)pending_notify_p);
+        cpr_free(pending_notify_p);
+
+        pending_notify_p = (pres_pending_notify_t *)sll_next(s_pending_notify_list,
+                                                             NULL);
+    }
+    (void)sll_destroy(s_pending_notify_list);
+    s_pending_notify_list = NULL;
+}
+
+/*
+ *  Function: terminate_cb()
+ *
+ *  Parameters: msg_data - the response data provoded by SIP stack.
+ *
+ *  Description:  is invoked by SIP stack when it needs to terminate a subscription because of
+ *                some reason (failover, shutdown, etc). if the reason is shutdown, rollover, etc,
+ *                we will terminate the subscription. Platform will resubscribe later if it is a failover/fallback.
+ *                if it is a protocol error such as wrong header/wrong body, then terminate the subscription
+ *                and resubscribe immediately.
+ *
+ *  Returns: void
+ */
+static void
+terminate_cb (ccsip_sub_not_data_t *msg_data)
+{
+    static const char fname[] = "terminate_cb";
+    ccsip_reason_code_e reason_code = msg_data->reason_code;
+    int status_code = msg_data->u.subs_term_data.status_code;
+    int request_id = msg_data->request_id;
+    sub_id_t sub_id = msg_data->sub_id;
+    pres_subscription_req_t *sub_req_p = NULL;
+    int orig_duration = 0;
+
+    BLF_DEBUG(DEB_F_PREFIX"Entering (reason_code=%d, status_code=%d)\n",
+              DEB_F_PREFIX_ARGS(BLF, fname), reason_code, status_code);
+
+    if (s_pres_req_list != NULL) {
+        sub_req_p = (pres_subscription_req_t *)
+            sll_find(s_pres_req_list, &request_id);
+    }
+    if (sub_req_p == NULL) {
+        /*
+         * we are not aware of any such subcription. So
+         * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED so that SIP stack cleans up.
+         */
+        (void) sub_int_subscribe_term(sub_id, TRUE, request_id,
+                                      CC_SUBSCRIPTIONS_PRESENCE);
+        BLF_DEBUG(DEB_F_PREFIX"Exiting : subscription does not exist\n", DEB_F_PREFIX_ARGS(BLF, fname));
+        return;
+    }
+
+    orig_duration = sub_req_p->duration;
+    if (reason_code == SM_REASON_CODE_ERROR) { // protocol error
+        /*
+         * Send a terminating SUBSCRIBE (expires=0) to make sure other end terminates the subscription
+         */
+        sub_req_p->duration = 0;
+        /*
+         * no point in checking return value of the subsmanager_handle_ev_app_subscribe()
+         * because we are terminating the subscription anyway.
+         */
+        (void) send_subscribe_ev_to_sip_task(sub_req_p);
+    }
+    /*
+     * post SIPSPI_EV_CC_SUBSCRIPTION_TERMINATED so that SIP stack cleans up.
+     * In case of SM_REASON_CODE_RESET_REG, SM_REASON_CODE_ROLLOVER & SM_REASON_CODE_SHUTDOWN,
+     * SIP stack already cleaned up SCB.
+     */
+    if ((reason_code != SM_REASON_CODE_RESET_REG) &&
+        (reason_code != SM_REASON_CODE_ROLLOVER) &&
+        (reason_code != SM_REASON_CODE_SHUTDOWN)) {
+        (void) sub_int_subscribe_term(sub_id, TRUE, request_id,
+                                  CC_SUBSCRIPTIONS_PRESENCE);
+    }
+
+    /* let platform know that the current state is UNKNOWN */
+    ui_BLF_notification(request_id, CC_SIP_BLF_UNKNOWN, sub_req_p->app_id);
+
+    if ((reason_code == SM_REASON_CODE_ERROR) ||
+        (reason_code == SM_REASON_CODE_RESET_REG)) {
+        /*
+         * send a new subscription
+         */
+        sub_req_p->sub_id = CCSIP_SUBS_INVALID_SUB_ID;
+        sub_req_p->highest_cseq = 0;
+        sub_req_p->duration = orig_duration;
+        (void) send_subscribe_ev_to_sip_task(sub_req_p);
+    } else {
+        /*
+         * remove the node from the list of subscriptions because we may be shutting down/rolling over.
+         */
+        free_sub_request(sub_req_p);
+    }
+
+    BLF_DEBUG(DEB_F_PREFIX"Exiting : terminated subscription\n", DEB_F_PREFIX_ARGS(BLF, fname));
+    return;
+}
+
+/*
+ *  Function: extract_blf_state()
+ *
+ *  Parameters: event_body_p
+ *              feature_mask - indicates the feature enabled
+ *
+ *  Description:  extracts blf state from presence event body.
+ *
+ *  Returns: one of CC_SIP_BLF_IDLE, CC_SIP_BLF_UNKNOWN and CC_SIP_BLF_INUSE.
+ */
+static int
+extract_blf_state (Presence_ext_t *event_body_p, int feature_mask)
+{
+    static const char fname[] = "extract_blf_state";
+    char *basic_p = NULL;
+    boolean on_the_phone;
+    boolean alerting;
+    int return_code = CC_SIP_BLF_UNKNOWN;
+
+    BLF_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(BLF, fname));
+
+    if (event_body_p == NULL) {
+        BLF_DEBUG(DEB_F_PREFIX
+                  "Exiting with return value %d because there is no event body\n",
+                  DEB_F_PREFIX_ARGS(BLF, fname), CC_SIP_BLF_UNKNOWN);
+        return CC_SIP_BLF_UNKNOWN;
+    }
+    basic_p = event_body_p->presence_body.person.personStatus.basic;
+    if (basic_p[0] == '\0') {
+        basic_p = event_body_p->presence_body.tuple[0].status.basic;
+    }
+    on_the_phone = event_body_p->onThePhone;
+    alerting = event_body_p->alerting;
+    BLF_DEBUG(DEB_F_PREFIX"basic: %s, onThePhone:%d, alerting:%d\n",
+              DEB_F_PREFIX_ARGS(BLF, fname), basic_p, on_the_phone, alerting);
+
+    if ((on_the_phone == FALSE) &&
+        (cpr_strcasecmp(basic_p, "closed") == 0)) {
+        BLF_DEBUG(DEB_F_PREFIX"Exiting with return value %d\n",
+                  DEB_F_PREFIX_ARGS(BLF, fname), CC_SIP_BLF_UNKNOWN);
+        return(CC_SIP_BLF_UNKNOWN);
+    }
+
+    if (feature_mask & BLF_PICKUP_FEATURE) {
+        if (alerting == TRUE) {
+            BLF_DEBUG(DEB_F_PREFIX"Exiting with return value %d\n",
+                      DEB_F_PREFIX_ARGS(BLF, fname), CC_SIP_BLF_ALERTING);
+            return CC_SIP_BLF_ALERTING;
+
+        }
+    }
+    if (on_the_phone == FALSE) {
+        if (cpr_strcasecmp(basic_p, "open") == 0) {
+            return_code = CC_SIP_BLF_IDLE;
+        }
+    } else {
+        return_code = CC_SIP_BLF_INUSE;
+    }
+
+    BLF_DEBUG(DEB_F_PREFIX"Exiting with return value %d\n",
+              DEB_F_PREFIX_ARGS(BLF, fname), return_code);
+    return return_code;
+}
+
+void
+pres_process_msg_from_msgq (uint32_t cmd, void *msg_p)
+{
+    static const char fname[] = "pres_process_msg_from_msgq";
+    pres_req_msg *pres_req_p;
+
+    BLF_DEBUG(DEB_F_PREFIX"Entering (cmd=%d, msg_p=0x%X)\n",
+              DEB_F_PREFIX_ARGS(BLF, fname), cmd, msg_p);
+
+    switch (cmd) {
+    case SUB_HANDLER_INITIALIZED:
+        sub_handler_initialized();
+        break;
+
+    case SUB_MSG_PRESENCE_GET_STATE:
+        pres_req_p = (pres_req_msg *) msg_p;
+        get_state(pres_req_p->request_id,
+                  pres_req_p->duration,
+                  pres_req_p->watcher,
+                  pres_req_p->presentity,
+                  pres_req_p->app_id,
+                  pres_req_p->feature_mask);
+        break;
+
+    case SUB_MSG_PRESENCE_TERM_REQ:
+        terminate_req(*((int *)msg_p));
+        break;
+
+    case SUB_MSG_PRESENCE_TERM_REQ_ALL:
+        terminate_req_all();
+        break;
+
+    case SUB_MSG_PRESENCE_SUBSCRIBE_RESP:
+        subscribe_response_ind((ccsip_sub_not_data_t *)msg_p);
+        break;
+
+    case SUB_MSG_PRESENCE_NOTIFY:
+        notify_ind_cb((ccsip_sub_not_data_t *)msg_p);
+        break;
+
+    case SUB_MSG_PRESENCE_UNSOLICITED_NOTIFY:
+        unsolicited_notify_ind_cb((ccsip_sub_not_data_t *)msg_p);
+        break;
+
+    case SUB_MSG_PRESENCE_TERMINATE:
+        terminate_cb((ccsip_sub_not_data_t *)msg_p);
+        break;
+
+    case TIMER_EXPIRATION:
+        process_timer_expiration(msg_p);
+        break;
+
+    default:
+        BLF_ERROR(MISC_F_PREFIX"bad Cmd received: %d\n", fname, cmd);
+        break;
+    }
+    BLF_DEBUG(DEB_F_PREFIX"Exiting\n", DEB_F_PREFIX_ARGS(BLF, fname));
+}
+
+/*
+ *  Function: pres_create_retry_after_timers()
+ *
+ *  Parameters: void
+ *
+ *  Description:  creates retry-after timers equivalant to the number of line buttons.
+ *
+ *  Returns: CPR_SUCCESS/CPR_FAILURE
+ */
+cpr_status_e
+pres_create_retry_after_timers (void)
+{
+    int i;
+    int j;
+
+    /*
+     * Create retry-after timers.
+     */
+    for (i = 0; i < MAX_REG_LINES; i++) {
+        s_retry_after_timers[i] =
+            cprCreateTimer("Presence/BLF Retry After Timer",
+                           PRES_RETRYAFTER_TIMER, TIMER_EXPIRATION,
+                           s_misc_msg_queue);
+        if (!s_retry_after_timers[i]) {
+            /*
+             *  destroy/free the already created timers.
+             */
+            for (j = 0; j < i; j++) {
+                (void) cprDestroyTimer(s_retry_after_timers[j]);
+                s_retry_after_timers[j] = NULL;
+            }
+            return CPR_FAILURE;
+        }
+    }
+    return CPR_SUCCESS;
+}
+
+/**
+ *  pres_destroy_retry_after_timers() destroys retry-after timers
+ *  created by pres_create_retry_after_timers().
+ *
+ *  @param  none.
+ *
+ *  @return none.
+ */
+void
+pres_destroy_retry_after_timers (void)
+{
+    int i;
+
+    /*
+     * Destroy retry-after timers.
+     */
+    for (i = 0; i < MAX_REG_LINES; i++) {
+        if (s_retry_after_timers[i] != NULL) {
+            (void) cprDestroyTimer(s_retry_after_timers[i]);
+            s_retry_after_timers[i] = NULL;
+        }
+    }
+}
+
+/*
+ *  Function: free_sub_request()
+ *
+ *  Parameters: sup_req_p - pointer to subscription request
+ *
+ *  Description:  removes from LL, cancels timers if necessary and frees the memory
+ *
+ *  Returns: void
+ */
+static void
+free_sub_request (pres_subscription_req_t *sub_req_p)
+{
+    /*
+     * remove the node from the linked list of subscriptions.
+     */
+    (void) sll_remove(s_pres_req_list, (void *)sub_req_p);
+
+    /*
+     * If it is a line button subscription, cancel retry-timer if it is running
+     */
+    if (sub_req_p->app_id > 0) {
+        (void) cprCancelTimer(s_retry_after_timers[sub_req_p->app_id - 1]);
+    }
+
+    /*
+     *  free the memory
+     */
+    cpr_free(sub_req_p);
+}
+
+/*
+ *  Function: process_timer_expiration()
+ *
+ *  Parameters: void *
+ *
+ *  Description:  processes timer expiration based on the timer ID.
+ *
+ *  Returns: void
+ */
+static void
+process_timer_expiration (void *msg_p)
+{
+    static const char fname[] = "process_timer_expiration";
+    cprCallBackTimerMsg_t *timerMsg;
+    pres_subscription_req_t *sub_req_p;
+
+    BLF_DEBUG(DEB_F_PREFIX"Entering\n", DEB_F_PREFIX_ARGS(BLF, fname));
+
+    timerMsg = (cprCallBackTimerMsg_t *) msg_p;
+    switch (timerMsg->expiredTimerId) {
+    case PRES_RETRYAFTER_TIMER:
+        sub_req_p = (pres_subscription_req_t *) (timerMsg->usrData);
+        if (send_subscribe_ev_to_sip_task(sub_req_p) != CC_RC_SUCCESS) {
+            /*
+             * remove the node from the list of subscriptions.
+             */
+            free_sub_request(sub_req_p);
+            BLF_DEBUG(DEB_F_PREFIX"Unable to send SUBSCRIBE", DEB_F_PREFIX_ARGS(BLF, fname));
+        }
+        BLF_DEBUG(DEB_F_PREFIX"resubscribed after retry-after seconds\n", DEB_F_PREFIX_ARGS(BLF, fname));
+        break;
+    default:
+        BLF_ERROR(MISC_F_PREFIX"unknown timer:%d expired\n", fname,
+                  timerMsg->expiredTimerId);
+        break;
+    }
+    BLF_DEBUG(DEB_F_PREFIX"Exiting\n", DEB_F_PREFIX_ARGS(BLF, fname));
+}
+
+/**
+ * This function will post an event - SUB_MSG_PRESENCE_UNSOLICITED_NOTIFY - to Misc task.
+ * This is invoked by sip task.
+ *
+ * @param[in] msg_data - pointer to ccsip_sub_not_data_t
+ *
+ * @return none
+ *
+ * @pre (msg_data != NULL)
+ */
+void pres_unsolicited_notify_ind (ccsip_sub_not_data_t *msg_data)
+{
+    static const char fname[] = "pres_unsolicited_notify_ind";
+    ccsip_sub_not_data_t *pmsg;
+    cpr_status_e rc;
+
+    pmsg = (ccsip_sub_not_data_t *) cc_get_msg_buf(sizeof(*pmsg));
+
+    if (!pmsg) {
+        BLF_ERROR(MISC_F_PREFIX"malloc failed\n", fname);
+        return;
+    }
+    memcpy(pmsg, msg_data, sizeof(*pmsg));
+
+    rc = MiscAppTaskSendMsg(SUB_MSG_PRESENCE_UNSOLICITED_NOTIFY, pmsg, sizeof(*pmsg));
+    if (rc == CPR_FAILURE) {
+        cpr_free(pmsg);
+    }
+
+}
+
+/**
+ * This function will post an event - CC_FEATURE_BLF_ALERT_TONE - to GSM task if
+ * there is an entry in BLF_ALERTING state.
+ *
+ * @param[in] none
+ *
+ * @return none
+ */
+void pres_play_blf_audible_alert (void)
+{
+    pres_subscription_req_t *sub_req_p;
+
+    sub_req_p = (pres_subscription_req_t *)sll_next(s_pres_req_list, NULL);
+    while (sub_req_p != NULL) {
+        if ((sub_req_p->app_id > 0) && (sub_req_p->blf_state == CC_SIP_BLF_ALERTING)) {
+            /*
+             * Post an event to GSM to play alerting tone.
+             */
+            cc_feature(CC_SRC_MISC_APP, CC_NO_CALL_ID, 0, CC_FEATURE_BLF_ALERT_TONE, NULL);
+            break;
+        }
+        sub_req_p = (pres_subscription_req_t *)sll_next(s_pres_req_list, sub_req_p);
+    }
+}
+/*************************************** THE END **************************/
diff --git a/libs/sipcc/core/src-common/publish_int.c b/libs/sipcc/core/src-common/publish_int.c
new file mode 100644 (file)
index 0000000..9982b89
--- /dev/null
@@ -0,0 +1,174 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "publish_int.h"
+#include "subapi.h"
+#include "ccsip_subsmanager.h"
+#include "phntask.h"
+
+/**
+ * This function will post an event - SIPSPI_EV_CC_PUBLISH_REQ - to SIP stack
+ * which will be handled by PUBLISH Handler in sip stack.
+ *
+ * @param[in] pub_req_p - pointer to a pub_req_t struct.
+ *
+ * @return  CC_RC_SUCCESS if the event is successfully posted.
+ *          Otherwise, CC_RC_ERROR is returned.
+ *
+ * @pre (pub_req_p != NULL)
+ */
+static cc_rcs_t pub_int_req (pub_req_t *pub_req_p)
+{
+    cc_rcs_t ret_code;
+
+    ret_code = app_send_message(pub_req_p, sizeof(pub_req_t), CC_SRC_SIP,  SIPSPI_EV_CC_PUBLISH_REQ);
+
+    if (ret_code != CC_RC_SUCCESS) {
+        free_event_data(pub_req_p->event_data_p);
+    }
+    return ret_code;
+}
+
+/**
+ * This function will trigger initial PUBLISH. This is invoked by
+ * applications that intend to PUBLISH an event state.
+ *
+ * @param[in] app_handle - a unique application handle.
+ * @param[in] ruri - Request URI or user part of it.
+ * @param[in] esc - event state compositor.
+ * @param[in] expires - event state expiration value.
+ * @param[in] event_type - event package name.
+ * @param[in] event_data_p -  pointer to event body. caller does not have to free it.
+ * @param[in] callback_task -  task that is interested in response.
+ * @param[in] message_id -  message id of the response.
+ *
+ * @note application MUST not free event_data_p
+ *
+ * @return  none.
+ */
+void publish_init (pub_handle_t             app_handle,
+                   char                    *ruri,
+                   char                    *esc,
+                   unsigned int             expires,
+                   cc_subscriptions_t       event_type,
+                   ccsip_event_data_t      *event_data_p,
+                   cc_srcs_t                callback_task,
+                   int                      message_id
+                  )
+{
+
+    pub_req_t pub_req;
+
+    /*
+     * Populate pub_req for initial PUBLISH
+     */
+    pub_req.pub_handle = NULL_PUBLISH_HANDLE; //because it is initial request.
+    pub_req.app_handle = app_handle;
+    sstrncpy(pub_req.ruri, ruri, MAX_URI_LENGTH);
+    sstrncpy(pub_req.esc, esc, MAX_URI_LENGTH);
+    pub_req.expires = expires;
+    pub_req.event_type = event_type;
+    pub_req.event_data_p = event_data_p;
+    pub_req.callback_task = callback_task;
+    pub_req.resp_msg_id = message_id;
+
+    (void)pub_int_req(&pub_req);
+}
+
+
+/**
+ * This function will trigger modification PUBLISH. This is invoked by
+ * applications that intend to PUBLISH to modify an event state.
+ *
+ * @param[in] pub_handle - a unique publish handle.
+ * @param[in] event_type - event package name.
+ * @param[in] event_data_p -  pointer to event body. caller does not have to free it.
+ * @param[in] callback_task -  task that is interested in response.
+ * @param[in] message_id -  message id of the response.
+ *
+ * @return  none.
+ */
+void publish_update (pub_handle_t          pub_handle,
+                     cc_subscriptions_t    event_type,
+                     ccsip_event_data_t   *event_data_p,
+                     cc_srcs_t             callback_task,
+                     int                   message_id
+                    )
+{
+    pub_req_t pub_req;
+
+    /*
+     * Populate pub_req for update PUBLISH
+     */
+    memset(&pub_req, 0, sizeof(pub_req));
+    pub_req.pub_handle = pub_handle;
+    pub_req.event_type = event_type;
+    pub_req.event_data_p = event_data_p;
+    pub_req.callback_task = callback_task;
+    pub_req.resp_msg_id = message_id;
+
+    (void)pub_int_req(&pub_req);
+}
+
+
+/**
+ * This function will trigger terminating PUBLISH. This is invoked by
+ * applications that intend to PUBLISH to remove an event state.
+ *
+ * @param[in] pub_handle - a unique publish handle.
+ * @param[in] event_type - event package name.
+ * @param[in] callback_task -  task that is interested in response.
+ * @param[in] message_id -  message id of the response.
+ *
+ * @return  none.
+ */
+void publish_terminate (pub_handle_t          pub_handle,
+                        cc_subscriptions_t    event_type,
+                        cc_srcs_t             callback_task,
+                        int                   message_id
+                       )
+{
+   pub_req_t pub_req;
+
+    /*
+     * Populate pub_req for terminating PUBLISH
+     */
+    memset(&pub_req, 0, sizeof(pub_req));
+    pub_req.pub_handle = pub_handle;
+    pub_req.event_type = event_type;
+    pub_req.callback_task = callback_task;
+    pub_req.resp_msg_id = message_id;
+
+    (void)pub_int_req(&pub_req);
+}
+
+
+/**
+ * This function will post a response event to applications.
+ * this is invoked by SIP stack.
+ *
+ * @param[in] pub_rsp_p - response struct.
+ * @param[in] callback_task -  task that is interested in response.
+ * @param[in] message_id -  message id of the response.
+ *
+ * @return  CC_RC_SUCCESS if the event is successfully posted.
+ *          Otherwise, CC_RC_ERROR is returned.
+ */
+cc_rcs_t publish_int_response (pub_rsp_t               *pub_rsp_p,
+                               cc_srcs_t               callback_task,
+                               int                     message_id
+                              )
+{
+    pub_rsp_t *pmsg;
+
+    pmsg = (pub_rsp_t *) cc_get_msg_buf(sizeof(*pmsg));
+    if (!pmsg) {
+        return CC_RC_ERROR;
+    }
+
+    memcpy(pmsg, pub_rsp_p, sizeof(*pmsg));
+
+    return sub_send_msg((cprBuffer_t)pmsg, message_id, sizeof(*pmsg), callback_task);
+}
+
diff --git a/libs/sipcc/core/src-common/singly_link_list.c b/libs/sipcc/core/src-common/singly_link_list.c
new file mode 100644 (file)
index 0000000..417486f
--- /dev/null
@@ -0,0 +1,306 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "singly_link_list.h"
+
+/*
+ * The following typedefs are included here because we do not want to
+ * reveal the internals to other modules.
+ */
+typedef struct slink_list_node_t {
+    struct slink_list_node_t *next_p; /* pointer to next node */
+    void                     *data_p; /* pointer to node data */
+} slink_list_node_t;
+
+typedef struct {
+    slink_list_node_t  *first_p; /* pointer to first node */
+    slink_list_node_t  *last_p;  /* pointer to last node */
+    unsigned int        count;   /* number of nodes in the list */
+    sll_find_callback_t find_fp; /* find function used to find a matching node */
+} slink_list_t;
+
+/*
+ *     LIST          NODE-1        NODE-2        NODE-3
+ *   -----------    ----------    ----------    ----------
+ *   | first_p |--->| next_p |--->| next_p |--->| next_p |--->NULL
+ *   | count   |    | data_p |    | data_p | |->| data_p |
+ *   | find_fp |    ----------    ---------- |  ----------
+ *   | last_p  |-----------------------------|
+ *   -----------
+ */
+
+/*
+ * sll_create(): creates a signly linked list control block and initializes it.
+ *
+ * Parameters: find_fp - function pointer which will be used to find
+ *                       the matching node.
+ *
+ * Returns: list handle or NULL if it can not create the list.
+ */
+sll_handle_t
+sll_create (sll_find_callback_t find_fp)
+{
+    slink_list_t *link_list_p;
+
+    link_list_p = (slink_list_t *) cpr_malloc(sizeof(slink_list_t));
+    if (link_list_p == NULL) {
+        return NULL;
+    }
+    link_list_p->first_p = NULL;
+    link_list_p->last_p = NULL;
+    link_list_p->count = 0;
+    link_list_p->find_fp = find_fp;
+
+    return (sll_handle_t) link_list_p;
+}
+
+/*
+ * sll_destroy(): if the list is empty, it frees the list.
+ *
+ * Parameters: list_handle - handle to the list.
+ *
+ * Returns: SLL_RET_SUCCESS if it successfully destroys.
+ *          SLL_RET_INVALID_ARGS if the arguments are invalid.
+ *          SLL_RET_LIST_NOT_EMPTY if the list is not empty.
+ */
+sll_return_e
+sll_destroy (sll_handle_t list_handle)
+{
+    slink_list_t *list_p = (slink_list_t *) list_handle;
+
+    /*
+     * validate the arguments.
+     */
+    if (list_p == NULL) {
+        return SLL_RET_INVALID_ARGS;
+    }
+
+    /*
+     * check if the list is empty.
+     */
+    if (list_p->count != 0) {
+        return SLL_RET_LIST_NOT_EMPTY;
+    }
+
+    /*
+     * free the memory allocated for list.
+     */
+    cpr_free(list_p);
+    return SLL_RET_SUCCESS;
+}
+
+/*
+ * sll_append(): creates a list node and appends it to the list.
+ *
+ * Parameters: list_handle - handle to the list.
+ *             data_p - pointer to the data that the list node will point to.
+ *
+ * Returns: SLL_RET_SUCCESS if it successfully appends.
+ *          SLL_RET_INVALID_ARGS if the arguments are invalid.
+ *          SLL_RET_MALLOC_FAILURE if memory allocation fails.
+ */
+sll_return_e
+sll_append (sll_handle_t list_handle, void *data_p)
+{
+    slink_list_t *list_p = (slink_list_t *) list_handle;
+    slink_list_node_t *list_node_p;
+
+    /*
+     * validate the arguments.
+     */
+    if ((list_p == NULL) || (data_p == NULL)) {
+        return SLL_RET_INVALID_ARGS;
+    }
+
+    /*
+     * create the node.
+     */
+    list_node_p = (slink_list_node_t *) cpr_malloc(sizeof(slink_list_node_t));
+    if (list_node_p == NULL) {
+        return SLL_RET_MALLOC_FAILURE;
+    }
+    list_node_p->next_p = NULL;
+    list_node_p->data_p = data_p;
+
+    /*
+     * append to the list.
+     */
+    if (list_p->first_p == NULL) { /* if the list is empty */
+        list_p->first_p = list_p->last_p = list_node_p;
+    } else {
+        list_p->last_p->next_p = list_node_p;
+        list_p->last_p = list_node_p;
+    }
+    list_p->count = list_p->count + 1;
+
+    return SLL_RET_SUCCESS;
+}
+
+/*
+ * sll_remove(): removes the node from the list and frees the node.
+ *
+ * Parameters: list_handle - handle to the list.
+ *             data_p - pointer to the data that the list node points to.
+ *
+ * Returns: SLL_RET_SUCCESS if it successfully removes.
+ *          SLL_RET_INVALID_ARGS if the arguments are invalid.
+ *          SLL_RET_NODE_NOT_FOUND if the node is not found in the list.
+ */
+sll_return_e
+sll_remove (sll_handle_t list_handle, void *data_p)
+{
+    slink_list_t *list_p = (slink_list_t *) list_handle;
+    slink_list_node_t *list_node_p;
+    slink_list_node_t *prev_node_p;
+
+    /*
+     * validate the arguments.
+     */
+    if ((list_p == NULL) || (data_p == NULL)) {
+        return SLL_RET_INVALID_ARGS;
+    }
+
+    /*
+     * search for the node to be removed
+     */
+    prev_node_p = NULL;
+    list_node_p = list_p->first_p;
+    while (list_node_p) {
+        if (list_node_p->data_p == data_p) {
+            break;
+        }
+        prev_node_p = list_node_p;
+        list_node_p = list_node_p->next_p;
+    }
+    if (list_node_p == NULL) {
+        return SLL_RET_NODE_NOT_FOUND;
+    }
+
+    /*
+     * remove the node.
+     */
+    if (prev_node_p == NULL) { /* if we are removing the first node */
+        list_p->first_p = list_node_p->next_p;
+        if (list_p->last_p == list_node_p) { /* if it had only one node */
+            list_p->last_p = list_p->first_p;
+        }
+    } else {
+        prev_node_p->next_p = list_node_p->next_p;
+        if (list_p->last_p == list_node_p) { /* if we are removing last node */
+            list_p->last_p = prev_node_p;
+        }
+    }
+    cpr_free(list_node_p);
+    list_p->count = list_p->count - 1;
+
+    return SLL_RET_SUCCESS;
+}
+
+/*
+ * sll_find(): finds the matching node data using find_fp function.
+ *
+ * Parameters: list_handle - handle to the list.
+ *             find_by_p - pointer to the opaque data that will be used
+ *                         by find_fp function.
+ *
+ * Returns: pointer to the data or NULL if it can not find.
+ */
+void *
+sll_find (sll_handle_t list_handle, void *find_by_p)
+{
+    slink_list_t *list_p = (slink_list_t *) list_handle;
+    slink_list_node_t *list_node_p;
+    sll_match_e match;
+
+    /*
+     * validate the arguments.
+     */
+    if ((list_p == NULL) || (find_by_p == NULL) || (list_p->find_fp == NULL)) {
+        return NULL;
+    }
+
+    list_node_p = list_p->first_p;
+    while (list_node_p) {
+        match = (*(list_p->find_fp))(find_by_p, list_node_p->data_p);
+        if (match == SLL_MATCH_FOUND) {
+            break;
+        }
+        list_node_p = list_node_p->next_p;
+    }
+
+    if (list_node_p == NULL) {
+        return NULL;
+    }
+
+    return list_node_p->data_p;
+}
+
+/*
+ * sll_next(): returns pointer to the data in the next node to the node
+ *             holding data_p.  if data_p is NULL, then returns pointer
+ *             to the data in the first node.
+ *
+ * Parameters: list_handle - handle to the list.
+ *             data_p - pointer to the data that the list node points to.
+ *
+ * Returns: pointer to the data or NULL if it can not find.
+ */
+void *
+sll_next (sll_handle_t list_handle, void *data_p)
+{
+    slink_list_t *list_p = (slink_list_t *) list_handle;
+    slink_list_node_t *list_node_p;
+
+    /*
+     * validate the arguments.
+     */
+    if (list_p == NULL) {
+        return NULL;
+    }
+
+    if (data_p == NULL) { /* if the first node is requested */
+        if (list_p->first_p == NULL) {
+            return NULL;
+        }
+        return list_p->first_p->data_p;
+    }
+
+    list_node_p = list_p->first_p;
+    while (list_node_p) {
+        if (list_node_p->data_p == data_p) {
+            break;
+        }
+        list_node_p = list_node_p->next_p;
+    }
+
+    if (list_node_p == NULL) {
+        return NULL;
+    }
+
+    if (list_node_p->next_p == NULL) {
+        return NULL;
+    }
+
+    return list_node_p->next_p->data_p;
+}
+
+/*
+ * sll_count(): returns the number of elements in the list.
+ *              count of the linked list.
+ *
+ * Parameters: list_handle - handle to the list.
+ *
+ * Returns: returns the number of elements in the list.
+ */
+unsigned int
+sll_count (sll_handle_t list_handle)
+{
+    slink_list_t *list_p = (slink_list_t *) list_handle;
+
+    return (list_p->count);
+}
+
+/*************************************** THE END **************************/
diff --git a/libs/sipcc/core/src-common/sll_lite.c b/libs/sipcc/core/src-common/sll_lite.c
new file mode 100644 (file)
index 0000000..0780374
--- /dev/null
@@ -0,0 +1,258 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *     This module provides sigle linked list near functionality of the
+ *     the singly_link_list.c module with limitations. The main
+ *     intention is for low overhead. The followings are the main
+ *     differences.
+ *
+ *     1) It does not allocate storage containers for list node or
+ *        list control structures by the facility. The caller
+ *        provides the storage from any source as needed.
+ *
+ *     2) No application call back for node search/find.
+ *
+ *     3) The use must include the node structure as the first
+ *        field of the user's data structure to linked. This allows
+ *        user to retrives the node and at the same time access to
+ *        the data associated with the node.
+ *
+ *     4) Lite verion is higher risk because the caller also has
+ *        the defition of the list and node structures. Use it
+ *        at your own risk.
+ *
+ *     5) There is no protection for mutual exclusive access by
+ *        multiple threads or from interrupt context. The caller
+ *        has to provide the protection if needed.
+ */
+
+#include "cpr_types.h"
+#include "sll_lite.h"
+
+/*
+ *     LIST          NODE-1        NODE-2        NODE-3
+ *   -----------    ----------    ----------    ----------
+ *   | head    |--->| next_p |--->| next_p |--->| next_p |--->NULL
+ *   | count   |    |        |    |        |  ->|        |
+ *   |         |    ----------    ----------  | ----------
+ *   | tail    |------------------------------|
+ *   -----------
+ */
+
+/**
+ * sll_lite_init initializes list control structure given by the
+ * caller.
+ *
+ * @param[in]list - pointer to the list control structure
+ *                  sll_lite_list_t
+ *
+ * @return        - SLL_LITE_RET_SUCCESS for success
+ *                - SLL_LITE_RET_INVALID_ARGS when arguments are
+ *                  invalid.
+ */
+sll_lite_return_e
+sll_lite_init (sll_lite_list_t *list)
+{
+    if (list == NULL) {
+        return (SLL_LITE_RET_INVALID_ARGS);
+    }
+    list->count   = 0;
+    list->head_p  = NULL;
+    list->tail_p  = NULL;
+    return (SLL_LITE_RET_SUCCESS);
+}
+
+/**
+ * sll_lite_link_head puts node to the head of the list.
+ *
+ * @param[in]list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ * @param[in]node - pointer to the list node structure.
+ *
+ * @return        - SLL_LITE_RET_SUCCESS for success
+ *                - SLL_LITE_RET_INVALID_ARGS when arguments are
+ *                  invalid.
+ */
+sll_lite_return_e
+sll_lite_link_head (sll_lite_list_t *list, sll_lite_node_t *node)
+{
+    if ((list == NULL) || (node == NULL)) {
+        return (SLL_LITE_RET_INVALID_ARGS);
+    }
+
+    if (list->head_p == NULL) {
+        /* list is empty, this node becomes head */
+        list->head_p = node;
+        list->tail_p = node;
+        node->next_p = NULL;
+    } else {
+        /* list is not empty, link to the head */
+        node->next_p = list->head_p;
+        list->head_p = node;
+    }
+    list->count++;
+
+    return (SLL_LITE_RET_SUCCESS);
+}
+
+/**
+ * sll_lite_link_tail puts node to the tail of the list.
+ *
+ * @param[in]list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ * @param[in]node - pointer to the list node structure.
+ *
+ * @return        - SLL_LITE_RET_SUCCESS for success
+ *                - SLL_LITE_RET_INVALID_ARGS when arguments are
+ *                  invalid.
+ */
+sll_lite_return_e
+sll_lite_link_tail (sll_lite_list_t *list, sll_lite_node_t *node)
+{
+    if ((list == NULL) || (node == NULL)) {
+        return (SLL_LITE_RET_INVALID_ARGS);
+    }
+
+    if (list->tail_p == NULL) {
+        /* list is empty, this node becomes head */
+        list->head_p = node;
+        list->tail_p = node;
+    } else {
+        /* list is not empty, link to the tail */
+        list->tail_p->next_p = node;
+        list->tail_p = node;
+    }
+    node->next_p = NULL;
+    list->count++;
+
+    return (SLL_LITE_RET_SUCCESS);
+}
+
+/**
+ * sll_lite_unlink_head removes head node from the head of the list and
+ * returns it to the caller.
+ *
+ * @param[in]list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ *
+ * @return        Pointer to the head node if one exists otherwise
+ *                return NULL.
+ */
+sll_lite_node_t *
+sll_lite_unlink_head (sll_lite_list_t *list)
+{
+    sll_lite_node_t *node_p = NULL;
+
+    if (list == NULL) {
+        return (NULL);
+    }
+
+    if (list->head_p !=  NULL) {
+        node_p = list->head_p;
+        list->head_p = list->head_p->next_p;
+        if (list->tail_p == node_p) {
+            /* this the only node on the list */
+            list->tail_p = list->head_p;
+        }
+        list->count--;
+        node_p->next_p = NULL;
+    }
+    return (node_p);
+}
+
+/**
+ * sll_lite_unlink_tail removes tail node from the list and
+ * returns it to the caller.
+ *
+ * @param[in]list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ *
+ * @return        Pointer to the tail node if one exists otherwise
+ *                return NULL.
+ */
+sll_lite_node_t *
+sll_lite_unlink_tail (sll_lite_list_t *list)
+{
+    sll_lite_node_t *node_p;
+
+    if ((list == NULL) || (list->tail_p == NULL)) {
+        return (NULL);
+    }
+
+    /* take the node at the tail and then remove it from the list */
+    node_p = list->tail_p;
+    if (sll_lite_remove(list, node_p) != SLL_LITE_RET_SUCCESS) {
+        /* some thing is wrong */
+        return (NULL);
+    }
+    return (node_p);
+}
+
+/**
+ * sll_lite_remove removes the given node from the list.
+ *
+ * @param[in]list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ * @param[in]node - pointer to the list node structure to be
+ *                  removed.
+ *
+ * @return        - SLL_LITE_RET_SUCCESS for success
+ *                - SLL_LITE_RET_INVALID_ARGS when arguments are
+ *                  invalid.
+ *                - SLL_LITE_RET_NODE_NOT_FOUND when the node
+ *                  to remove is not found.
+ */
+sll_lite_return_e
+sll_lite_remove (sll_lite_list_t *list, sll_lite_node_t *node)
+{
+    sll_lite_node_t *this_p, *prev_p;
+
+    if ((list == NULL) || (node == NULL)) {
+        return (SLL_LITE_RET_INVALID_ARGS);
+    }
+
+    prev_p = NULL;
+    this_p = list->head_p; /* starting from the head */
+
+    /*
+     * Find the node on the list and then remove it from
+     * the list.
+     */
+    while (this_p != NULL) {
+        if (this_p == node) {
+            break;
+        }
+        prev_p = this_p;
+        this_p = this_p->next_p;
+    }
+
+    if (this_p != NULL) {
+       /* found the element */
+       if (prev_p == NULL) {
+           /* the node to remove is the head */
+           list->head_p = list->head_p->next_p;
+           if (list->tail_p == this_p) {
+               /* this the only node on the list */
+               list->tail_p = list->head_p;
+           }
+       } else {
+           /* the element is in the middle of the chain */
+           prev_p->next_p = this_p->next_p;
+           if (list->tail_p == this_p) {
+               /* the node to remove is the last node */
+               list->tail_p = prev_p;
+           }
+       }
+       list->count--;
+       node->next_p = NULL;
+       return (SLL_LITE_RET_SUCCESS);
+    }
+    return (SLL_LITE_RET_NODE_NOT_FOUND);
+}
diff --git a/libs/sipcc/core/src-common/string_lib.c b/libs/sipcc/core/src-common/string_lib.c
new file mode 100755 (executable)
index 0000000..b3db123
--- /dev/null
@@ -0,0 +1,386 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define __STRINGLIB_INTERNAL__
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "cpr_stddef.h"
+#include "cpr_locks.h"
+#include "string_lib.h"
+#include "phone_debug.h"
+#include "debug.h"
+
+
+#define STRING_SIGNATURE (('S'<<8)|'T')
+#define STR_TO_STRUCT(str) ((string_block_t *)((str) - (offsetof(string_block_t,data))))
+#define STRUCT_TO_STR(sbt) ((const char *) (sbt)->data)
+
+static string_t empty_str;
+static int strlib_is_string(string_t str);
+
+/*
+ *  Function: strlib_malloc
+ *
+ *  PARAMETERS:const char* : string which is to be malloc'ed
+               int         : length of string or -1
+ *
+ *  DESCRIPTION:strlib_malloc : creates a new string and returns a const char*
+ *  to the new string. Size of String is equal to length specified or actual
+ *  length of the string when length is specified as -1(LEN_UNKNOWN)
+ *
+ *  RETURNS: Pointer to malloc'ed string
+ *
+ */
+string_t
+strlib_malloc (const char *str, int length, const char *fname, int line)
+{
+    string_block_t *temp;
+    int size;
+
+    // if specified length is unknown or invalid... then calculate it
+    // Length < 0 is not expected, but since length is an int, it could
+    // theoritically be negative.  [ This check accounts for that, and
+    // avoids a static analysis warning related to same ]
+    if ((length == LEN_UNKNOWN) || (length < 0)) {
+        length = strlen(str);
+    }
+
+    size = sizeof(string_block_t) + length + 1;
+    temp = (string_block_t *) cpr_malloc(size);
+
+    if (!temp) {
+        err_msg("Error: Strlib_Malloc() Failed. Requested Size = %d\n", size);
+        return (string_t) 0;
+    }
+
+    temp->refcount  = 1;
+    temp->length    = (uint16_t) length;
+    temp->signature = STRING_SIGNATURE;
+    temp->fname     = fname;
+    temp->line      = line;
+    /* There used to be memcpy here which will walk off the end of */
+    /* str pointer which is a bad thing to do */
+    sstrncpy(temp->data, str, length + 1);
+    temp->data[length] = '\0';
+
+    return STRUCT_TO_STR(temp);
+}
+
+
+/*
+ *  Function: strlib_copy
+ *
+ *  PARAMETERS:string_t : string whose ref count is to be incremented
+ *
+ *  DESCRIPTION:just increments the reference count of the string.
+ *  It is not like conventional strcpy as far as implementation
+ *  is concerned, but effectivly when user calls strlib_copy he gets
+ *  private copy which cannot be modified by any other function.
+ *  String should be modified only by calling strlib_open
+ *  which checks for the refcount and if its more than 1 allocates new string.
+ *
+ *  RETURNS: string_t: string whose ref count is  incremented
+ *
+ */
+string_t
+strlib_copy (string_t str)
+{
+    string_block_t *temp;
+
+    if (!strlib_is_string(str)) {
+        return (NULL);
+    }
+
+    temp = STR_TO_STRUCT(str);
+
+    /*
+     * Refcount is limited to USHRT_MAX since refcount is
+     * of type uint16_t.
+     */
+    if ((temp->refcount < 0xffff) && (str != empty_str)) {
+        temp->refcount++;
+    }
+
+    return STRUCT_TO_STR(temp);
+}
+
+
+/*
+ *  Function: strlib_update
+ *
+ *  PARAMETERS:string_t    : destination string
+ *             const char* : source string
+ *
+ *  DESCRIPTION:like strcpy but returns const char* to a string in pool
+ *
+ *  RETURNS: string_t:Pointer to a new string
+ *
+ */
+string_t
+strlib_update (string_t destination, const char *source,
+              const char *calling_fname, int line)
+{
+    const char *fname = "strlib_udpate";
+    string_t ret_str;
+
+    /* Bogus destination */
+    if (!destination) {
+        /* Should never happen, so report it */
+        err_msg("%s: Destination String is invalid: %s:%d", fname,
+                calling_fname, line);
+        /* bad input, bad output */
+        return NULL;
+    }
+
+    /* Bogus source */
+    if (!source) {
+        /* Should never happen, so report it and return something */
+        err_msg("%s: Source String is invalid: %s:%d", fname,
+                calling_fname, line);
+        strlib_free(destination);
+        return strlib_empty();
+    }
+
+    if (source[0] == '\0') {
+        /* Source is NULL string, use string empty */
+        strlib_free(destination);
+        return strlib_empty();
+    }
+
+    ret_str = strlib_malloc(source, LEN_UNKNOWN, calling_fname, line);
+
+    if (!ret_str) {
+        /*
+         * If a malloc error occurred, give them back what they had.
+         * It's not right, but it's better than nothing.
+         */
+        ret_str = destination;
+    } else {
+        strlib_free(destination);
+    }
+
+    return (ret_str);
+}
+
+
+/*
+ *  Function: strlib_append
+ *
+ *  PARAMETERS: string_t    : oringinal string
+ *              const char* : to be appended string
+ *
+ *  DESCRIPTION:this function will append a string to the original string,
+ *  makes a duplicate of the string and returns it.
+ *
+ *  RETURNS: Appended string (string_t) or NULL if failure
+ *
+ */
+string_t
+strlib_append (string_t str, const char *toappend_str,
+              const char *fname, int line)
+{
+    int curlen;
+    char *buf;
+
+    /* Should never happen */
+    if (!str) {
+        return NULL;
+    }
+
+    curlen = strlen(str);
+    /* not really an open, rather more like modify */
+    buf = strlib_open(str, curlen + strlen(toappend_str) + 1, fname, line);
+    /*
+     * Validate returned value, but buf could be equal to str (buf == str)
+     * so there still may be issues.
+     */
+    if (buf) {
+        strcpy(buf + curlen, toappend_str);
+        return buf;
+    }
+    return NULL;
+}
+
+
+/*
+ *  Function: strlib_free
+ *
+ *  PARAMETERS: string_t : string to which reference is to be removed
+ *
+ *  DESCRIPTION:It will remove the node containing the string from the linked
+ *  list if its refcount is 0 else it will decrement the refcount.
+ *
+ *  RETURNS: none
+ *
+ */
+void
+strlib_free (string_t str)
+{
+    string_block_t *temp;
+
+    if ((!strlib_is_string(str)) || (str == empty_str)) {
+        return;
+    }
+
+    temp = STR_TO_STRUCT(str);
+    temp->refcount--;
+    if (temp->refcount == 0) {
+        temp->signature = 0;
+        cpr_free(temp);
+    }
+
+    return;
+}
+
+
+/*
+ *  Function: strlib_open
+ *
+ *  PARAMETERS: string_t : string to be modified
+ *              int      : length of string to be modified
+ *
+ *  DESCRIPTION:User will call open when he wants to modify the string. If
+ *  length of string after modification is going to be = < original string
+ *  return to him pointer to original string, else a new string is malloced
+ *  for user and a pointer to it is send to him.
+ *
+ *  RETURNS: char* to modifiable string
+ *
+ */
+char *
+strlib_open (string_t str, int length, const char *fname, int line)
+{
+    char *ret_str;
+    string_block_t *temp;
+
+    if (!strlib_is_string(str)) {
+        return (NULL);
+    }
+
+    temp = STR_TO_STRUCT(str);
+
+    if ((temp->refcount == 1) && (length <= temp->length)) {
+        ret_str = (char *) str;
+    } else {
+        ret_str = (char *) strlib_malloc(str, length, fname, line);
+        if (!ret_str) {
+            /*
+             * If a malloc error occurred, give them back what they had.
+             * It's not right, but it's better than nothing.
+             */
+            ret_str = (char *) str;
+        } else {
+            strlib_free(str);
+        }
+    }
+
+    return (ret_str);
+}
+
+
+/*
+ *  Function: strlib_close
+ *
+ *  PARAMETERS:char* : string to be made unmodifiable
+ *
+ *  DESCRIPTION:Just returns const char* for the string, so that user
+ *  does not change it by mistake
+ *
+ *  RETURNS:string_t: pointer to same string
+ *
+ */
+string_t
+strlib_close (char *str)
+{
+    if (!strlib_is_string(str)) {
+        return (NULL);
+    }
+    return (str);
+}
+
+
+/*
+ *  Function: strlib_is_string
+ *
+ *  PARAMETERS: string_t : string which is to be validated
+ *
+ *  DESCRIPTION:Checks whether the signature for the provided string is valid
+ *
+ *  RETURNS: static int
+ *
+ */
+int
+strlib_is_string (string_t str)
+{
+    string_block_t *temp;
+
+    if (str == NULL) {
+        err_msg("Strlib Error: strlib_is_tring passed invalid string\n");
+        return (0);
+    }
+
+    temp = STR_TO_STRUCT(str);
+
+    if (temp->signature == STRING_SIGNATURE) {
+        return (1);
+    } else {
+        err_msg("Strlib Error: strlib_is_tring passed invalid string\n");
+        return (0);
+    }
+
+}
+
+int
+strlib_test_memory_is_string (void *mem)
+{
+    string_block_t *temp;
+
+    if (!mem) {
+        return FALSE;
+    }
+
+    temp = (string_block_t *) mem;
+    if (temp->signature == STRING_SIGNATURE) {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+
+/*
+ *  Function: strlib_empty
+ *
+ *  PARAMETERS: None
+ *
+ *  DESCRIPTION:strlib_empty will be called by user when he wants
+ *  to initialize his string to '/0' or "". It is not same as initializing
+ *  it to NULL. if(str) will evaluate to true if str = strlib_empty().
+ *  Correct way to check for empty string will be to do if (str[0] == '\0') or
+ *  if (str[0] == "")
+ *
+ *  RETURNS: Pointer to empty_str
+ */
+string_t
+strlib_empty (void)
+{
+    string_block_t *temp;
+    static boolean empty_str_init = FALSE;
+
+    if (empty_str_init == FALSE) {
+        empty_str = strlib_malloc("", LEN_UNKNOWN, __FILE__, __LINE__);
+        temp = STR_TO_STRUCT(empty_str);
+        temp->refcount = 0xffff;
+        empty_str_init = TRUE;
+    }
+    return (empty_str);
+}
+
+void
+strlib_init (void)
+{
+  (void) strlib_empty(); // force it to allocate the empty string buffer
+}
diff --git a/libs/sipcc/core/src-common/util_ios_queue.c b/libs/sipcc/core/src-common/util_ios_queue.c
new file mode 100644 (file)
index 0000000..694efd4
--- /dev/null
@@ -0,0 +1,215 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "util_ios_queue.h"
+
+/* Forward function declarations */
+void enqueue_inline(queuetype *qptr, void *eaddr);
+void *dequeue_inline(queuetype *qptr);
+void unqueue_inline(queuetype *q, void *e);
+void requeue_inline(queuetype *qptr, void *eaddr);
+
+/* Type definitions */
+#define validmem(x) x
+
+/*
+ * queue_init
+ * Initialize a queuetype
+ */
+void
+queue_init (queuetype * q, int maximum)
+{
+    q->qhead = NULL;
+    q->qtail = NULL;
+    q->count = 0;
+    q->maximum = maximum;
+}
+
+#ifdef MGCP_FIXME
+
+/*
+ * peekqueuehead -- Return address of element at head of queue.
+ * Does not lock out interrupts.
+ */
+void *
+peekqueuehead (queuetype *q)
+{
+    nexthelper *p;
+
+    p = (nexthelper *) q->qhead;    /* first member */
+    return (p);
+
+}
+#endif
+
+/*
+ * enqueue - add an element to a fifo queue.
+ * Note no interrupt interlocking.
+ */
+void
+enqueue (queuetype *qptr, void *eaddr)
+{
+    nexthelper *node;
+
+    node = (nexthelper *) eaddr;
+    node->next = NULL;
+    enqueue_inline(qptr, (void *)node);
+}
+
+/*
+ * dequeue - remove first element of a fifo queue.
+ * Note no interrupt interlocking.
+ */
+void *
+dequeue (queuetype *qptr)
+{
+    return (dequeue_inline(qptr));
+}
+
+/*
+ * enqueue_inline - add an element to a fifo queue.
+ */
+void
+enqueue_inline (queuetype *qptr, void *eaddr)
+{
+    nexthelper *p, *ptr;
+
+    p = (nexthelper *) qptr->qtail; /* last element pointer */
+    ptr = (nexthelper *) eaddr;
+    /*
+     * Make sure the element isn't already queued or the last
+     * element in this list
+     */
+    if ((ptr->next != NULL) || (p == ptr)) {
+        err_msg("Queue: Error, queue corrupted %d %d\n", (long)qptr,
+                (long) eaddr);
+        return;
+    }
+    if (!p)                     /* q empty */
+        qptr->qhead = ptr;
+    else                        /* not empty */
+        p->next = ptr;          /* update link */
+    qptr->qtail = ptr;          /* tail points to new element */
+    qptr->count++;
+}
+
+/*
+ * dequeue_inline - remove first element of a fifo queue.
+ */
+void *
+dequeue_inline (queuetype * qptr)
+{
+    nexthelper *p;
+
+    if (qptr == NULL)
+        return (NULL);
+    p = (nexthelper *) qptr->qhead; /* points to head of queue */
+    if (p) {                    /* is there a list? */
+        qptr->qhead = p->next;  /* next link */
+        if (!p->next) {
+            qptr->qtail = NULL; /* this was last guy, so zap tail */
+        }
+        p->next = NULL;         /* clear link, just in case */
+    }
+    if (p && (--qptr->count < 0) && qptr->maximum) {
+        err_msg("Queue: Error, queue count under or over %d\n", qptr->count);
+        qptr->count = 0;
+    }
+    return (p);
+}
+
+#ifdef MGCP_FIXME
+/*
+ * queue_create_init initializes the queue structure
+ *
+ * queue_ptr_t *queue: pointer to pointer to the queue structure
+ * The function returns TRUE if the queue can be initialized, FALSE otherwise
+ */
+boolean
+queue_create_init (queue_ptr_t *queue)
+{
+    if (queue) {
+        *queue = cpr_malloc(sizeof(queuetype));
+        if (*queue == NULL)
+            return FALSE;
+
+        queue_init((queuetype *) *queue, 0);
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
+/*
+ * queue_delete deletes the queue structure initialized by
+ * queue_create_init previously
+ *
+ * queue_ptr_t queue: pointer to the queue structure
+ * The function does not return any value
+ */
+void
+queue_delete (queue_ptr_t queue)
+{
+    if (queue) {
+        cpr_free((queuetype *) queue);
+    }
+}
+
+
+/*
+ *
+ * add_list add a node to the head of the queue
+ * queue_ptr_t queue: pointer to the queue structure
+ * node_ptr_t node: node to add to the queue
+ * The function does not return any value
+ */
+void
+add_list (queue_ptr_t queue, node_ptr_t node)
+{
+    if (queue && node) {
+        node->next = NULL;
+        enqueue_inline((queuetype *) queue, node);
+    }
+}
+
+/*
+ * get_node returns a node from the queue. The head of the queue is
+ * returned if the argument node is pointing to NULL, or the node next to it is
+ * returned if it is pointing to non-NULL
+ *
+ * queue_ptr_t queue: pointer to the queue structure
+ * node_ptr_t node: pointer to pointer to a node in the queue if the next
+ * node is to be returned, other wise pointer to NULL to return the head of
+ * the queue
+ * The function returns a node from the queue in the argument node and
+ * return value GET_NODE_SUCCESS, or GET_NODE_FAIL_EMPTY_LIST if list
+ * is empty, or GET_NODE_FAIL_END_OF_LIST if end of list is reached
+ */
+get_node_status
+get_node (queue_ptr_t queue, node_ptr_t *node)
+{
+    if (queue && node) {
+        if (!*node) {
+            /* get from head of the queue */
+            *node = peekqueuehead(queue);
+            if (!*node)
+                return GET_NODE_FAIL_EMPTY_LIST;
+            else
+                return GET_NODE_SUCCESS;
+        } else {
+            *node = (*node)->next;
+            if (!*node)
+                return GET_NODE_FAIL_END_OF_LIST;
+            else
+                return GET_NODE_SUCCESS;
+        }
+    } else {
+        return GET_NODE_FAIL_EMPTY_LIST;
+    }
+}
+#endif
diff --git a/libs/sipcc/core/src-common/util_parse.c b/libs/sipcc/core/src-common/util_parse.c
new file mode 100644 (file)
index 0000000..59b5325
--- /dev/null
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_string.h"
+#include "xml_defs.h"
+
+XMLToken
+parse_xml_tokens (char **parseptr, char *value, int maxlen)
+{
+    char *input = *parseptr;
+
+    for (;;) {
+        if (*input == '\0') {
+            return TOK_EOF;
+        } else if (isspace(*input)) {
+            input++;
+        } else if (input[0] == '<') {
+            input++;
+            if (input[0] == '/') {
+                *parseptr = input + 1;
+                return TOK_ENDLBRACKET;
+            }
+            /* A starting bracket */
+            if (input[0] != '!') {
+                *parseptr = input;
+                return TOK_LBRACKET;
+            }
+            /* find closing bracket */
+            while ((*input != '\0') && (*input != '>')) {
+                input++;
+            }
+            /* Throw away the closing bracket for the comment */
+            if (input[0] == '>') {
+                input++;
+            }
+        } else if ((input[0] == '/') && (input[1] == '>')) {
+            *parseptr = input + 2;
+            return TOK_EMPTYBRACKET;
+        } else if (input[0] == '>') {
+            *parseptr = input + 1;
+            return TOK_RBRACKET;
+        } else if (input[0] == '=') {
+            *parseptr = input + 1;
+            return TOK_EQ;
+        } else if (input[0] == '"') {
+            char *endquote;
+            int len;
+
+            input++;
+            endquote = strchr(input, '"');
+            if (endquote == NULL) {
+                *parseptr = input + strlen(input);
+                return TOK_EOF;
+            }
+            len = endquote - input;
+            if (len >= maxlen) {
+                len = maxlen - 1;
+            }
+            memcpy(value, input, len);
+            value[len] = 0;
+            *parseptr = endquote + 1;
+            return TOK_STR;
+        } else if (isalnum(input[0]) || (input[0] == '{')) {
+            char *endtok = input + 1;
+            int len;
+
+            while (isalnum(endtok[0]) || (endtok[0] == '-')
+                   || (endtok[0] == '}')) {
+                endtok++;
+            }
+            len = endtok - input;
+            if (len >= maxlen) {
+                len = maxlen - 1;
+            }
+            memcpy(value, input, len);
+            value[len] = 0;
+            *parseptr = endtok;
+            return TOK_KEYWORD;
+        } else {
+            *parseptr = input + 1;
+            return TOK_ERR;
+        }
+    }
+}
diff --git a/libs/sipcc/core/src-common/util_string.c b/libs/sipcc/core/src-common/util_string.c
new file mode 100644 (file)
index 0000000..5c4c9dc
--- /dev/null
@@ -0,0 +1,366 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+#include <limits.h>
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_stdlib.h"
+#include "cpr_socket.h"
+#include "cpr_in.h"
+#include "util_string.h"
+/*
+ *  Conver IP address into dotted format or colon format.
+ *
+ *  @param addr_str : string to hold IP
+ *         addr     : pointer to the IP address
+ *
+ *  @return  none
+ *
+ *  @pre     none
+ *
+ */
+
+void
+ipaddr2dotted (char *addr_str, cpr_ip_addr_t *addr)
+{
+    if (addr_str)
+    {
+        switch (addr->type) {
+        case CPR_IP_ADDR_IPV4 :
+        sprintf(addr_str, "%u.%u.%u.%u",
+                (addr->u.ip4 >> 24) & 0xFF,
+                (addr->u.ip4 >> 16) & 0xFF,
+                (addr->u.ip4 >> 8)  & 0xFF,
+                (addr->u.ip4)       & 0xFF);
+            break;
+        case CPR_IP_ADDR_IPV6:
+        sprintf(addr_str, "[%x:%x:%x:%x:%x:%x:%x:%x]",
+                (addr->u.ip6.addr.base16[7]),
+                (addr->u.ip6.addr.base16[6]),
+                (addr->u.ip6.addr.base16[5]),
+                (addr->u.ip6.addr.base16[4]),
+                (addr->u.ip6.addr.base16[3]),
+                (addr->u.ip6.addr.base16[2]),
+                (addr->u.ip6.addr.base16[1]),
+                (addr->u.ip6.addr.base16[0]));
+
+            break;
+        default:
+            break;
+        }
+
+
+    }
+}
+
+/*
+ *  Convert IP address to appropriate format dotted,or with colon
+ *
+ *  @param addr_str ip address string
+ *
+ *  @return  address
+ *
+ *  @pre      none
+ *
+ */
+
+uint32_t
+dotted2ipaddr (const char *addr_str)
+{
+    uint32_t    address = 0;
+    const char *p = NULL;
+    char        section_str[3];
+    int         sections[4];
+    int         section;
+    int         i;
+    long        strtoul_result;
+    char       *strtoul_end;
+
+    /* Check args */
+    if ((!addr_str) || (addr_str[0] == '\0'))
+    {
+        return 0xFFFFFFFF;
+    }
+
+    /* Init */
+    for (i = 0; i < 4; i++)
+    {
+        sections[i] = 0;
+    }
+    p = addr_str;
+
+    /* Remove leading blanks */
+    while ((*p == ' ') || (*p == '\t') || (*p == '\n') || (*p == '\r'))
+    {
+        p++;
+    }
+
+    for (section = 0; section < 4; section++)
+    {
+        i = 0;
+        section_str[0] = '\0';
+        section_str[1] = '\0';
+        section_str[2] = '\0';
+        while ((*p != '.') && (i < 3)) {
+            section_str[i] = *p;
+            i++;
+            p++;
+        }
+
+        errno = 0;
+        strtoul_result = strtoul(section_str, &strtoul_end, 10);
+
+        if (errno || section_str == strtoul_end ||
+            strtoul_result > 255) {
+            return 0xFFFFFFFF;
+        }
+
+        sections[section] = (int) strtoul_result;
+
+        address = address | (sections[section]<<((3-section)*8));
+        p++;
+    }
+
+    return address;
+}
+
+/*
+ * ntohl function wrapper for both IPv4 and Ipv6
+ *
+ *  @param ip_addr_in, ip_addr_out : IP addresses
+ *
+ *  @return  none
+ *
+ *  @pre none
+ *
+ */
+
+void util_ntohl (cpr_ip_addr_t *ip_addr_out, cpr_ip_addr_t *ip_addr_in)
+{
+    int i,j;
+    unsigned char tmp;
+
+    ip_addr_out->type = ip_addr_in->type;
+
+    if (ip_addr_in->type == CPR_IP_ADDR_IPV4) {
+
+        ip_addr_out->u.ip4 = ntohl(ip_addr_in->u.ip4);
+
+    } else {
+        //to do IPv6: Add ntohl functionality for ipv6.
+
+        if (ip_addr_out == ip_addr_in) {
+            for (i=0, j=15; i<8; i++, j--) {
+                tmp  = ip_addr_out->u.ip6.addr.base8[j];
+                ip_addr_out->u.ip6.addr.base8[j] = ip_addr_in->u.ip6.addr.base8[i];
+                ip_addr_in->u.ip6.addr.base8[i] = tmp;
+            }
+        } else {
+            for (i=0, j=15; i<16; i++, j--) {
+                ip_addr_out->u.ip6.addr.base8[j] = ip_addr_in->u.ip6.addr.base8[i];
+            }
+        }
+    }
+}
+
+/*
+ *  Check if the IP address passed is valid. For IPv4& IPv6 check if value 0 if so
+ *   return FALSE. If the type is CPR_IP_ADDR_INVALID or any other non defined type
+ *   return FALSE.
+ *
+ *
+ *  @param ip_addr  IP address
+ *
+ *  @return  boolean False if not a valid IP, or else true
+ *
+ *  @pre     none
+ *
+ */
+
+boolean util_check_if_ip_valid (cpr_ip_addr_t *ip_addr)
+{
+    if (ip_addr->type == CPR_IP_ADDR_INVALID) {
+        return(FALSE);
+    }
+    if (ip_addr->type == CPR_IP_ADDR_IPV4 &&
+        ip_addr->u.ip4 == 0) {
+
+        return(FALSE);
+    }
+
+    if ((ip_addr->type == CPR_IP_ADDR_IPV6) &&
+        (ip_addr->u.ip6.addr.base16[7] == 0) &&
+        (ip_addr->u.ip6.addr.base16[6] == 0) &&
+        (ip_addr->u.ip6.addr.base16[5] == 0) &&
+        (ip_addr->u.ip6.addr.base16[4] == 0) &&
+        (ip_addr->u.ip6.addr.base16[3] == 0) &&
+        (ip_addr->u.ip6.addr.base16[2] == 0) &&
+        (ip_addr->u.ip6.addr.base16[1] == 0) &&
+        (ip_addr->u.ip6.addr.base16[0] == 0)) {
+
+        return(FALSE);
+    }
+
+    if ((ip_addr->type != CPR_IP_ADDR_INVALID) &&
+        (ip_addr->type != CPR_IP_ADDR_IPV4) &&
+        (ip_addr->type != CPR_IP_ADDR_IPV6)) {
+
+        return(FALSE);
+    }
+
+    return(TRUE);
+}
+
+/*
+ *  Compare 2 IP addresses
+ *
+ *  @param ip_addr1 & ip_addr2 2 ip addresses to compare
+ *
+ *  @return  true if that matches or else false
+ *
+ *  @pre     (ip_addr1 != NULL) && (ip_addr2 != NULL)
+ *
+ */
+
+boolean util_compare_ip (cpr_ip_addr_t *ip_addr1, cpr_ip_addr_t *ip_addr2)
+{
+    if (ip_addr1->type != ip_addr2->type) {
+
+        return(FALSE);
+    }
+
+    if (ip_addr1->type == CPR_IP_ADDR_IPV4 &&
+        ip_addr2->type == CPR_IP_ADDR_IPV4) {
+
+        return((boolean) (ip_addr1->u.ip4 == ip_addr2->u.ip4));
+
+    } else if (ip_addr1->type == CPR_IP_ADDR_IPV6 &&
+            ip_addr2->type == CPR_IP_ADDR_IPV6) {
+
+        return((boolean)memcmp((void *)&(ip_addr1->u.ip6.addr.base8),
+                    (void *)&(ip_addr2->u.ip6.addr.base8), 16));
+    }
+
+    return(FALSE);
+}
+
+/*
+ *  Extract ip address information from socket storage structure
+ *
+ *  @param ip_addr  ip address to be filled.
+ *          cpr_sockaddr_storage storage structure pointer
+ *
+ *  @return  none
+ *
+ *  @pre     (ip_addr != NULL)
+ *
+ */
+void util_extract_ip (cpr_ip_addr_t *ip_addr,
+                        cpr_sockaddr_storage *from)
+{
+    switch (from->ss_family) {
+    case AF_INET6:
+        ip_addr->type = CPR_IP_ADDR_IPV6;
+        ip_addr->u.ip6 = ((cpr_sockaddr_in6_t *)from)->sin6_addr;
+        break;
+    case AF_INET:
+        ip_addr->type = CPR_IP_ADDR_IPV4;
+        ip_addr->u.ip4 = ((cpr_sockaddr_in_t *)from)->sin_addr.s_addr;
+        break;
+    default:
+        break;
+    }
+}
+
+/*
+ *  Get port number from storage structure.
+ *
+ *  @param sock_storage
+ *
+ *  @return  port number
+ *
+ *  @pre     (sock_storage != NULL)
+ *
+ */
+
+uint16_t util_get_port (cpr_sockaddr_storage *sock_storage)
+{
+    switch (sock_storage->ss_family) {
+    case AF_INET6:
+        return(((cpr_sockaddr_in6_t *)sock_storage)->sin6_port);
+    case AF_INET:
+        return(((cpr_sockaddr_in_t *)sock_storage)->sin_port);
+    default:
+        break;
+    }
+    return(0);
+}
+
+void util_get_ip_using_mode (cpr_ip_addr_t *ip_addr, cpr_ip_mode_e ip_mode,
+                             uint32_t  ip4, char *ip6)
+{
+    *ip_addr = ip_addr_invalid;
+
+    switch (ip_mode) {
+    case CPR_IP_MODE_IPV4:
+        ip_addr->type = CPR_IP_ADDR_IPV4;
+        ip_addr->u.ip4 = ip4;
+        break;
+    case CPR_IP_MODE_IPV6:
+        ip_addr->type = CPR_IP_ADDR_IPV6;
+        if (ip6 != NULL) {
+            memcpy((void *)&(ip_addr->u.ip6.addr.base8[16]), (void *)ip6, 16);
+        }
+        break;
+    case CPR_IP_MODE_DUAL:
+    default:
+        break;
+    }
+
+}
+
+unsigned long
+gmt_string_to_seconds (char *gmt_string, unsigned long *seconds)
+{
+    char *token;
+
+    // Do a preliminary check and see if the string
+    // passed in is a completely numeric string
+    // If it is, then we will return just that number
+    // and return a 1 for the error code, meaning
+    // we parsed a
+    *seconds = strtoul(gmt_string, &token, 10);
+    if ((token == NULL) || (token[0] == '\0')) {
+        return 1;
+    }
+    *seconds = 0;
+    return 0;
+}
+
+long
+diff_current_time (unsigned long t1, unsigned long *difference)
+{
+    *difference = 1;
+    return 0;
+}
+
+boolean
+is_empty_str (char *str)
+{
+    if (str == NULL) {
+        return TRUE;
+    }
+    if (strncmp(str, EMPTY_STR, EMPTY_STR_LEN) == 0) {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+void
+init_empty_str (char *str)
+{
+    strcpy(str,EMPTY_STR);
+}
diff --git a/libs/sipcc/cpr/android/cpr_android_align.h b/libs/sipcc/cpr/android/cpr_android_align.h
new file mode 100644 (file)
index 0000000..389f69f
--- /dev/null
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __CPR_ANDROID_ALIGN_H__
+#define __CPR_ANDROID_ALIGN_H__
+#include "cpr_types.h"
+
+/*
+ * Macros to determine how an address is aligned
+ */
+#define is_16bit_aligned(addr) (!((uint32_t)(addr) & 0x01))
+#define is_32bit_aligned(addr) (!((uint32_t)(addr) & 0x03))
+#define is_64bit_aligned(addr) (!((uint32_t)(addr) & 0x07))
+
+/*
+ * Macro to get a mask for a specified byte alignment
+ */
+#define ALIGN_MASK(alignment)  (~((alignment) - 1))
+
+/*
+ * Macro to set the minimum alignment
+ */
+#define ALIGN_MIN(align,align_min) (((align) > (align_min)) ? (align) : (align_min))
+
+/*
+ * Macro to round up or down "val" to be a multiple of "align", assuming
+ * "align" is a power of 2. if "align" is zero then no action will
+ * be performed
+ */
+#ifdef __typeof__
+#define ALIGN_UP(val,align)   ((align == 0) ? (val) : (__typeof__(val))(((uint32_t)(val)  +  ((align) - 1)) & ~((align) - 1)))
+#define ALIGN_DOWN(val,align) ((align == 0) ? (val) : (__typeof__(val))(((uint32_t)(val)) & ~((align) - 1)))
+#else
+#define ALIGN_UP(val,align)   ((align == 0) ? (val) : (uint32_t)(((uint32_t)(val)  +  ((align) - 1)) & ~((align) - 1)))
+#define ALIGN_DOWN(val,align) ((align == 0) ? (val) : (uint32_t)(((uint32_t)(val)) & ~((align) - 1)))
+#endif
+
+/**
+ * Macro to safely write 4 bytes based on memory alignment setting
+ */
+#ifndef CPR_MEMORY_LITTLE_ENDIAN
+#define WRITE_4BYTES_UNALIGNED(mem, value) \
+    { ((char *)mem)[0] = (uint8_t)(((value) & 0xff000000) >> 24); \
+      ((char *)mem)[1] = (uint8_t)(((value) & 0x00ff0000) >> 16); \
+      ((char *)mem)[2] = (uint8_t)(((value) & 0x0000ff00) >>  8); \
+      ((char *)mem)[3] = (uint8_t) ((value) & 0x000000ff);       \
+    }
+#else
+#define WRITE_4BYTES_UNALIGNED(mem, value) \
+    { ((char *)mem)[3] = (uint8_t)(((value) & 0xff000000) >> 24); \
+      ((char *)mem)[2] = (uint8_t)(((value) & 0x00ff0000) >> 16); \
+      ((char *)mem)[1] = (uint8_t)(((value) & 0x0000ff00) >>  8); \
+      ((char *)mem)[0] = (uint8_t) ((value) & 0x000000ff);       \
+    }
+#endif
+/**
+ * Macro to safely read 4 bytes based on memory alignment setting
+ */
+#ifndef CPR_MEMORY_LITTLE_ENDIAN
+#ifdef __typeof__
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    __typeof__(value)((((uint8_t *)mem)[0] << 24) | (((uint8_t *)mem)[1] << 16) | \
+                      (((uint8_t *)mem)[2] <<  8) |  ((uint8_t *)mem)[3])
+#else
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    (uint32_t)((((uint8_t *)mem)[0] << 24) | (((uint8_t *)mem)[1] << 16) | \
+               (((uint8_t *)mem)[2] <<  8) |  ((uint8_t *)mem)[3])
+#endif
+#else
+#ifdef __typeof__
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    __typeof__(value)((((uint8_t *)mem)[3] << 24) | (((uint8_t *)mem)[2] << 16) | \
+                      (((uint8_t *)mem)[1] <<  8) |  ((uint8_t *)mem)[0])
+#else
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    (uint32_t)((((uint8_t *)mem)[3] << 24) | (((uint8_t *)mem)[2] << 16) | \
+               (((uint8_t *)mem)[1] <<  8) |  ((uint8_t *)mem)[0])
+#endif
+#endif
+
+#endif /* __CPR_ANDROID_ALIGN_H__ */
diff --git a/libs/sipcc/cpr/android/cpr_android_assert.h b/libs/sipcc/cpr/android/cpr_android_assert.h
new file mode 100644 (file)
index 0000000..7321de0
--- /dev/null
@@ -0,0 +1,85 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_ASSERT_H_
+#define _CPR_ANDROID_ASSERT_H_
+
+#include "assert.h"
+
+/*--------------------------------------
+ *
+ * Macros
+ *
+ */
+
+/**
+ * CPR assert macro which calls cpr_assert_msg instead of abort
+ *
+ * The macro is dependent on the setting of FILE_ID which is used
+ * to override the __FILE__ setting.  For certain compilers, i.e.
+ * read 'Diab 4.4b', the __FILE__ is set to a Windows type path
+ * name which can contain backslashes that can cause odd output.
+ */
+#ifdef FILE_ID
+#define cpr_assert(expr) \
+    ((expr) ? (void)0 : cpr_assert_msg(FILE_ID, __LINE__, #expr))
+#else
+#define cpr_assert(expr) \
+    ((expr) ? (void)0 : cpr_assert_msg(__FILE__, __LINE__, #expr))
+#endif
+
+#define cpr_assert_debug(expr)
+
+/*
+ * A side note if somehow concerned about performance.
+ *
+ * This method will pre-render the string via the compiler,
+ * but will use more space due to larger strings.  Basically,
+ * good for speed and bad for memory.
+ *
+ * This is coded mostly as an example so if performance was an issue
+ * that the asserts could be low impact.
+ *
+ * #define cpr_assert_debug(expr) \
+ *   ((expr) ? (void)0 : cpr_assert_msg( \
+ *             __FILE__ ": line " __LINE__ ": assertion failed: " #expr))
+ *
+ * Note that this is not allowed when using __STRING_ANSI__
+ */
+
+#define cpr_assert_debug_rtn(expr)
+
+/*--------------------------------------
+ *
+ * Structures
+ *
+ */
+
+/**
+ * CPR assert modes of operation
+ */
+typedef enum {
+    CPR_ASSERT_MODE_NONE,            /**< Off, no message ouput          */
+    CPR_ASSERT_MODE_WARNING_LIMITED, /**< Warnings to syslog are limited */
+    CPR_ASSERT_MODE_WARNING_ALL,     /**< All warnings sent to syslog    */
+    CPR_ASSERT_MODE_ABORT            /**< Assert failure will call abort */
+} cpr_assert_mode_e;
+
+
+/*--------------------------------------
+ *
+ * Globals
+ *
+ */
+extern uint32_t cpr_assert_count;
+
+/*--------------------------------------
+ *
+ * Prototypes
+ *
+ */
+void
+cpr_assert_msg(const char *file, const int line, const char *expression);
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_errno.c b/libs/sipcc/cpr/android/cpr_android_errno.c
new file mode 100644 (file)
index 0000000..3a593de
--- /dev/null
@@ -0,0 +1,184 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_errno.h"
+#include <errno.h>
+
+/**
+ * @addtogroup OSAPIs The CPR OS Abstractions
+ * @brief Misc OS API Abstractions in CPR
+ *
+ * @{
+ */
+static int8_t errno_table[] =
+{
+    CPR_EPERM,
+    CPR_ENOENT,
+    CPR_ESRCH,
+    CPR_EINTR,
+    CPR_EIO,
+    CPR_ENXIO,
+    CPR_E2BIG,
+    CPR_ENOEXEC,
+    CPR_EBADF,
+    CPR_ECHILD,
+    CPR_EAGAIN, /*10*/
+    CPR_ENOMEM,
+    CPR_EACCES,
+    CPR_EFAULT,
+    CPR_ENOTBLK,
+    CPR_EBUSY,
+    CPR_EEXIST,
+    CPR_EXDEV,
+    CPR_ENODEV,
+    CPR_ENOTDIR,
+    CPR_EISDIR,/*20*/
+    CPR_EINVAL,
+    CPR_ENFILE,
+    CPR_EMFILE,
+    CPR_ENOTTY,
+    CPR_ETXTBSY,
+    CPR_EFBIG,
+    CPR_ENOSPC,
+    CPR_ESPIPE,
+    CPR_EROFS,
+    CPR_EMLINK,/*30*/
+    CPR_EPIPE,
+    CPR_EDOM,
+    CPR_ERANGE,
+    CPR_ENOMSG,
+    CPR_EIDRM,
+    CPR_ECHRNG,
+    CPR_EL2NSYNC,
+    CPR_EL3HLT,
+    CPR_EL3RST,
+    CPR_ELNRNG,/*40*/
+    CPR_EUNATCH,
+    CPR_ENOCSI,
+    CPR_EL2HLT,
+    CPR_EDEADLK,
+    CPR_ENOLCK,
+    CPR_ECANCELED,
+    CPR_ENOTSUP,
+    CPR_EDQUOT,
+    CPR_EBADE,
+    CPR_EBADR,
+    CPR_EXFULL,
+    CPR_ENOANO,
+    CPR_EBADRQC,
+    CPR_EBADSLT,
+    CPR_EDEADLOCK,
+    CPR_EBFONT,
+    CPR_UNKNOWN_ERR,            /* empty 58 */
+    CPR_UNKNOWN_ERR,            /* empty 59 */
+    CPR_ENOSTR,
+    CPR_ENODATA,
+    CPR_ETIME,
+    CPR_ENOSR,
+    CPR_ENONET,
+    CPR_ENOPKG,
+    CPR_EREMOTE,
+    CPR_ENOLINK,
+    CPR_EADV,
+    CPR_ESRMNT,
+    CPR_ECOMM,
+    CPR_EPROTO,
+    CPR_UNKNOWN_ERR,            /* empty 72 */
+    CPR_UNKNOWN_ERR,            /* empty 73 */
+    CPR_EMULTIHOP,
+    CPR_UNKNOWN_ERR,            /* empty 75 */
+    CPR_UNKNOWN_ERR,            /* empty 76 */
+    CPR_EBADMSG,
+    CPR_ENAMETOOLONG,
+    CPR_EOVERFLOW,
+    CPR_ENOTUNIQ,
+    CPR_EBADFD,
+    CPR_EREMCHG,
+    CPR_ELIBACC,
+    CPR_ELIBBAD,
+    CPR_ELIBSCN,
+    CPR_ELIBMAX,
+    CPR_ELIBEXEC,
+    CPR_EILSEQ,
+    CPR_ENOSYS,
+    CPR_ELOOP,
+    CPR_ERESTART,
+    CPR_ESTRPIPE,
+    CPR_ENOTEMPTY,
+    CPR_EUSERS,
+    CPR_ENOTSOCK,
+    CPR_EDESTADDRREQ,
+    CPR_EMSGSIZE,
+    CPR_EPROTOTYPE,
+    CPR_ENOPROTOOPT,
+    /* errno index goes from 99 to 120 */
+    CPR_EPROTONOSUPPORT,
+    CPR_ESOCKTNOSUPPORT,
+    CPR_EOPNOTSUPP,
+    CPR_EPFNOSUPPORT,
+    CPR_EAFNOSUPPORT,
+    CPR_EADDRINUSE,
+    CPR_EADDRNOTAVAIL,
+    CPR_ENETDOWN,
+    CPR_ENETUNREACH,
+    CPR_ENETRESET,
+    CPR_ECONNABORTED,
+    CPR_ECONNRESET,
+    CPR_ENOBUFS,
+    CPR_EISCONN,
+    CPR_ENOTCONN,
+    CPR_ECLOSED,
+    CPR_UNKNOWN_ERR,            /* empty 136 */
+    CPR_UNKNOWN_ERR,            /* empty 137 */
+    CPR_UNKNOWN_ERR,            /* empty 138 */
+    CPR_UNKNOWN_ERR,            /* empty 139 */
+    CPR_UNKNOWN_ERR,            /* empty 140 */
+    CPR_UNKNOWN_ERR,            /* empty 141 */
+    CPR_UNKNOWN_ERR,            /* empty 142 */
+    CPR_ESHUTDOWN,
+    CPR_ETOOMANYREFS,
+    CPR_ETIMEDOUT,
+    CPR_ECONNREFUSED,
+    CPR_EHOSTDOWN,
+    CPR_EHOSTUNREACH,
+    CPR_EALREADY,
+    CPR_EINPROGRESS,
+    CPR_ESTALE
+};
+
+/**
+ *
+ * @brief Translates to "cpr_errno" Macro
+ *
+ * pSIPCC uses the cpr_errno macro to print the errno
+ * for error conditions. This function is used to map the standard
+ * errno to standard CPR errors
+ *
+ * @return The CPR error number
+ *
+ */
+int16_t
+cprTranslateErrno (void)
+{
+    int16_t e = (int16_t) errno;
+
+    /*
+     * Verify against MIN and MAX errno numbers
+     */
+    if ((e < 1) || (e > 151)) {
+        return CPR_UNKNOWN_ERR;
+    } else if (e >= 120) {
+        e = e - 20;
+    } else if (e >= 100) {
+        /*
+         * In the gap from 100 to 119
+         */
+        return CPR_UNKNOWN_ERR;
+    }
+    return (int16_t) errno_table[e - 1];
+}
+
+/**
+  * @}
+  */
diff --git a/libs/sipcc/cpr/android/cpr_android_errno.h b/libs/sipcc/cpr/android/cpr_android_errno.h
new file mode 100644 (file)
index 0000000..095b799
--- /dev/null
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_ERRNO_H_
+#define _CPR_ANDROID_ERRNO_H_
+
+#include <errno.h>
+
+/*
+ * Maintain re-entrant nature by wrapping 'errno'
+ */
+/** @def cpr_errno is used by pSIPCC. MUST be defined by the CPR layer.
+  */
+#define cpr_errno cprTranslateErrno()
+
+int16_t cprTranslateErrno(void);
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_in.h b/libs/sipcc/cpr/android/cpr_android_in.h
new file mode 100644 (file)
index 0000000..b9a1d90
--- /dev/null
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_IN_H_
+#define _CPR_ANDROID_IN_H_
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_init.c b/libs/sipcc/cpr/android/cpr_android_init.c
new file mode 100644 (file)
index 0000000..8cdb017
--- /dev/null
@@ -0,0 +1,216 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *  @section intro_sec Introduction
+ *  CPR is an OS-abstraction layer which provides the functionality set of an OS
+ *  while hiding all the operational details from the applications. CPR also
+ *  provides additional functionality when certain operating-systems prove
+ *  deficient providing a consistent set of functionality for the applications
+ *  to use. The pSipCC library uses the CPR routines to achieve portablity. It
+ *  is the responsibility of any vendor requiring to port pSipCC into their
+ *  platforms to have the correct implementation of the CPR layer.
+ *
+ *  The CPR delivery includes two "archives/binary" for memory and string
+ *  related operations. These binaries contain functions that the pSIPCC uses
+ *  for it's functionality. The header files that contain the external functions
+ *  (APIs) from these binaries are also distributed.
+ *
+ *  @section sub_sec Functionality
+ *  CPR consists of a number of functional subsystems to achieve OS abstraction. The main
+ *  functionality provided by CPR to the pSIPCC library are
+ *  @li Memory Management - Provided as a binary that needs to be linked into CPR
+ *  @li Timers
+ *  @li Inter Process Communication - Sockets, Message Queues, Mutex, Semaphores
+ *  @li String Handling - Provided as a binary that needs to be linked into CPR
+ *  @li Thread Abstraction
+ *  @li Other Platform/OS Abstractions
+ *  @li Debug/Logging Abstraction
+ *
+ *  @section file_list EXTERNAL APIS
+ *   The External APIs that need to be exposed by CPR to the pSIPCC application are
+ *   defined in the following header files. The documentation within
+ *   each header file lists the functions/macros that @b NEED to be defined for
+ *   pSIPCC to work correctly. Example functions (and an implementation for
+ *   Linux) is available for most functions defined in these headers. Look under
+ *   the "Modules" tab to find the various subsystems and associated APIs and
+ *   helper functions.
+ *   @li cpr_debug.h
+ *   @li cpr_errno.h
+ *   @li cpr_in.h
+ *   @li cpr_locks.h
+ *   @li cpr_rand.h
+ *   @li cpr_socket.h
+ *   @li cpr_stdio.h
+ *   @li cpr_threads.h
+ *   @li cpr_timers.h
+ *   @li cpr.h
+ *   @li cpr_ipc.h
+ *   @li cpr_stddef.h
+ *   @li cpr_time.h
+ *   @li cpr_types.h
+ *
+ *   The function prototypes in these header files are implemented in the
+ *   binaries related to memory and string functionality. The prototypes are
+ *   given so that vendors can use these functions for implementation of other
+ *   CPR parts.
+ *   @li cpr_memory.h
+ *   @li cpr_stdlib.h
+ *   @li cpr_string.h
+ *   @li cpr_strings.h
+ *
+ *  @section standard_headers Standard Header Files
+ *  The pSIPCC component expects some standard header files and associated
+ *  functionality to be present in the native operating system of the vendors
+ *  porting it. These header files contain some basic functionality that pSIPCC
+ *  uses for proper operation. A list of the standard headers (from a standard
+ *  Linux distribution) are -
+ *   @li <assert.h>
+ *   @li <errno.h>
+ *   @li <arpa/inet.h>
+ *   @li <netinet/in.h>
+ *   @li <netinet/tcp.h>
+ *   @li <pthread.h>
+ *   @li <sys/socket.h>
+ *   @li <sys/select.h>
+ *   @li <sys/un.h>
+ *   @li <unistd.h>
+ *   @li <stdarg.h>
+ *   @li <stdio.h>
+ *   @li <stdlib.h>
+ *   @li <string.h>
+ *   @li <ctype.h>
+ *   @li <strings.h>
+ *   @li <time.h>
+ *
+ *
+ *  @section Comp Compiling CPR
+ *  A sample makefile is also provided with the Linux distribution. Running
+ *  "make" generates a CPR executable called "cprtest". This sample makefile can
+ *  be modified to generate a shared library if required. Modify the CC, STDINC,
+ *  STDLIB paths to point to the correct compiler and header files in the
+ *  environment where this is being built.
+ *  @note The README file shows the exact commands required to build/test the CPR
+ *  functionality.
+ *
+ *  @file cpr_linux_init.c
+ *  @brief This file contains CPR initialization routines
+ *
+ *  DESCRIPTION
+ *     Initialization routine for the Cisco Portable Runtime layer
+ *     running in the Linux operating System.
+ */
+
+/**
+  * @addtogroup Initialization The initialization module
+  * @ingroup CPR
+  * @brief The intialization module consists of APIs used to initialize or destroy the CPR layer by pSipCC
+  *
+  * @{
+  */
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_timers.h"
+#include "cpr_android_locks.h"
+#include "cpr_android_timers.h"
+#include "plat_api.h"
+#include <errno.h>
+#ifndef ANDROID
+#include <sys/msg.h>
+#include <sys/ipc.h>
+#endif
+#include "plat_debug.h"
+
+/**
+  * Mutex to manage message queue list.
+  */
+extern pthread_mutex_t msgQueueListMutex;
+
+/**
+  * Boolean to check that cprPreInit been called
+  */
+static boolean pre_init_called = FALSE;
+
+/**
+ * cprInfo prints out informational messages that are
+ * not necessarily errors, but maybe of interest to
+ * someone debugging a problem. Examples of this are
+ * requesting a msg from a msg queue that is empty,
+ * stopping a timer that is not running, etc...
+ */
+int32_t cprInfo = TRUE;
+
+
+/**
+ * cprPreInit
+ *
+ * @brief The cprPreInit function IS called from pSIPCC @b before any components are initialized.
+ *
+ * This function @b SHOULD initialize those portions of the CPR that
+ * are needed before applications can start using it. The memory subsystem
+ * (sandbox) is initialized from this routine.
+ *
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ * @note pSIPCC will NOT continue and stop initialization if the return value is CPR_FAILURE.
+ */
+cprRC_t
+cprPreInit (void)
+{
+    static const char fname[] = "cprPreInit";
+    int32_t returnCode;
+
+    /*
+     * Make function reentreant
+     */
+    if (pre_init_called == TRUE) {
+        return CPR_SUCCESS;
+    }
+    pre_init_called = TRUE;
+    /*
+     * Create message queue list mutex
+     */
+    returnCode = pthread_mutex_init(&msgQueueListMutex, NULL);
+    if (returnCode != 0) {
+        CPR_ERROR("%s: MsgQueue Mutex init failure %d\n", fname, returnCode);
+        return CPR_FAILURE;
+    }
+
+    returnCode = cpr_timer_pre_init();
+    if (returnCode != 0) {
+        CPR_ERROR("%s: timer pre init failed %d\n", fname, returnCode);
+        return CPR_FAILURE;
+    }
+
+
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * cprPostInit
+ *
+ * @brief The cprPostInit function IS called from pSIPCC @b after all the components are initialized.
+ *
+ * This function @b SHOULD complete any CPR activities before the phone is
+ * operational. In other words a call to this function will be the last line of
+ * the phone initializtion routine from pSIPCC. This function implementation
+ * ties in a couple of debug commands to get more information on the CPR from
+ * the shell.
+ *
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ * @note pSIPCC will NOT continue and stop initialization if the return value is CPR_FAILURE.
+ */
+cprRC_t
+cprPostInit (void)
+{
+/*    if (cpr_memory_mgmt_post_init() not_eq TRUE) {
+        return CPR_FAILURE;
+    }
+*/
+    return CPR_SUCCESS;
+}
+
diff --git a/libs/sipcc/cpr/android/cpr_android_ipc.c b/libs/sipcc/cpr/android/cpr_android_ipc.c
new file mode 100644 (file)
index 0000000..2302df1
--- /dev/null
@@ -0,0 +1,681 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include <cpr_stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include <plat_api.h>
+#include "cpr_string.h"
+
+/*
+ * If building with debug test interface,
+ * allow access to internal CPR functions
+ */
+#define STATIC static
+
+#define OS_MSGTQL 31 /* need to check number for MV linux and put here */
+
+/*
+ * Internal CPR API
+ */
+extern pthread_t cprGetThreadId(cprThread_t thread);
+
+/*
+ * Extended internal message queue node
+ *
+ * A double-linked list holding the nessasary message information
+ */
+typedef struct cpr_msgq_node_s
+{
+    struct cpr_msgq_node_s *next;
+    struct cpr_msgq_node_s *prev;
+    void *msg;
+    void *pUserData;
+} cpr_msgq_node_t;
+
+/*
+ * Msg queue information needed to hide OS differences in implementation.
+ * To use msg queues, the application code may pass in a name to the
+ * create function for msg queues. CPR does not use this field, it is
+ * solely for the convenience of the application and to aid in debugging.
+ *
+ * Note: Statistics are not protected by a mutex; therefore, there exists
+ * the possibility that the results may not be accurate.
+ *
+ * Note:if the depth supplied by OS is insufficient,a message queue owner may
+ * increase the message queue depth via cprCreateMessageQueue's depth
+ * parameter where the value can range from MSGTQL to CPR_MAX_MSG_Q_DEPTH.
+ */
+typedef struct cpr_msg_queue_s
+{
+    struct cpr_msg_queue_s *next;
+    const char *name;
+    pthread_t thread;
+    int32_t queueId;
+    uint16_t maxCount;
+    uint16_t currentCount;
+    uint32_t totalCount;
+    uint32_t sendErrors;
+    uint32_t reTries;
+    uint32_t highAttempts;
+    uint32_t selfQErrors;
+    uint16_t extendedQDepth;
+    uint16_t maxExtendedQDepth;
+    pthread_mutex_t mutex;       /* lock for managing extended queue     */
+       pthread_cond_t cond;             /* signal for queue/dequeue */
+    cpr_msgq_node_t *head;       /* extended queue head (newest element) */
+    cpr_msgq_node_t *tail;       /* extended queue tail (oldest element) */
+} cpr_msg_queue_t;
+
+/*
+ * A enumeration used to report the result of posting a message to
+ * a message queue
+ */
+typedef enum
+{
+    CPR_MSGQ_POST_SUCCESS,
+    CPR_MSGQ_POST_FAILED,
+    CPR_MSGQ_POST_PENDING
+} cpr_msgq_post_result_e;
+
+
+/*
+ * Head of list of message queues
+ */
+static cpr_msg_queue_t *msgQueueList = NULL;
+
+/*
+ * Mutex to manage message queue list
+ */
+pthread_mutex_t msgQueueListMutex;
+
+/*
+ * String to represent message queue name when it is not provided
+ */
+static const char unnamed_string[] = "unnamed";
+
+
+/*
+ * CPR_MAX_MSG_Q_DEPTH
+ *
+ * The maximum queue depth supported by the CPR layer.  This value
+ * is arbitrary though the purpose is to limit the memory usage
+ * by CPR and avoid (nearly) unbounded situations.
+ *
+ * Note: This value should be greater than MSGTQL which is currently
+ *       defined as 31
+ */
+#define CPR_MAX_MSG_Q_DEPTH 256
+
+/*
+ * CPR_SND_TIMEOUT_WAIT_INTERVAL
+ *
+ * The interval of time to wait in milliseconds between attempts to
+ * send a message to the message queue
+ *
+ * Note: 20 ms. to avoid less than a tick wake up since on most
+ *       OSes 10ms is one 1 tick
+ *       this should really be OS_TICK_MS * 2 or OS_TICK_MS + X
+ */
+#define CPR_SND_TIMEOUT_WAIT_INTERVAL 20
+
+/*
+ * CPR_ATTEMPTS_TO_SEND
+ *
+ * The number of attempts made to send a message when the message
+ * would otherwise be blocked.  Note in this condition the thread
+ * will sleep the timeout interval to allow the msg queue to be
+ * drained.
+ *
+ * Note: 25 attempts for upto .5 seconds at the interval of
+ *       CPR_SND_TIMEOUT_WAIT_INTERVAL worst case.
+ */
+#define CPR_ATTEMPTS_TO_SEND 25
+
+/*
+ * Also, important to note that the total timeout interval must be
+ * greater than the SIP's select call timeout value which is 25msec.
+ * This is necessary to cover the case where the SIP message queue
+ * is full and the select timeout occurs.
+ *
+ * Total timeout interval = CPR_SND_TIMEOUT_WAIT_INTERVAL *
+ *                          CPR_ATTEMPTS_TO_SEND;
+ */
+
+
+/*
+ * Prototype declarations
+ */
+static cpr_msgq_post_result_e
+cprPostMessage(cpr_msg_queue_t *msgq, void *msg, void **ppUserData);
+static void
+cprPegSendMessageStats(cpr_msg_queue_t *msgq, uint16_t numAttempts);
+
+
+/*
+ * Functions
+ */
+
+/**
+ * Creates a message queue
+ *
+ * @param name  - name of the message queue
+ * @param depth - the message queue depth, optional field which will
+ *                default if set to zero(0)
+ *
+ * @return Msg queue handle or NULL if init failed, errno provided
+ *
+ * @note the actual message queue depth will be bounded by the
+ *       standard system message queue depth and CPR_MAX_MSG_Q_DEPTH.
+ *       If 'depth' is outside of the bounds, the value will be
+ *       reset automatically.
+ */
+cprMsgQueue_t
+cprCreateMessageQueue (const char *name, uint16_t depth)
+{
+    static const char fname[] = "cprCreateMessageQueue";
+    cpr_msg_queue_t *msgq;
+    static int key_id = 100; /* arbitrary starting number */
+
+    msgq = cpr_calloc(1, sizeof(cpr_msg_queue_t));
+    if (msgq == NULL) {
+        printf("%s: Malloc failed: %s\n", fname,
+                  name ? name : unnamed_string);
+        errno = ENOMEM;
+        return NULL;
+    }
+
+    msgq->name = name ? name : unnamed_string;
+       msgq->queueId = key_id++;
+
+       pthread_cond_t _cond = PTHREAD_COND_INITIALIZER;
+       msgq->cond = _cond;
+       pthread_mutex_t _lock = PTHREAD_MUTEX_INITIALIZER;
+       msgq->mutex = _lock;
+
+    /*
+     * Add message queue to list for statistics reporting
+     */
+    pthread_mutex_lock(&msgQueueListMutex);
+    msgq->next = msgQueueList;
+    msgQueueList = msgq;
+    pthread_mutex_unlock(&msgQueueListMutex);
+
+    return msgq;
+}
+
+
+/**
+ * Removes all messages from the queue and then destroy the message queue
+ *
+ * @param msgQueue - message queue to destroy
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE, errno provided
+ */
+cprRC_t
+cprDestroyMessageQueue (cprMsgQueue_t msgQueue)
+{
+    static const char fname[] = "cprDestroyMessageQueue";
+    cpr_msg_queue_t *msgq;
+    void *msg;
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+    if (msgq == NULL) {
+        /* Bad application! */
+        CPR_ERROR("%s: Invalid input\n", fname);
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    /* Drain message queue */
+    msg = cprGetMessage(msgQueue, FALSE, NULL);
+    while (msg != NULL) {
+        cpr_free(msg);
+        msg = cprGetMessage(msgQueue, FALSE, NULL);
+    }
+
+    /* Remove message queue from list */
+    pthread_mutex_lock(&msgQueueListMutex);
+    if (msgq == msgQueueList) {
+        msgQueueList = msgq->next;
+    } else {
+        cpr_msg_queue_t *msgql = msgQueueList;
+
+        while ((msgql->next != NULL) && (msgql->next != msgq)) {
+            msgql = msgql->next;
+        }
+        if (msgql->next == msgq) {
+            msgql->next = msgq->next;
+        }
+    }
+    pthread_mutex_unlock(&msgQueueListMutex);
+
+    /* Remove message queue mutex */
+    if (pthread_mutex_destroy(&msgq->mutex) != 0) {
+        CPR_ERROR("%s: Failed to destroy msg queue (%s) mutex: %d\n",
+                  fname, msgq->name, errno);
+    }
+
+    cpr_free(msgq);
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * Associate a thread with the message queue
+ *
+ * @param msgQueue  - msg queue to set
+ * @param thread    - CPR thread to associate with queue
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ *
+ * @note Nothing is done to prevent overwriting the thread ID
+ *       when the value has already been set.
+ */
+cprRC_t
+cprSetMessageQueueThread (cprMsgQueue_t msgQueue, cprThread_t thread)
+{
+    static const char fname[] = "cprSetMessageQueueThread";
+    cpr_msg_queue_t *msgq;
+
+    if ((!msgQueue) || (!thread)) {
+        CPR_ERROR("%s: Invalid input\n", fname);
+        return CPR_FAILURE;
+    }
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+    if (msgq->thread != 0) {
+        CPR_ERROR("%s: over-writing previously msgq thread name for %s",
+                  fname, msgq->name);
+    }
+
+    msgq->thread = cprGetThreadId(thread);
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * Retrieve a message from a particular message queue
+ *
+ * @param[in]  msgQueue    - msg queue from which to retrieve the message
+ * @param[in]  waitForever - boolean to either wait forever (TRUE) or not
+ *                           wait at all (FALSE) if the msg queue is empty.
+ * @param[out] ppUserData  - pointer to a pointer to user defined data
+ *
+ * @return Retrieved message buffer or NULL if failure occurred or
+ *         the waitForever flag was set to false and no messages were
+ *         on the queue.
+ *
+ * @note   If ppUserData is defined, the value will be initialized to NULL
+ */
+void *
+cprGetMessage (cprMsgQueue_t msgQueue, boolean waitForever, void **ppUserData)
+{
+    static const char fname[] = "cprGetMessage";
+
+    void *buffer = 0;
+    cpr_msg_queue_t *msgq;
+    cpr_msgq_node_t *node;
+    struct timespec timeout;
+    struct timeval tv;
+    struct timezone *tz;
+
+    /* Initialize ppUserData */
+    if (ppUserData) {
+        *ppUserData = NULL;
+    }
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+    if (msgq == NULL) {
+        /* Bad application! */
+        CPR_ERROR("%s: Invalid input\n", fname);
+        errno = EINVAL;
+        return NULL;
+    }
+
+    /*
+     * If waitForever is set, block on the message queue
+     * until a message is received, else return after
+        * 25msec of waiting
+     */
+       pthread_mutex_lock(&msgq->mutex);
+
+       if (!waitForever)
+       {
+               // We'll wait till 25uSec from now
+               gettimeofday(&tv, &tz);
+               timeout.tv_nsec = (tv.tv_usec * 1000) + 25000;
+               timeout.tv_sec = tv.tv_sec;
+
+               pthread_cond_timedwait(&msgq->cond, &msgq->mutex, &timeout);
+
+       }
+       else
+       {
+               while(msgq->tail==NULL)
+               {
+                       pthread_cond_wait(&msgq->cond, &msgq->mutex);
+               }
+       }
+
+       // If there is a message on the queue, de-queue it
+       if (msgq->tail)
+       {
+               node = msgq->tail;
+               msgq->tail = node->prev;
+               if (msgq->tail) {
+                       msgq->tail->next = NULL;
+               }
+               if (msgq->head == node) {
+                       msgq->head = NULL;
+               }
+               msgq->currentCount--;
+               /*
+                * Pull out the data
+                */
+               if (ppUserData) {
+                       *ppUserData = node->pUserData;
+               }
+               buffer = node->msg;
+
+       }
+
+       pthread_mutex_unlock(&msgq->mutex);
+
+    return buffer;
+}
+
+
+/**
+ * Place a message on a particular queue.  Note that caller may
+ * block (see comments below)
+ *
+ * @param msgQueue   - msg queue on which to place the message
+ * @param msg        - pointer to the msg to place on the queue
+ * @param ppUserData - pointer to a pointer to user defined data
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE, errno provided
+ *
+ * @note 1. Messages queues are set to be non-blocking, those cases
+ *       where the system call fails with a would-block error code
+ *       (EAGAIN) the function will attempt other mechanisms described
+ *       below.
+ * @note 2. If enabled with an extended message queue, either via a
+ *       call to cprCreateMessageQueue with depth value or a call to
+ *       cprSetExtendMessageQueueDepth() (when unit testing), the message
+ *       will be added to the extended message queue and the call will
+ *       return successfully.  When room becomes available on the
+ *       system's message queue, those messages will be added.
+ * @note 3. If the message queue becomes full and no space is availabe
+ *       on the extended message queue, then the function will attempt
+ *       to resend the message up to CPR_ATTEMPTS_TO_SEND and the
+ *       calling thread will *BLOCK* CPR_SND_TIMEOUT_WAIT_INTERVAL
+ *       milliseconds after each failed attempt.  If unsuccessful
+ *       after all attempts then EGAIN error code is returned.
+ * @note 4. This applies to all CPR threads, including the timer thread.
+ *       So it is possible that the timer thread would be forced to
+ *       sleep which would have the effect of delaying all active
+ *       timers.  The work to fix this rare situation is not considered
+ *       worth the effort to fix....so just leaving as is.
+ */
+cprRC_t
+cprSendMessage (cprMsgQueue_t msgQueue, void *msg, void **ppUserData)
+{
+    static const char fname[] = "cprSendMessage";
+    static const char error_str[] = "%s: Msg not sent to %s queue: %s\n";
+    cpr_msgq_post_result_e rc;
+    cpr_msg_queue_t *msgq;
+    int16_t attemptsToSend = CPR_ATTEMPTS_TO_SEND;
+    uint16_t numAttempts   = 0;
+
+    /* Bad application? */
+    if (msgQueue == NULL) {
+        CPR_ERROR(error_str, fname, "undefined", "invalid input");
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+
+    /*
+     * Attempt to send message
+     */
+    do {
+
+               /*
+                * Post the message to the Queue
+                */
+               rc = cprPostMessage(msgq, msg, ppUserData);
+
+               if (rc == CPR_MSGQ_POST_SUCCESS) {
+                       cprPegSendMessageStats(msgq, numAttempts);
+                       return CPR_SUCCESS;
+               } else if (rc == CPR_MSGQ_POST_FAILED) {
+                       CPR_ERROR("%s: Msg not sent to %s queue: %d\n",
+                                         fname, msgq->name, errno);
+                       msgq->sendErrors++;
+                       /*
+                        * If posting to calling thread's own queue,
+                        * then peg the self queue error.
+                        */
+                       if (pthread_self() == msgq->thread) {
+                               msgq->selfQErrors++;
+                       }
+
+                       return CPR_FAILURE;
+               }
+
+
+        /*
+         * Did not succeed in sending the message, so continue
+         * to attempt up to the CPR_ATTEMPTS_TO_SEND.
+         */
+        attemptsToSend--;
+        if (attemptsToSend > 0) {
+            /*
+             * Force a context-switch of the thread attempting to
+             * send the message, in order to help the case where
+             * the msg queue is full and the owning thread may get
+             * a a chance be scheduled so it can drain it (Note:
+             * no guarantees, more of a "last-ditch effort" to
+             * recover...especially when temporarily over-whelmed).
+             */
+            cprSleep(CPR_SND_TIMEOUT_WAIT_INTERVAL);
+            msgq->reTries++;
+            numAttempts++;
+        }
+    } while (attemptsToSend > 0);
+
+    CPR_ERROR(error_str, fname, msgq->name, "FULL");
+    msgq->sendErrors++;
+    return CPR_FAILURE;
+}
+
+/**
+ * Peg the statistics for successfully posting a message
+ *
+ * @param msgq        - message queue
+ * @param numAttempts - number of attempts to post message to message queue
+ *
+ * @return none
+ *
+ * @pre (msgq not_eq NULL)
+ */
+static void
+cprPegSendMessageStats (cpr_msg_queue_t *msgq, uint16_t numAttempts)
+{
+    /*
+     * Collect statistics
+     */
+    msgq->totalCount++;
+    if (msgq->currentCount > msgq->maxCount) {
+        msgq->maxCount = msgq->currentCount;
+    }
+
+    if (numAttempts > msgq->highAttempts) {
+        msgq->highAttempts = numAttempts;
+    }
+}
+
+/**
+ * Post message to system message queue
+ *
+ * @param msgq       - message queue
+ * @param msg        - message to post
+ * @param ppUserData - ptr to ptr to option user data
+ *
+ * @return the post result which is CPR_MSGQ_POST_SUCCESS,
+ *         CPR_MSGQ_POST_FAILURE or CPR_MSGQ_POST_PENDING
+ *
+ * @pre (msgq not_eq NULL)
+ * @pre (msg not_eq NULL)
+ */
+static cpr_msgq_post_result_e
+cprPostMessage (cpr_msg_queue_t *msgq, void *msg, void **ppUserData)
+{
+       cpr_msgq_node_t *node;
+
+       /*
+        * Allocate new message queue node
+        */
+       node = cpr_malloc(sizeof(*node));
+       if (!node) {
+               errno = ENOMEM;
+               return CPR_MSGQ_POST_FAILED;
+       }
+
+       pthread_mutex_lock(&msgq->mutex);
+
+       /*
+        * Fill in data
+        */
+       node->msg = msg;
+       if (ppUserData != NULL) {
+               node->pUserData = *ppUserData;
+       } else {
+               node->pUserData = NULL;
+       }
+
+       /*
+        * Push onto list
+        */
+       node->prev = NULL;
+       node->next = msgq->head;
+       msgq->head = node;
+
+       if (node->next) {
+               node->next->prev = node;
+       }
+
+       if (msgq->tail == NULL) {
+               msgq->tail = node;
+       }
+       msgq->currentCount++;
+
+       pthread_cond_signal(&msgq->cond);
+       pthread_mutex_unlock(&msgq->mutex);
+
+       return CPR_MSGQ_POST_SUCCESS;
+
+}
+
+
+/**
+ * cprGetMessageQueueStats
+ *
+ * Get statistics for a given message queue
+ *
+ * @param msgQueue - message queue on which to gather stats
+ * @param stats    - pointer to struct to place statistics
+ *
+ * @return none
+ */
+STATIC void
+cprGetMessageQueueStats (cprMsgQueue_t msgQueue, cprMsgQueueStats_t *stats)
+{
+    cpr_msg_queue_t *msgq;
+
+    if (msgQueue && stats) {
+        msgq = (cpr_msg_queue_t *) msgQueue;
+
+        sstrncpy(stats->name, msgq->name ? msgq->name : "undefined",
+                sizeof(stats->name));
+
+        stats->extendedDepth = msgq->maxExtendedQDepth;
+        stats->maxCount = msgq->maxCount;
+        stats->currentCount = msgq->currentCount;
+        stats->totalCount = msgq->totalCount;
+        stats->reTries = msgq->reTries;
+        stats->sendErrors = msgq->sendErrors;
+        stats->highAttempts = msgq->highAttempts;
+        stats->selfQErrors = msgq->selfQErrors;
+    }
+}
+
+
+/**
+ * Report statistics for all message queues
+ *
+ * @param argc - not used
+ * @param argv - not used
+ *
+ * @return zero(0)
+ *
+ * @note Prototype is 'canned' so return of zero is necessary
+ */
+int32_t
+cprShowMessageQueueStats (int32_t argc, const char *argv[])
+{
+    cpr_msg_queue_t *msgq;
+    cprMsgQueueStats_t stats;
+
+    debugif_printf("CPR Message Queues\n");
+
+    pthread_mutex_lock(&msgQueueListMutex);
+    msgq = msgQueueList;
+    while (msgq != NULL) {
+        memset(&stats, 0, sizeof(stats));
+        cprGetMessageQueueStats(msgq, &stats);
+
+        debugif_printf("Name: %s\n", stats.name);
+        debugif_printf("   extended depth: %d\n", stats.extendedDepth);
+        debugif_printf("   max: %d\n", stats.maxCount);
+        debugif_printf("   active: %d\n", stats.currentCount);
+        debugif_printf("   total: %d\n", stats.totalCount);
+        debugif_printf("   retries: %d\n", stats.reTries);
+        debugif_printf("   high attempts: %d\n", stats.highAttempts);
+        debugif_printf("   send errors: %d\n", stats.sendErrors);
+        debugif_printf("   self queue errors: %d\n\n", stats.selfQErrors);
+
+        msgq = msgq->next;
+    }
+    pthread_mutex_unlock(&msgQueueListMutex);
+
+    return 0;
+}
+
+/**
+ * cprGetDepth
+ *
+ * @brief get depth of a message queue
+ *
+ * The pSIPCC uses this API to look at the depth of a message queue for internal
+ * routing and throttling decision
+ *
+ * @param[in] msgQueue - message queue
+ *
+ * @return depth of msgQueue
+ *
+ * @pre (msgQueue not_eq NULL)
+ */
+uint16_t cprGetDepth (cprMsgQueue_t msgQueue)
+{
+        cpr_msg_queue_t *msgq;
+        msgq = (cpr_msg_queue_t *) msgQueue;
+        return msgq->currentCount;
+}
+
diff --git a/libs/sipcc/cpr/android/cpr_android_ipc.h b/libs/sipcc/cpr/android/cpr_android_ipc.h
new file mode 100644 (file)
index 0000000..a735f07
--- /dev/null
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_CNU_IPC_H_
+#define _CPR_CNU_IPC_H_
+
+#include "cpr_threads.h"
+#include <pthread.h>
+
+/* Enable support for cprSetMessageQueueThread API */
+#define CPR_USE_SET_MESSAGE_QUEUE_THREAD
+
+/* Maximum message size allowed by CNU */
+#define CPR_MAX_MSG_SIZE  8192
+
+/* Our CNU msgtype */
+#define CPR_IPC_MSG 1
+
+
+/* Message buffer layout */
+struct msgbuffer {
+    long    mtype;    /* Message type */
+    void   *msgPtr;   /* Ptr to msg */
+    void   *usrPtr;   /* Ptr to user data */
+};
+
+/* For gathering statistics regarding message queues */
+typedef struct {
+    char name[16];
+    uint16_t maxCount;
+    uint16_t currentCount;
+    uint32_t totalCount;
+    uint32_t rcvTimeouts;
+    uint32_t sendErrors;
+    uint32_t reTries;
+    uint32_t highAttempts;
+    uint32_t selfQErrors;
+    uint16_t extendedDepth;
+} cprMsgQueueStats_t;
+
+
+/*
+ * Mutex for updating the message queue list
+ */
+extern pthread_mutex_t msgQueueListMutex;
+
+
+/**
+ * cprGetDepth
+ *
+ * Get depth of a message queue
+ */
+uint16_t cprGetDepth(cprMsgQueue_t msgQueue);
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_locks.c b/libs/sipcc/cpr/android/cpr_android_locks.c
new file mode 100644 (file)
index 0000000..b945d1c
--- /dev/null
@@ -0,0 +1,209 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include <errno.h>
+#include <pthread.h>
+#include <sys/time.h>
+
+/**
+  * @defgroup MutexIPCAPIs The Mutex/Semaphore IPC APIs
+  * @ingroup IPC
+  * @brief The module related to Mutex/Sempahore abstraction for the pSIPCC
+  * @{
+  */
+
+
+/**
+ * cprCreateMutex
+ *
+ * @brief Creates a mutual exclusion block
+ *
+ * The cprCreateMutex function is called to allow the OS to perform whatever
+ * work is needed to create a mutex.
+ *
+ * @param[in] name  - name of the mutex. If present, CPR assigns this name to
+ * the mutex to assist in debugging.
+ *
+ * @return Mutex handle or NULL if creation failed. If NULL, set errno
+ */
+cprMutex_t
+cprCreateMutex (const char *name)
+{
+    static const char fname[] = "cprCreateMutex";
+    static uint16_t id = 0;
+    int32_t returnCode;
+    cpr_mutex_t *cprMutexPtr;
+    pthread_mutex_t *pthreadMutexPtr;
+
+    /*
+     * Malloc memory for a new mutex. CPR has its' own
+     * set of mutexes so malloc one for the generic
+     * CPR view and one for the CNU specific version.
+     */
+    cprMutexPtr = (cpr_mutex_t *) cpr_malloc(sizeof(cpr_mutex_t));
+    pthreadMutexPtr = (pthread_mutex_t *) cpr_malloc(sizeof(pthread_mutex_t));
+    if ((cprMutexPtr != NULL) && (pthreadMutexPtr != NULL)) {
+        /* Assign name */
+        cprMutexPtr->name = name;
+
+        /*
+         * Use default mutex attributes. TBD: if we do not
+         * need cnuMutexAttributes global get rid of it
+         */
+        returnCode = pthread_mutex_init(pthreadMutexPtr, NULL);
+        if (returnCode != 0) {
+            CPR_ERROR("%s - Failure trying to init Mutex %s: %d\n",
+                      fname, name, returnCode);
+            cpr_free(pthreadMutexPtr);
+            cpr_free(cprMutexPtr);
+            return (cprMutex_t)NULL;
+        }
+
+        /*
+         * TODO - It would be nice for CPR to keep a linked
+         * list of active mutexes for debugging purposes
+         * such as a show command or walking the list to ensure
+         * that an application does not attempt to create
+         * the same mutex twice.
+         */
+        cprMutexPtr->u.handlePtr = pthreadMutexPtr;
+        cprMutexPtr->lockId = ++id;
+        return (cprMutex_t)cprMutexPtr;
+    }
+
+    /*
+     * Since the code malloced two pointers ensure both
+     * are freed since one malloc call could have worked
+     * and the other failed.
+     */
+    if (pthreadMutexPtr != NULL) {
+        cpr_free(pthreadMutexPtr);
+    } else if (cprMutexPtr != NULL) {
+        cpr_free(cprMutexPtr);
+    }
+
+    /* Malloc failed */
+    CPR_ERROR("%s - Malloc for mutex %s failed.\n", fname, name);
+    errno = ENOMEM;
+    return (cprMutex_t)NULL;
+}
+
+
+/**
+ * cprDestroyMutex
+ *
+ * @brief Destroys the mutex passed in.
+ *
+ * The cprDestroyMutex function is called to destroy a mutex. It is the
+ * application's responsibility to ensure that the mutex is unlocked when
+ * destroyed. Unpredictiable behavior will occur if an application
+ * destroys a locked mutex.
+ *
+ * @param[in] mutex - mutex to destroy
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE. errno should be set for CPR_FAILURE.
+ */
+cprRC_t
+cprDestroyMutex (cprMutex_t mutex)
+{
+    static const char fname[] = "cprDestroyMutex";
+    cpr_mutex_t *cprMutexPtr;
+    int32_t rc;
+
+    cprMutexPtr = (cpr_mutex_t *) mutex;
+    if (cprMutexPtr != NULL) {
+        rc = pthread_mutex_destroy(cprMutexPtr->u.handlePtr);
+        if (rc != 0) {
+            CPR_ERROR("%s - Failure destroying Mutex %s: %d\n",
+                      fname, cprMutexPtr->name, rc);
+            return CPR_FAILURE;
+        }
+        cprMutexPtr->lockId = 0;
+        cpr_free(cprMutexPtr->u.handlePtr);
+        cpr_free(cprMutexPtr);
+        return CPR_SUCCESS;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprGetMutex
+ *
+ * @brief Acquire ownership of a mutex
+ *
+ * This function locks the mutex referenced by the mutex parameter. If the mutex
+ * is locked by another thread, the calling thread will block until the mutex is
+ * released.
+ *
+ * @param[in] mutex - Which mutex to acquire
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprGetMutex (cprMutex_t mutex)
+{
+    static const char fname[] = "cprGetMutex";
+    cpr_mutex_t *cprMutexPtr;
+    int32_t rc;
+
+    cprMutexPtr = (cpr_mutex_t *) mutex;
+    if (cprMutexPtr != NULL) {
+        rc = pthread_mutex_lock((pthread_mutex_t *) cprMutexPtr->u.handlePtr);
+        if (rc != 0) {
+            CPR_ERROR("%s - Error acquiring mutex %s: %d\n",
+                      fname, cprMutexPtr->name, rc);
+            return CPR_FAILURE;
+        }
+        return CPR_SUCCESS;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprReleaseMutex
+ *
+ * @brief Release ownership of a mutex
+ *
+ * This function unlocks the mutex referenced by the mutex parameter.
+ * @param[in] mutex - Which mutex to release
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprReleaseMutex (cprMutex_t mutex)
+{
+    static const char fname[] = "cprReleaseMutex";
+    cpr_mutex_t *cprMutexPtr;
+    int32_t rc;
+
+    cprMutexPtr = (cpr_mutex_t *) mutex;
+    if (cprMutexPtr != NULL) {
+        rc = pthread_mutex_unlock((pthread_mutex_t *) cprMutexPtr->u.handlePtr);
+        if (rc != 0) {
+            CPR_ERROR("%s - Error releasing mutex %s: %d\n",
+                      fname, cprMutexPtr->name, rc);
+            return CPR_FAILURE;
+        }
+        return CPR_SUCCESS;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
diff --git a/libs/sipcc/cpr/android/cpr_android_locks.h b/libs/sipcc/cpr/android/cpr_android_locks.h
new file mode 100644 (file)
index 0000000..5e1d8df
--- /dev/null
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_LOCKS_H_
+#define _CPR_ANDROID_LOCKS_H_
+
+/*
+ * System Mutexes.
+ */
+extern pthread_mutex_t linuxThreadMutex;
+#endif
+
diff --git a/libs/sipcc/cpr/android/cpr_android_private.h b/libs/sipcc/cpr/android/cpr_android_private.h
new file mode 100644 (file)
index 0000000..96f2147
--- /dev/null
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_PRIVATE_H_
+#define _CPR_ANDROID_PRIVATE_H_
+
+extern pthread_mutexattr_t cprMutexAttributes;
+
+cpr_status_e cprLockInit(void);
+cpr_status_e cprTimerInit(void);
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_rand.h b/libs/sipcc/cpr/android/cpr_android_rand.h
new file mode 100644 (file)
index 0000000..a64aaea
--- /dev/null
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_RAND_H_
+#define _CPR_ANDROID_RAND_H_
+
+/* @def Defines to generate a random number */
+#define cpr_srand(seed) srand(seed)
+#define cpr_rand()      rand()
+
+
+
+#endif
+
diff --git a/libs/sipcc/cpr/android/cpr_android_socket.c b/libs/sipcc/cpr/android/cpr_android_socket.c
new file mode 100644 (file)
index 0000000..4e271b4
--- /dev/null
@@ -0,0 +1,965 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_assert.h"
+#include "cpr_socket.h"
+#include "cpr_debug.h"
+#include "cpr_rand.h"
+#include "cpr_timers.h"
+#include "cpr_errno.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include <syslog.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+
+//const cpr_in6_addr_t in6addr_any = IN6ADDR_ANY_INIT;
+const cpr_ip_addr_t ip_addr_invalid = {0};
+
+#define IN6ADDRSZ   16
+#define INT16SZ     2
+#define        INADDRSZ        4
+
+#define MAX_RETRY_FOR_EAGAIN 10
+
+/* Forward declarations of internal (helper) functions */
+static int cpr_inet_pton4(const char *src, uint8_t *dst, int pton);
+static int cpr_inet_pton6(const char *src, uint8_t *dst);
+
+/**
+ * cprBind
+ *
+ * @brief The cprBind function is the CPR wrapper for the "Bind" socket call.
+ *
+ * The cprBind() function shall assign a local socket address address to a
+ * socket identified by descriptor socket that has no local socket address
+ * assigned. Sockets created with the cprSocket() function are initially
+ * unnamed; they are identified only by their address family.
+ *
+ * @param[in] soc  - The socket previously created using cprAccept that is to be
+ *                   bound
+ * @param[out] addr - The address of the socket that is to be bound
+ * @param[in] addr_len Points to a cpr_socklen_t structure which on specifies the length
+ *                   of the supplied cpr_sockaddr_t structure.
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ *         @li [CPR_ENOTSOCK]   The descriptor references a file, not a socket
+ *
+ */
+cpr_status_e
+cprBind (cpr_socket_t soc,
+         CONST cpr_sockaddr_t * RESTRICT addr,
+         cpr_socklen_t addr_len)
+{
+    cprAssert(addr != NULL, CPR_FAILURE);
+
+    return ((bind(soc, (struct sockaddr *)addr, addr_len) != 0) ?
+            CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprCloseSocket
+ *
+ * @brief The cprCloseSocket function shall destroy the socket
+ *
+ * The cprCloseSocket() function shall destroy the socket descriptor indicated
+ * by socket.  The descriptor may be made available for return by subsequent
+ * calls to cprSocket().  If the linger option is set with a non-zero timeout
+ * and the socket has untransmitted data, then cprCloseSocket() shall block for
+ * up to the current linger interval until all data is transmitted.
+ *
+ * @param[in] soc  - The socket that needs to be destroyed
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+cprCloseSocket (cpr_socket_t soc)
+{
+    return ((close(soc) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprConnect
+ *
+ * @brief The cprConnect function is the wrapper for the "connect" socket API
+ *
+ * The cprConnect() function shall attempt to make a connection on a socket.
+ * If the connection cannot be established immediately and non-blocking is set for
+ * the socket, cprConnect() shall fail and set cpr_errno to [CPR_EINPROGRESS], but
+ * the connection request shall not be aborted, and the connection shall be
+ * established asynchronously. When the connection has been established
+ * asynchronously, cprSelect() shall indicate that the file descriptor for the
+ * socket is ready for writing.
+ * If the initiating socket is connection-mode, then cprConnect() shall attempt to
+ * establish a connection to the address specified by the address argument.If the
+ * connection cannot be established immediately and non-blocking is not set for
+ * the socket, cprConnect() shall block for up to an unspecified timeout interval
+ * until the connection is established. If the timeout interval expires before the
+ * connection is established, cprConnect() shall fail and the connection attempt
+ * shall be aborted.
+ * If the initiating socket is connectionless (i.e. SOCK_DGRAM), then cprConnect()
+ * shall set the socket's peer address, and no connection is made. The peeraddress
+ * identifies where all datagrams are sent on subsequent cprSend() functions, and
+ * limits the remote sender for subsequent cprRecv() functions. If address is a
+ * null address for the protocol, the socket's peer address shall be reset.
+ *
+ * @param[in] soc  - Specifies the socket created with cprSocket() to connect
+ * @param[in] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
+ * @param[in] addr_len  - Points to a cpr_socklen_t structure which specifies
+ *                        the length of the supplied cpr_sockaddr_t structure.
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+cprConnect (cpr_socket_t soc,
+            SUPPORT_CONNECT_CONST cpr_sockaddr_t * RESTRICT addr,
+            cpr_socklen_t addr_len)
+{
+    int retry = 0, retval;
+
+    cprAssert(addr != NULL, CPR_FAILURE);
+
+    retval = connect(soc, (struct sockaddr *)addr, addr_len);
+
+    while( retval == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      retval = connect(soc, (struct sockaddr *)addr, addr_len);
+    }
+
+    return ((retval!=0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprGetSockName
+ *
+ * @brief The cprGetSockName retrieves the locally-bound name of the socket
+ *
+ * The cprGetSockName() function shall retrieve the locally-bound name of the
+ * specified socket, store this address in the cpr_sockaddr_t struct
+ * structure pointed to by the "addr" argument, and store the length of this address in
+ * the object pointed to by the "addr_len" argument.  If the actual length
+ * of the address is greater than the length of the supplied cpr_sockaddr_t
+ * structure, the stored address shall be truncated.  If the socket has not been
+ * bound to a local name, the value stored in the object pointed to by address is
+ * unspecified.
+ *
+ * @param[in] soc  - Specifies the socket to get the peer address from
+ * @param[out] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
+ * @param[out] addr_len  - Points to a cpr_socklen_t structure which specifies
+ *                        the length of the supplied cpr_sockaddr_t structure.
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ *  @note If successful, the address argument shall point to the address of the socket
+ *  @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
+ *        @li [CPR_ENOTCONN]  The socket is not connected
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_OPNOTSUPP] The operation is not supported for the socket
+ */
+cpr_status_e
+cprGetSockName (cpr_socket_t soc,
+                cpr_sockaddr_t * RESTRICT addr,
+                cpr_socklen_t * RESTRICT addr_len)
+{
+    cprAssert(addr != NULL, CPR_FAILURE);
+    cprAssert(addr_len != NULL, CPR_FAILURE);
+
+    return ((getsockname(soc, (struct sockaddr *)addr, addr_len) != 0) ?
+            CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprListen
+ *
+ * @brief The cprListen is the CPR wrapper for the "listen" socket API.
+ *
+ * The cprListen() function shall mark a connection-mode socket, specified by
+ * the "soc" argument, as accepting connections.  The "backlog" argument
+ * provides a hint to the implementation which the implementation shall use to
+ * limit the number of outstanding connections in the socket's listen queue.
+ * Implementations may impose a limit on backlog and silently reduce the
+ * specified value. Implementations shall support values of backlog up to
+ * SOMAXCONN.
+ * If listen() is called with a backlog argument value that is less than zero
+ * (0), the function behaves as if it had been called with a backlog argument
+ * value of 0.  A backlog argument of zero (0) may allow the socket to accept
+ * connections, in which case the length of the listen queue may be set to an
+ * implementation-defined minimum value.
+ *
+ * @param[in] soc  - Specifies the socket to get the peer address
+ * @param[in] backlog  - The limit on the number of outstanding connections
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *  @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EDESTADDRREQ]  The socket is not bound to a local address
+ */
+cpr_status_e
+cprListen (cpr_socket_t soc,
+           uint16_t backlog)
+{
+    return ((listen(soc, backlog) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprRecv
+ *
+ * @brief The cprRecv() function shall receive a message from a socket.
+ *
+ * This function is normally used with connected sockets because it does not permit
+ * the application to retrieve the source address of received data.  The cprRecv()
+ * function shall return the length of the message written to the buffer pointed
+ * to by the "buf" argument.
+ * For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
+ * a single operation.  If the message is too long to fit in the supplied buffer
+ * and the "flags" argument does not have MSG_PEEK set, the excess bytes are
+ * discarded.  If the MSG_WAITALL flag is not set, data shall be returned onlyup
+ * to the end of the first message.
+ * For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
+ * data is returned as it becomes available; therefore, no data is discarded.
+ * If no messages are available at the socket and non-blocking is not set on the
+ * socket's file descriptor, cprRecv() shall block until a message arrives. If no
+ * messages are available at the socket and non-blocking is set on the socket's
+ * file descriptor, cprRecv() shall fail and set cpr_errno to [CPR_EAGAIN]or
+ * [CPR_EWOULDBLOCK].
+ * The cprSelect() function can be used to determine when data is available to be
+ * received.  The cprRecv() function is the same as cprRecvFrom() with a zero
+ * address_len argument.
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ * @param[in] flags  - The options used for the recv.
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+cprRecv (cpr_socket_t soc,
+         void * RESTRICT buf,
+         size_t len,
+         int32_t flags)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(buf != NULL, CPR_FAILURE);
+
+    rc = recv(soc, buf, len, flags);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = recv(soc, buf, len, flags);
+    }
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprRecvFrom
+ *
+ * @brief The cprRecvFrom() function shall receive a message from a specific socket.
+ *
+ * The cprRecvFrom() function shall receive a message from a socket and is
+ * normally used with connectionless-mode sockets because it permits the
+ * application to retrieve the source address of received data.  This function
+ * shall return the length of the message written to the buffer pointed to bythe
+ * buffer argument.
+ * For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
+ * a single operation.  If the message is too long to fit in the supplied buffer
+ * and the flags argument does not have MSG_PEEK set, the excess bytes are
+ * discarded.  If the MSG_WAITALL flag is not set, data shall be returned onlyup
+ * to the end of the first message.
+ * For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
+ * data is returned as it becomes available; therefore, no data is discarded.
+ * If no messages are available at the socket and non-blocking is not set on the
+ * socket's file descriptor, cprRecvFrom() shall block until a message arrives. If no
+ * messages are available at the socket and non-blocking is set on the socket's
+ * file descriptor, cprRecvFrom() shall fail and set cpr_errno to [CPR_EAGAIN]or
+ * [CPR_EWOULDBLOCK].
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ * @param[in] flags  - The options used for the recvFrom
+ * @param[out] from - A null pointer or pointer to a cpr_sockaddr_t structure in
+ *                   which the sending address is to be stored.
+ * @param[out] fromlen - The length of the cpr_sockaddr_t structure pointed to by
+ *                    the "from" argument.
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+cprRecvFrom (cpr_socket_t soc,
+             void * RESTRICT buf,
+             size_t len,
+             int32_t flags,
+             cpr_sockaddr_t * RESTRICT from,
+             cpr_socklen_t * RESTRICT fromlen)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(buf != NULL, CPR_FAILURE);
+    cprAssert(from != NULL, CPR_FAILURE);
+    cprAssert(fromlen != NULL, CPR_FAILURE);
+
+    rc = recvfrom(soc, buf, len, flags, (struct sockaddr *)from, fromlen);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = recvfrom(soc, buf, len, flags, (struct sockaddr *)from, fromlen);
+    }
+
+    if (rc == -1) {
+        CPR_INFO("error in recvfrom buf=%x fromlen=%d\n", buf, *fromlen);
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSelect
+ *
+ * @brief The cprSelect() function is the CPR wrapper for the "select" socket API.
+ *
+ * The cprSelect() function returns which of the specified file descriptors is ready for
+ * reading, ready for writing, or has an exception pending.  The function will
+ * block up to the specified timeout interval for one of the conditions to be
+ * true or until interrupted by a signal.
+ *
+ * File descriptor masks of type fd_set can be initialized and tested with
+ * FD_CLR(), FD_ISSET(), FD_SET(), and FD_ZERO().  The OS-implementation may
+ * implement these calls either as a macro definition or an actual function.
+ * void FD_CLR(cpr_socket_t, fd_set *) shall remove the file descriptor fd from the
+ * set. If fd is not a member of this set, there shall be no effect on theset.
+ * int FD_ISSET(cpr_socket_t, fd_set *) shall evaluate to non-zero if the file
+ * descriptor fd is a member of the set, and shall evaluate to zero otherwise.
+ * void FD_SET(cpr_socket_t, fd_set *) shall add the file descriptor fd to the set.
+ * If the file descriptor fd is already in this set, there shall be no effect on
+ * the set.
+ * void FD_ZERO(fd_set *) shall initialize the descriptor set to the null set.
+ *
+ * @param[in] nfds    Specifies the argument range of file descriptors to be tested. The
+ *            descriptors from zero through nfds-1 in the descriptor sets shall be examined.
+ * @param[in] read_fds    If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for being ready to read.
+ *    @li On output this specifies the file descriptors that are ready to read.
+ * @param[in] write_fds   If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for being ready to write.
+ *    @li On output this specifies the file descriptors that are ready to write.
+ * @param[in] except_fds   If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for errors/exceptions pending.
+ *    @li On output this specifies the file descriptors that have errors/exceptions pending.
+ * @param[in] timeout If not a null pointer, this  points to an object of type struct cpr_timeval
+ *       that specifies the maximum time interval to wait for the selection to complete.
+ *       If timeout expires, the function shall return.  If the parameter is a null pointer, the function
+ *       will block indefinitely until at least one file descriptor meets the criteria.
+ *
+ * @note While this function supports multiple file descriptor types, only file descriptors referring to a
+ *      socket are guaranteed to be supported.
+ * @note Note that the "nfds" parameter is not used in Windows.
+ *
+ * @return Upon successful completion, cprSelect() shall return the number of file descriptors ready.
+ *       Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to indicate the error where read_fds,
+ *       write_fds and error_fds are not modified.
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_INTR]   The function was interrupted before an event or
+ *                         timeout occurred
+ *        @li [CPR_INVAL]  An invalid timeout was specified or nfds is less
+ *                        than 0 or greater than FD_SETSIZE
+ *
+ */
+int16_t
+cprSelect (uint32_t nfds,
+           fd_set * RESTRICT read_fds,
+           fd_set * RESTRICT write_fds,
+           fd_set * RESTRICT except_fds,
+           struct cpr_timeval * RESTRICT timeout)
+{
+    int16_t rc;
+    struct timeval t, *t_p;
+
+    if (timeout != NULL) {
+        t.tv_sec  = timeout->tv_sec;
+        t.tv_usec = timeout->tv_usec;
+        t_p       = &t;
+    } else {
+        t_p       = NULL;
+    }
+
+    rc = (int16_t) select(nfds, read_fds, write_fds, except_fds, t_p);
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSend
+ *
+ * @brief The cprSend() function is the CPR wrapper for the "send" socket API.
+ *
+ * The cprSend() function shall transmit a message from the specified socket to
+ * its peer. The cprSend() function shall send a message only when the socket is
+ * connected. The length of the message to be sent is specified by the length
+ * argument. If the message is too long to pass through the underlying protocol,
+ * cprSend() shall fail and no data is transmitted.  Delivery of the message is
+ * not guaranteed.
+ * If space is not available at the sending socket to hold the message to be
+ * transmitted, and the socket does not have non-blocking set, cprSend() shall
+ * block until space is available; otherwise, if non-blocking is set, the
+ * cprSend() call shall fail.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags   The socket options
+ *
+ * @return Upon successful completion, cprSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+cprSend (cpr_socket_t soc,
+         CONST void *buf,
+         size_t len,
+         int32_t flags)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(buf != NULL, CPR_FAILURE);
+
+    rc = send(soc, buf, len, flags);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = send(soc, buf, len, flags);
+    }
+
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSendTo
+ *
+ * @brief The cprSendTo() function is the CPR wrapper for the "send" socket API.
+ *
+ * The cprSendTo() function shall send a message through a socket. If the socket
+ * is connectionless-mode, the message shall be sent to the address specified by
+ * address. If the socket is connection-mode, address shall be ignored.
+ * Delivery of the message is not guaranteed.
+ * If space is not available at the sending socket to hold the message to be
+ * transmitted, and the socket does not have non-blocking set, cprSendTo() shall
+ * block until space is available; otherwise, if non-blocking is set, the
+ * cprSendTo() call shall fail.
+ * The cprSelect() function can be used to determine when it is possible to send
+ * more data.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] msg  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags   The socket options
+ * @param[in] dest_addr Points to a cpr_sockaddr_t structure containing the destination
+ *                    address.
+ * @param[in] dest_len Specifies the length of the cpr_sockaddr_t structure pointed to by
+ *                     the "dest_addr" argument.
+ *
+ * @return Upon successful completion, cprSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+cprSendTo (cpr_socket_t soc,
+           CONST void *msg,
+           size_t len,
+           int32_t flags,
+           CONST cpr_sockaddr_t *dest_addr,
+           cpr_socklen_t dest_len)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(msg != NULL, CPR_FAILURE);
+    cprAssert(dest_addr != NULL, CPR_FAILURE);
+
+    rc = sendto(soc, msg, len, flags, (struct sockaddr *)dest_addr, dest_len);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = sendto(soc, msg, len, flags, (struct sockaddr *)dest_addr, dest_len);
+    }
+
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSetSockOpt
+ *
+ * @brief The cprSetSockOpt() function is used to set the socket options
+ *
+ * The cprSetSockOpt() function shall set the option specified by the
+ * option_name argument, at the protocol level specified by the "level" argument,
+ * to the value pointed to by the "opt_val" argument for the socket specified
+ * by the "soc" argument.
+ * The level argument specifies the protocol level at which the option resides. To
+ * set options at the socket level, specify the level argument as SOL_SOCKET. To
+ * set options at other levels, supply the appropriate level identifier for the
+ * protocol controlling the option. For example, to indicate that an option is
+ * interpreted by the TCP (Transport Control Protocol), set level to IPPROTO_TCP
+ * as defined in the <cpr_in.h> header.
+ * The opt_name argument specifies a single option to set. The option_name
+ * argument and any specified options are passed uninterpreted to the appropriate
+ * protocol module. The <cpr_socket.h> header defines the socket-level options.
+ *
+ * @param[in] soc The socket on which the options need to be set
+ * @param[in] level The protocol level at which the option resides
+ * @param[in] opt_name This specifies the single option that is being set
+ * @param[in] opt_val The values for the option
+ * @param[in] opt_len The length field for the option values
+ *
+ * @return Upon successful completion, CPR_SUCCESS shall be returned;
+ * otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
+ * error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EINVAL]    The specified option is invalid or the socket is
+ *                          shut down
+ *        @li [CPR_EISCONN]   The specified socket is already connected and can
+ *                          not be changed
+ *        @li [CPR_ENOPROTOOPT]   The option is not supported by the protocol
+ *
+ */
+cpr_status_e
+cprSetSockOpt (cpr_socket_t soc,
+               uint32_t level,
+               uint32_t opt_name,
+               CONST void *opt_val,
+               cpr_socklen_t opt_len)
+{
+    cprAssert(opt_val != NULL, CPR_FAILURE);
+
+    return ((setsockopt(soc, (int)level, (int)opt_name, opt_val, opt_len) != 0)
+            ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprSetSockNonBlock
+ *
+ * @brief The cprSetSockNonBlock() function is used to set the socket options
+ *
+ * The cprSetSockNonBlock() function shall set a socket to be non blocking. It
+ * uses the fcntl function on the socket desriptor to achieve this. If the fcntl
+ * operation fails, a CPR_FAILURE is returned and errno is set by the OS
+ * implementation.
+ *
+ * @param[in] soc The socket that needs to be set to non-blocking
+ *
+ * @return Upon successful completion, CPR_SUCCESS shall be returned;
+ * otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
+ * error.
+ */
+cpr_status_e
+cprSetSockNonBlock (cpr_socket_t soc)
+{
+
+    return ((fcntl(soc, F_SETFL, O_NONBLOCK) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+
+/**
+ * cprSocket
+ *
+ * @brief The cprSocket() is the CPR wrapper for the "socket" API
+ *
+ * The cprSocket() function shall create an unbound socket in a
+ * communications domain, and return a file descriptor that can be used
+ * in later function calls that operate on sockets.
+ *
+ * @param[in] domain  The communications domain, i.e. address family, in which a socket is to
+ *                   be created
+ * @param[in] type    The type of socket to be created. The following types must
+ *                   be supported:
+ *             @li SOCK_STREAM Provides sequenced, reliable, bidirectional, connection-mode
+ *                               byte streams, i.e. TCP
+ *             @li SOCK_DGRAM  Provides connectionless-mode, unreliable
+ *                           datagrams of fixed maximum length, i.e. UDP
+ *             @li SOCK_SEQPACKET   Provides sequenced, reliable, bidirectional, connection-mode
+ *                          transmission paths for records.  A single operation never transfers part of
+ *                          more than one record.  Record boundaries are visible to the receiver via the
+ *                          MSG_EOR flag.
+ * @param[in] protocol    The protocol to be used with the socket.
+ *
+ * @return Upon successful completion, a socket handle defined by
+ *      cpr_socket_t shall be returned; otherwise, INVALID_SOCKET shall be returned and
+ *       cpr_errno set to indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EINVAL]   The specified option is invalid or the socket is
+ *                           shut down
+ *        @li [CPR_EMFILE]   No more socket descriptors available for the
+ *                           process
+ *        @li [CPR_ENFILE]   No more socket descriptors available for the
+ *                           system
+ *        @li [CPR_EPROTOTYPE] The socket type is not supported by the
+ *                              protocol
+ *        @li [CPR_EPROTONOSUPPORT]   The protocol is not supported for the
+ *                                    domain
+ *
+ */
+cpr_socket_t
+cprSocket (uint32_t domain,
+           uint32_t type,
+           uint32_t protocol)
+{
+    cpr_socket_t s;
+
+    s = socket((int)domain, (int)type, (int)protocol);
+    if (s == -1) {
+        return INVALID_SOCKET;
+    }
+    return s;
+}
+
+/**
+ * @}
+ */
+
+
+
+/* cpr_inet_pton
+ *     Convert from presentation format (which usually means ASCII printable)
+ *     to network format (which is usually some kind of binary format).
+ * @param[in] af The address family IPv4 or IPv6
+ * @param[in] src The address that needs to be converted
+ * @param[out] dst The address after the conversion
+ * @return
+ *     1 if the address was valid for the specified address family
+ *     0 if the address wasn't valid (`dst' is untouched in this case)
+ *     -1 if some other error occurred (`dst' is untouched in this case, too)
+ */
+int
+cpr_inet_pton (int af, const char *src, void *dst)
+{
+
+       switch (af) {
+       case AF_INET:
+               return (cpr_inet_pton4(src, dst, 1));
+       case AF_INET6:
+               return (cpr_inet_pton6(src, dst));
+       default:
+               return (-1);
+       }
+       /* NOTREACHED */
+}
+
+
+/**
+ *  Utility function that sets up the socket address, using
+ *  the name and the pid to guarantee uniqueness
+ *
+ *  @param[in] addr - socket fd to bind with the IPC address.
+ *  @param[in] name - pointer to the name of socket to bind to.
+ *
+ *
+ *  @pre  (name != NULL)
+ */
+void cpr_set_sockun_addr (cpr_sockaddr_un_t *addr, const char *name, pid_t pid)
+{
+    /* Bind to the local socket */
+    memset(addr, 0, sizeof(cpr_sockaddr_un_t));
+    addr->sun_family = AF_LOCAL;
+    snprintf((char *) addr->sun_path, sizeof(addr->sun_path), "%s_%d", name, pid);
+}
+
+/* int
+ * inet_pton4(src, dst, pton)
+ *     when last arg is 0: inet_aton(). with hexadecimal, octal and shorthand.
+ *     when last arg is 1: inet_pton(). decimal dotted-quad only.
+ * return:
+ *     1 if `src' is a valid input, else 0.
+ * notice:
+ *     does not touch `dst' unless it's returning 1.
+ */
+static int
+cpr_inet_pton4(const char *src, uint8_t *dst, int pton)
+{
+       uint32_t val;
+       uint32_t digit;
+       int base, n;
+       unsigned char c;
+       uint32_t parts[4];
+       uint32_t *pp = parts;
+
+       c = *src;
+       for (;;) {
+               /*
+                * Collect number up to ``.''.
+                * Values are specified as for C:
+                * 0x=hex, 0=octal, isdigit=decimal.
+                */
+               if (!isdigit(c))
+                       return (0);
+               val = 0; base = 10;
+               if (c == '0') {
+                       c = *++src;
+                       if (c == 'x' || c == 'X')
+                               base = 16, c = *++src;
+                       else if (isdigit(c) && c != '9')
+                               base = 8;
+               }
+               /* inet_pton() takes decimal only */
+               if (pton && base != 10)
+                       return (0);
+               for (;;) {
+                       if (isdigit(c)) {
+                               digit = c - '0';
+                               if (digit >= (uint16_t)base)
+                                       break;
+                               val = (val * base) + digit;
+                               c = *++src;
+                       } else if (base == 16 && isxdigit(c)) {
+                               digit = c + 10 - (islower(c) ? 'a' : 'A');
+                               if (digit >= 16)
+                                       break;
+                               val = (val << 4) | digit;
+                               c = *++src;
+                       } else
+                               break;
+               }
+               if (c == '.') {
+                       /*
+                        * Internet format:
+                        *      a.b.c.d
+                        *      a.b.c   (with c treated as 16 bits)
+                        *      a.b     (with b treated as 24 bits)
+                        *      a       (with a treated as 32 bits)
+                        */
+                       if (pp >= parts + 3)
+                               return (0);
+                       *pp++ = val;
+                       c = *++src;
+               } else
+                       break;
+       }
+       /*
+        * Check for trailing characters.
+        */
+       if (c != '\0' && !isspace(c))
+               return (0);
+       /*
+        * Concoct the address according to
+        * the number of parts specified.
+        */
+       n = pp - parts + 1;
+       /* inet_pton() takes dotted-quad only.  it does not take shorthand. */
+       if (pton && n != 4)
+               return (0);
+       switch (n) {
+
+       case 0:
+               return (0);             /* initial nondigit */
+
+       case 1:                         /* a -- 32 bits */
+               break;
+
+       case 2:                         /* a.b -- 8.24 bits */
+               if (parts[0] > 0xff || val > 0xffffff)
+                       return (0);
+               val |= parts[0] << 24;
+               break;
+
+       case 3:                         /* a.b.c -- 8.8.16 bits */
+               if ((parts[0] | parts[1]) > 0xff || val > 0xffff)
+                       return (0);
+               val |= (parts[0] << 24) | (parts[1] << 16);
+               break;
+
+       case 4:                         /* a.b.c.d -- 8.8.8.8 bits */
+               if ((parts[0] | parts[1] | parts[2] | val) > 0xff)
+                       return (0);
+               val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+               break;
+       }
+       if (dst) {
+               val = htonl(val);
+               memcpy(dst, &val, INADDRSZ);
+       }
+       return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ *     convert presentation level address to network order binary form.
+ * return:
+ *     1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ *     (1) does not touch `dst' unless it's returning 1.
+ *     (2) :: in a full address is silently ignored.
+ */
+static int
+cpr_inet_pton6(const char *src, uint8_t *dst)
+{
+       static const char xdigits_l[] = "0123456789abcdef",
+                         xdigits_u[] = "0123456789ABCDEF";
+       uint8_t tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+       const char *xdigits, *curtok;
+       int ch, saw_xdigit;
+       unsigned int val;
+
+       memset((tp = tmp), '\0', IN6ADDRSZ);
+       endp = tp + IN6ADDRSZ;
+       colonp = NULL;
+       /* Leading :: requires some special handling. */
+       if (*src == ':')
+               if (*++src != ':')
+                       return (0);
+       curtok = src;
+       saw_xdigit = 0;
+       val = 0;
+       while ((ch = *src++) != '\0') {
+               const char *pch;
+
+               if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+                       pch = strchr((xdigits = xdigits_u), ch);
+               if (pch != NULL) {
+                       val <<= 4;
+                       val |= (pch - xdigits);
+                       if (val > 0xffff)
+                               return (0);
+                       saw_xdigit = 1;
+                       continue;
+               }
+               if (ch == ':') {
+                       curtok = src;
+                       if (!saw_xdigit) {
+                               if (colonp)
+                                       return (0);
+                               colonp = tp;
+                               continue;
+                       } else if (*src == '\0')
+                               return (0);
+                       if (tp + INT16SZ > endp)
+                               return (0);
+                       *tp++ = (uint8_t) (val >> 8) & 0xff;
+                       *tp++ = (uint8_t) val & 0xff;
+                       saw_xdigit = 0;
+                       val = 0;
+                       continue;
+               }
+               if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+                   cpr_inet_pton4(curtok, tp, 1) > 0) {
+                       tp += INADDRSZ;
+                       saw_xdigit = 0;
+                       break;  /* '\0' was seen by inet_pton4(). */
+               }
+               return (0);
+       }
+       if (saw_xdigit) {
+               if (tp + INT16SZ > endp)
+                       return (0);
+               *tp++ = (uint8_t) (val >> 8) & 0xff;
+               *tp++ = (uint8_t) val & 0xff;
+       }
+       if (colonp != NULL) {
+               /*
+                * Since some memmove()'s erroneously fail to handle
+                * overlapping regions, we'll do the shift by hand.
+                */
+               const int n = tp - colonp;
+               int i;
+
+               if (tp == endp)
+                       return (0);
+               for (i = 1; i <= n; i++) {
+                       endp[- i] = colonp[n - i];
+                       colonp[n - i] = 0;
+               }
+               tp = endp;
+       }
+       if (tp != endp)
+               return (0);
+       memcpy(dst, tmp, IN6ADDRSZ);
+       return (1);
+}
+
diff --git a/libs/sipcc/cpr/android/cpr_android_socket.h b/libs/sipcc/cpr/android/cpr_android_socket.h
new file mode 100644 (file)
index 0000000..36aff45
--- /dev/null
@@ -0,0 +1,351 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_SOCKET_H_
+#define _CPR_ANDROID_SOCKET_H_
+
+#include "cpr_types.h"
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+/**
+ * Set public CPR header file options
+ */
+#ifdef CPR_USE_SOCKETPAIR
+#undef CPR_USE_SOCKETPAIR
+#endif
+#define SUPPORT_CONNECT_CONST const
+
+/**
+ * Define SOCKET_ERROR
+ */
+#define SOCKET_ERROR   (-1)
+
+/**
+ * Define INVALID_SOCKET
+ */
+#define INVALID_SOCKET (-1)
+
+/**
+ * Define cpr_socket_t
+ */
+typedef int cpr_socket_t;
+
+/**
+ * Define cpr_socklen_t
+ */
+typedef socklen_t cpr_socklen_t;
+
+/**
+ * Address family, defined in sys/socket.h
+ *  AF_UNSPEC
+ *  AF_LOCAL / AF_UNIX
+ *  AF_INET
+ *  AF_INET6
+ *  AF_MAX
+ *
+ *  AF_NETLYR2 (unique to CNU) interface directly to layer 2, bypass IP
+ */
+#ifndef AF_UNIX
+#define AF_UNIX  AF_LOCAL
+#endif
+
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981, based on the BSD file netinet/in.h.
+ * IPv6 additions per RFC 2292.
+ */
+
+
+/*
+ * Define the following socket options as needed
+ *   SO_DEBUG
+ *   SO_ACCEPTCONN
+ *   SO_REUSEADDR / SO_EXCLUSIVEADDRUSE
+ *   SO_KEEPALIVE
+ *   SO_DONTROUTE
+ *   SO_BROADCAST
+ *   SO_USELOOPBACK
+ *   SO_LINGER / SO_DONTLINGER
+ *   SO_OOBINLINE
+ *   SO_SNDBUF
+ *   SO_RCVBUF
+ *   SO_ERROR
+ *   SO_TYPE
+ *
+ * The following options are available for Unix-only variants
+ *   SO_SNDLOWAT
+ *   SO_RCVLOWAT
+ *   SO_SNDTIMEO
+ *   SO_RCVTIMEO
+ *   SO_PROTOTYPE - Not documented as being supported by CNU
+ */
+
+/* defined in netinet/in.h */
+#define SO_DONTLINGER       ((int)(~SO_LINGER))
+#define SO_EXCLUSIVEADDRUSE ((int)(~SO_REUSEADDR))
+
+/*
+ * Protocols (Base),
+ * reference http://www.iana.org/assignments/protocol-numbers.html
+ *   IPPROTO_IP
+ *   IPPROTO_GGP
+ *   IPPROTO_ICMP
+ *   IPPROTO_IGMP
+ *   IPPROTO_IPV4 / IPPROTO_IPIP
+ *   IPPROTO_TCP
+ *   IPPROTO_EGP
+ *   IPPROTO_PUP
+ *   IPPROTO_UDP
+ *   IPPROTO_IDP
+ *   IPPROTO_IPV6
+ *   IPPROTO_ROUTING
+ *   IPPROTO_FRAGMENT
+ *   IPPROTO_ESP
+ *   IPPROTO_AH
+ *   IPPROTO_ICMPV6
+ *   IPPROTO_NONE
+ *   IPPROTO_DSTOPTS
+ *   IPPROTO_ND
+ *   IPPROTO_EON
+ *   IPPROTO_IGRP
+ *   IPPROTO_ENCAP
+ *   IPPROTO_IPCOMP
+ *   IPPROTO_RAW
+ *   IPPROTO_MAX
+ */
+
+/* defined in netinet/in.h */
+#ifndef IPPROTO_IPV4
+#define IPPROTO_IPV4         4
+#endif
+
+#ifndef IPPROTO_IPIP
+#define IPPROTO_IPIP  IPPROTO_IPV4
+#endif
+
+//#define IPPROTO_RSVP        46
+#define IPPROTO_IGRP        88  /* Cisco/GXS IGRP */
+#define IPPROTO_EIGRP       88
+#define IPPROTO_IPCOMP     108  /* IP payload compression */
+
+/*
+ * Protocols (IPv6)
+ *   Assumming if IPV6 is not there, then none of the are
+ */
+#ifndef IPPROTO_IPV6
+#define IPPROTO_HOPOPTS      0  /* IPv6 hop-by-hop options */
+#define IPPROTO_IPV6        41  /* IPv6 */
+#define IPPROTO_ROUTING     43  /* IPv6 routing header */
+#define IPPROTO_FRAGMENT    44  /* IPv6 fragmentation header */
+#define IPPROTO_ICMPV6      58  /* ICMPv6 */
+#define IPPROTO_NONE        59  /* IPv6 no next header */
+#define IPPROTO_DSTOPTS     60  /* IPv6 destination options */
+#endif
+
+/*
+ * Protocols (Local)
+ * reference, RFC 3692
+ *   IPPROTO_UNX       Local sockets Unix protocol
+ *   IPPROTO_CDP       Non-Standard at 254, technially this value
+ *                     is for experimentation and testing
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Port/socket numbers: network standard functions
+ * reference http://www.iana.org/assignments/port-numbers
+ *  IPPORT_ECHO
+ *  IPPORT_DISCARD
+ *  IPPORT_SYSTAT
+ *  IPPORT_DAYTIME
+ *  IPPORT_NETSTAT
+ *  IPPORT_FTP
+ *  IPPORT_SSH
+ *  IPPORT_TELNET
+ *  IPPORT_SMTP
+ *  IPPORT_TIMESERVER
+ *  IPPORT_NAMESERVER
+ *  IPPORT_WHOIS
+ *  IPPORT_MTP
+ *  IPPORT_HTTP
+ *  IPPORT_NTP
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Port/socket numbers: host specific functions
+ *
+ *  IPPORT_TFTP
+ *  IPPORT_RJE
+ *  IPPORT_FINGER
+ *  IPPORT_TTYLINK
+ *  IPPORT_SUPDUP
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * UNIX TCP sockets
+ *
+ *  IPPORT_EXECSERVER
+ *  IPPORT_LOGINSERVER
+ *  IPPORT_CMDSERVER
+ *  IPPORT_EFSSERVER
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * UNIX UDP sockets
+ *
+ *  IPPORT_BIFFUDP
+ *  IPPORT_WHOSERVER
+ *  IPPORT_ROUTESERVER
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * SCCP sockets
+ */
+#define IPPORT_SCCP       2000
+
+/*
+ * tbd: need to finalize placement.
+ * Define range of ephemeral ports used for Cisco IP Phones.
+ */
+#define CIPPORT_EPH_LOW         0xC000
+#define CIPPORT_EPH_HI          0xCFFF
+
+
+/*
+ * Ports < IPPORT_RESERVED are reserved for
+ * privileged processes (e.g. root).
+ *
+ * IPPORT_RESERVED
+ * IPPORT_USERRESERVED
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Define INADDR constants
+ *   INADDR_ANY
+ *   INADDR_LOOPBACK
+ *   INADDR_BROADCAST
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Define IN_CLASS constants/macros
+ *   IN_CLASS{A|B|C|D}(x)
+ *   IN_CLASS{A|B|C|D}_NET
+ *   IN_CLASS{A|B|C|D}_NSHIFT
+ *   IN_CLASS{A|B|C|D}_HOST
+ *   IN_CLASS{A|B|C|D}_MAX
+ */
+
+
+/*
+ * sockaddr_storage:
+ * Common superset of at least AF_INET, AF_INET6 and AF_LINK sockaddr
+ * structures. Has sufficient size and alignment for those sockaddrs.
+ */
+typedef uint16_t        sa_family_t;
+
+typedef struct
+{
+    sa_family_t sun_family;  /* AF_LOCAL/AF_UNIX */
+    char        sun_path[108];
+} cpr_sockaddr_un_t;
+
+
+/*
+ * Desired maximum size, alignment size and related types.
+ */
+#define _SS_MAXSIZE     256     /* Implementation specific max size */
+
+#define cpr_sun_len(a) sizeof(a)
+void cpr_set_sockun_addr(cpr_sockaddr_un_t *addr, const char *name, pid_t pid);
+
+/*
+ * To represent desired sockaddr max alignment for platform, a
+ * type is chosen which may depend on implementation platform architecture.
+ * Type chosen based on alignment size restrictions from <sys/isa_defs.h>.
+ * We desire to force up to (but no more than) 64-bit (8 byte) alignment,
+ * on platforms where it is possible to do so. (e.g not possible on ia32).
+ * For all currently supported platforms by our implementation
+ * in <sys/isa_defs.h>, (i.e. sparc, sparcv9, ia32, ia64)
+ * type "double" is suitable for that intent.
+ *
+ * Note: Type "double" is chosen over the more obvious integer type int64_t.
+ *   int64_t is not a valid type for strict ANSI/ISO C compilation on ILP32.
+ */
+typedef double          sockaddr_maxalign_t;
+
+#define _SS_ALIGNSIZE   (sizeof (sockaddr_maxalign_t))
+
+/*
+ * Definitions used for sockaddr_storage structure paddings design.
+ */
+#define _SS_PAD1SIZE    (_SS_ALIGNSIZE - sizeof (sa_family_t))
+#define _SS_PAD2SIZE    (_SS_MAXSIZE - (sizeof (sa_family_t)+ \
+                        _SS_PAD1SIZE + _SS_ALIGNSIZE))
+
+#ifndef __cplusplus
+typedef struct cpr_sockaddr_storage sockaddr_storage;
+#endif
+
+/*
+ * IP level options
+ *   IP_HDRINCL
+ *   IP_TOS
+ *   IP_TTL
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * TCP level options
+ *   TCP_NODELAY
+ *   TCP_MAXSEG
+ *   TCP_KEEPALIVE
+ */
+
+/* defined in netinet/tcp.h */
+
+
+/* TODO: Still to cleanup */
+
+/*
+ * WinSock 2 extension -- new options
+ */
+#define SO_MAX_MSG_SIZE   0x2003    /* maximum message size */
+
+
+#define SO_NBIO                 0x0400  /* Nonblocking socket I/O operation */
+#define SO_ASYNC                0x0800  /* should send asyn notification of
+                                         * I/O events */
+#define SO_VRFTABLEID           0x1000  /* set VRF routing table id */
+#define SO_SRC_SPECIFIED        0x2000  /* Specified Source Address to be used */
+#define SO_STRICT_ADDR_BIND     0x4000  /* Accept only those packets that have
+                                         * been sent to the address that this
+                                         * socket is bound to */
+/*
+ * Used for getting random port for tls connect
+ */
+#define TCP_PORT_RETRY_CNT      5
+#define TCP_PORT_MASK           0xfff
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_stdio.c b/libs/sipcc/cpr/android/cpr_android_stdio.c
new file mode 100644 (file)
index 0000000..48b101b
--- /dev/null
@@ -0,0 +1,148 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "CSFLog.h"
+
+/**
+ * @def LOG_MAX
+ *
+ * Constant represents the maximum allowed length for a message
+ */
+#define LOG_MAX 1024
+
+/**
+ * @addtogroup DebugAPIs The CPR Logging Abstractions
+ * @ingroup CPR
+ * @brief The CPR Debug/Logging APIs
+ *
+ * @{
+ */
+
+/**
+ * Debug message
+ *
+ * @param _format  format string
+ * @param ...      variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+int
+buginf (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    va_list ap;
+    int rc;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+    if (rc <= 0) {
+        return rc;
+    }
+
+  CSFLogDebug("cpr", "%s", fmt_buf);
+
+  return rc;
+}
+
+/**
+ * Debug message that can be larger than #LOG_MAX
+ *
+ * @param str - a fixed constant string
+ *
+ * @return  zero(0)
+ *
+ * @pre (str not_eq NULL)
+ */
+int
+buginf_msg (const char *str)
+{
+    char buf[LOG_MAX + 1];
+    const char *p;
+    int16_t len;
+
+    // terminate buffer
+    buf[LOG_MAX] = NUL;
+
+    len = (int16_t) strlen(str);
+
+    if (len > LOG_MAX) {
+        p = str;
+        do {
+            memcpy(buf, p, LOG_MAX);
+            p += LOG_MAX;
+            len -= LOG_MAX;
+
+            printf("%s",buf);
+        } while (len > LOG_MAX);
+
+        if (len) {
+          CSFLogDebug("cpr", "%s", (char *)p);
+        }
+    } else {
+      CSFLogDebug("cpr", "%s", (char *) str);
+    }
+
+    return 0;
+}
+
+/**
+ * Error message
+ *
+ * @param _format  format string
+ * @param ...     variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+void
+err_msg (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    va_list ap;
+    int rc;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+    if (rc <= 0) {
+        return;
+    }
+
+    CSFLogError("cpr", "%s", fmt_buf);
+}
+
+
+/**
+ * Notice message
+ *
+ * @param _format  format string
+ * @param ...     variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+void
+notice_msg (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    va_list ap;
+    int rc;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+    if (rc <= 0) {
+        return;
+    }
+
+    CSFLogInfo("cpr", "%s", fmt_buf);
+}
+
diff --git a/libs/sipcc/cpr/android/cpr_android_stdio.h b/libs/sipcc/cpr/android/cpr_android_stdio.h
new file mode 100644 (file)
index 0000000..0d12d27
--- /dev/null
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_STDIO_H_
+#define _CPR_ANDROID_STDIO_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_string.c b/libs/sipcc/cpr/android/cpr_android_string.c
new file mode 100644 (file)
index 0000000..099fc73
--- /dev/null
@@ -0,0 +1,173 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_strings.h"
+
+
+/**
+ * cpr_strdup
+ *
+ * @brief The CPR wrapper for strdup
+
+ * The cpr_strdup shall return a pointer to a new string, which is a duplicate
+ * of the string pointed to by "str" argument. A null pointer is returned if the
+ * new string cannot be created.
+ *
+ * @param[in] str  - The string that needs to be duplicated
+ *
+ * @return The duplicated string or NULL in case of no memory
+ *
+ */
+char *
+cpr_strdup (const char *str)
+{
+    char *dup;
+    size_t len;
+
+    if (!str) {
+        return (char *) NULL;
+    }
+
+    len = strlen(str);
+    if (len == 0) {
+        return (char *) NULL;
+    }
+    len++;
+
+    dup = cpr_malloc(len * sizeof(char));
+    if (!dup) {
+        return (char *) NULL;
+    }
+    (void) memcpy(dup, str, len);
+    return dup;
+}
+
+
+/**
+ * cpr_strcasecmp
+ *
+ * @brief The CPR wrapper for strcasecmp
+ *
+ * The cpr_strcasecmp performs case insensitive string comparison of the "s1"
+ * and the "s2" strings.
+ *
+ * @param[in] s1  - The first string
+ * @param[in] s2  - The second string
+ *
+ * @return integer <,=,> 0 depending on whether s1 is <,=,> s2
+ */
+#ifndef CPR_USE_OS_STRCASECMP
+int
+cpr_strcasecmp (const char *s1, const char *s2)
+{
+    const unsigned char *us1 = (const unsigned char *) s1;
+    const unsigned char *us2 = (const unsigned char *) s2;
+
+    /* No match if only one ptr is NULL */
+    if ((!s1 && s2) || (s1 && !s2)) {
+        /*
+         * If one of these is NULL it will be the lesser of the two
+         * values and therefore we'll get the proper sign in the int
+         */
+        return (int) (s1 - s2);
+    }
+
+    /* Match if both ptrs the same (e.g. NULL) */
+    if (s1 == s2)
+        return 0;
+
+    while (*us1 != '\0' && *us2 != '\0' && toupper(*us1) == toupper(*us2)) {
+        us1++;
+        us2++;
+    }
+
+    return (toupper(*us1) - toupper(*us2));
+}
+
+/**
+ * cpr_strncasecmp
+ *
+ * @brief The CPR wrapper for strncasecmp
+ *
+ * The cpr_strncasecmp performs case insensitive string comparison for specific
+ * length "len".
+ *
+ * @param[in] s1  - The first string
+ * @param[in] s2  - The second string
+ * @param[in] len  - The length to be compared
+ *
+ * @return integer <,=,> 0 depending on whether s1 is <,=,> s2
+ */
+int
+cpr_strncasecmp (const char *s1, const char *s2, size_t len)
+{
+    const unsigned char *us1 = (const unsigned char *) s1;
+    const unsigned char *us2 = (const unsigned char *) s2;
+
+    /* No match if only one ptr is NULL */
+    if ((!s1 && s2) || (s1 && !s2))
+        return ((int) (s1 - s2));
+
+    if ((len == 0) || (s1 == s2))
+        return 0;
+
+    while (len-- > 0 && toupper(*us1) == toupper(*us2)) {
+        if (len == 0 || *us1 == '\0' || *us2 == '\0')
+            break;
+        us1++;
+        us2++;
+    }
+
+    return (toupper(*us1) - toupper(*us2));
+}
+#endif
+
+/**
+ * strcasestr
+ *
+ * @brief The same as strstr, but ignores case
+ *
+ * The strcasestr performs the strstr function, but ignores the case.
+ * This function shall locate the first occurrence in the string
+ * pointed to by s1 of the sequence of bytes (excluding the terminating
+ * null byte) in the string pointed to by s2.
+ *
+ * @param[in] s1  - The input string
+ * @param[in] s2  - The pattern to be matched
+ *
+ * @return A pointer to the first occurrence of string s2 found
+ *           in string s1 or NULL if not found.  If s2 is an empty
+ *           string then s1 is returned.
+ */
+char *
+strcasestr (const char *s1, const char *s2)
+{
+    unsigned int i;
+
+    if (!s1)
+        return (char *) NULL;
+
+    if (!s2 || (s1 == s2) || (*s2 == '\0'))
+        return (char *) s1;
+
+    while (*s1) {
+        i = 0;
+        do {
+            if (s2[i] == '\0')
+                return (char *) s1;
+            if (s1[i] == '\0')
+                return (char *) NULL;
+            if (toupper(s1[i]) != toupper(s2[i]))
+                break;
+            i++;
+        } while (1);
+        s1++;
+    }
+
+    return (char *) NULL;
+}
+
diff --git a/libs/sipcc/cpr/android/cpr_android_string.h b/libs/sipcc/cpr/android/cpr_android_string.h
new file mode 100644 (file)
index 0000000..963d398
--- /dev/null
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_STRING_H_
+#define _CPR_ANDROID_STRING_H_
+
+#include <string.h>
+#include <ctype.h>
+
+/**
+ * cpr_strdup
+ *
+ * @brief The CPR wrapper for strdup
+
+ * The cpr_strdup shall return a pointer to a new string, which is a duplicate
+ * of the string pointed to by "str" argument. A null pointer is returned if the
+ * new string cannot be created.
+ *
+ * @param[in] str  - The string that needs to be duplicated
+ *
+ * @return The duplicated string or NULL in case of no memory
+ *
+ */
+char *
+cpr_strdup(const char *str);
+
+
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_strings.h b/libs/sipcc/cpr/android/cpr_android_strings.h
new file mode 100644 (file)
index 0000000..0dfd75a
--- /dev/null
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_STRINGS_H_
+#define _CPR_ANDROID_STRINGS_H_
+
+#include <strings.h>
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_threads.c b/libs/sipcc/cpr/android/cpr_android_threads.c
new file mode 100644 (file)
index 0000000..2be26dc
--- /dev/null
@@ -0,0 +1,203 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include <pthread.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#define ANDROID_MIN_THREAD_PRIORITY (-20)      /* tbd: check MV linux: current val from Larry port */
+#define ANDROID_MAX_THREAD_PRIORITY (+19)      /* tbd: check MV linux. current val from Larry port */
+
+
+/**
+ * cprCreateThread
+ *
+ * @brief Create a thread
+ *
+ *  The cprCreateThread function creates another execution thread within the
+ *  current process. If the input parameter "name" is present, then this is used
+ *  for debugging purposes. The startRoutine is the address of the function where
+ *  the thread execution begins. The start routine prototype is defined as
+ *  follows
+ *  @code
+ *     int32_t (*cprThreadStartRoutine)(void* data)
+ *  @endcode
+ *
+ * @param[in]  name         - name of the thread created (optional)
+ * @param[in]  startRoutine - function where thread execution begins
+ * @param[in]  stackSize    - size of the thread's stack
+ * @param[in]  priority     - thread's execution priority
+ * @param[in]  data         - parameter to pass to startRoutine
+ *
+ * Return Value: Thread handle or NULL if creation failed.
+ */
+cprThread_t
+cprCreateThread (const char *name,
+                 cprThreadStartRoutine startRoutine,
+                 uint16_t stackSize,
+                 uint16_t priority,
+                 void *data)
+{
+    static const char fname[] = "cprCreateThread";
+    static uint16_t id = 0;
+    cpr_thread_t *threadPtr;
+    pthread_t threadId;
+    pthread_attr_t attr;
+
+    CPR_INFO("%s: creating '%s' thread\n", fname, name);
+
+    /* Malloc memory for a new thread */
+    threadPtr = (cpr_thread_t *)cpr_malloc(sizeof(cpr_thread_t));
+    if (threadPtr != NULL) {
+        if (pthread_attr_init(&attr) != 0) {
+
+            CPR_ERROR("%s - Failed to init attribute for thread %s\n",
+                      fname, name);
+            cpr_free(threadPtr);
+            return (cprThread_t)NULL;
+        }
+
+        if (pthread_attr_setstacksize(&attr, stackSize) != 0) {
+            CPR_ERROR("%s - Invalid stacksize %d specified for thread %s\n",
+                      fname, stackSize, name);
+            cpr_free(threadPtr);
+            return (cprThread_t)NULL;
+        }
+
+        if (pthread_create(&threadId, &attr, startRoutine, data) != 0) {
+            CPR_ERROR("%s - Creation of thread %s failed: %d\n",
+                      fname, name, errno);
+            cpr_free(threadPtr);
+            return (cprThread_t)NULL;
+        }
+
+        /* Assign name to CPR if one was passed in */
+        if (name != NULL) {
+            threadPtr->name = name;
+        }
+
+        /*
+         * TODO - It would be nice for CPR to keep a linked
+         * list of running threads for debugging purposes
+         * such as a show command or walking the list to ensure
+         * that an application does not attempt to create
+         * the same thread twice.
+         */
+        threadPtr->u.handleInt = threadId;
+        threadPtr->threadId = ++id;
+        return (cprThread_t)threadPtr;
+    }
+
+    /* Malloc failed */
+    CPR_ERROR("%s - Malloc for thread %s failed.\n", fname, name);
+    errno = ENOMEM;
+    return (cprThread_t)NULL;
+}
+
+
+/**
+ * cprDestroyThread
+ *
+ * @brief Destroys the thread passed in.
+ *
+ * The cprDestroyThread function is called to destroy a thread. The thread
+ * parameter may be any valid thread including the calling thread itself.
+ *
+ * @param[in] thread - thread to destroy.
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE. errno should be set for FAILURE case.
+ *
+ * @note In Linux there will never be a success indication as the
+ *       calling thread will have been terminated.
+ */
+cprRC_t
+cprDestroyThread (cprThread_t thread)
+{
+    static const char fname[] = "cprDestroyThread";
+    cpr_thread_t *cprThreadPtr;
+
+    cprThreadPtr = (cpr_thread_t *) thread;
+    if (cprThreadPtr != NULL) {
+        /*
+         * Make sure thread is trying to destroy itself.
+         */
+        if ((pthread_t) cprThreadPtr->u.handleInt == pthread_self()) {
+            cprThreadPtr->threadId = 0;
+            cpr_free(cprThreadPtr);
+            pthread_exit(NULL);
+            return CPR_SUCCESS;
+        }
+
+        CPR_ERROR("%s: Thread attempted to destroy another thread, not itself.\n",
+                  fname);
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+/**
+ * cprAdjustRelativeThreadPriority
+ *
+ * @brief The function sets the relative thread priority up or down by the given value.
+ *
+ * This function is used pSIPCC to set up the thread priority. The values of the
+ * priority range from -20 (Maximum priority) to +19 (Minimum priority).
+ *
+ * @param[in] relPri - nice value of the thread -20 is MAX and 19 is MIN
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprAdjustRelativeThreadPriority (int relPri)
+{
+    const char *fname = "cprAdjustRelativeThreadPriority";
+
+    if (setpriority(PRIO_PROCESS, 0, relPri) == -1) {
+        CPR_ERROR("%s: could not set the nice..err=%d\n",
+                  fname, errno);
+        return CPR_FAILURE;
+    }
+    return CPR_SUCCESS;
+}
+
+/**
+ * @}
+ * @addtogroup ThreadInternal Helper functions for implementing threads in CPR
+ * @ingroup Threads
+ * @brief Helper functions used by CPR for thread implementation
+ *
+ * @{
+ */
+
+/**
+ * cprGetThreadId
+ *
+ * @brief Return the pthread ID for the given CPR thread.
+ *
+ * @param[in] thread - thread to query
+ *
+ * @return Thread's Id or zero(0)
+ *
+ */
+pthread_t
+cprGetThreadId (cprThread_t thread)
+{
+    if (thread) {
+        return ((cpr_thread_t *)thread)->u.handleInt;
+    }
+    return 0;
+}
+
+/**
+  * @}
+  */
diff --git a/libs/sipcc/cpr/android/cpr_android_time.h b/libs/sipcc/cpr/android/cpr_android_time.h
new file mode 100644 (file)
index 0000000..1ebba15
--- /dev/null
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_TIME_H_
+#define _CPR_ANDROID_TIME_H_
+
+#include <time.h>
+
+typedef size_t cpr_time_t;
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_timers.h b/libs/sipcc/cpr/android/cpr_android_timers.h
new file mode 100644 (file)
index 0000000..1091448
--- /dev/null
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_TIMERS_H_
+#define _CPR_ANDROID_TIMERS_H_
+#include <pthread.h>
+
+/*
+ * Linux does not provide native support for non-blocking timers
+ * so CPR provides that functionality for Linux.
+ */
+
+/*
+ * Determine the granularity of the timers in
+ * milliseconds. ie how often does the TickThread
+ * wake up to decrement the timer intervals
+ */
+#define timerGranularity 10
+
+//struct timerBlk_s timerBlk;
+
+typedef struct cpr_timer_s
+{
+  const char *name;
+  uint32_t cprTimerId;
+  cprMsgQueue_t callBackMsgQueue;
+  uint16_t applicationTimerId;
+  uint16_t applicationMsgId;
+  void *data;
+  union {
+    void *handlePtr;
+  }u;
+}cpr_timer_t;
+
+/* Linked List of currently running timers */
+typedef struct timerDef
+{
+    int32_t duration;
+    boolean timerActive;
+    cpr_timer_t *cprTimerPtr;
+    struct timerDef *previous;
+    struct timerDef *next;
+} timerBlk;
+
+/* Timer mutex for updating the timer linked list */
+extern pthread_mutex_t timerMutex;
+
+/* Start routines for the timer threads */
+extern void *linuxTimerTick(void *);
+
+cprRC_t cpr_timer_pre_init(void);
+cprRC_t cpr_timer_de_init(void);
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_timers_using_select.c b/libs/sipcc/cpr/android/cpr_android_timers_using_select.c
new file mode 100644 (file)
index 0000000..98b02a4
--- /dev/null
@@ -0,0 +1,1300 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *  @brief CPR layer for Timers.
+ *
+ *     This file contains the Cisco Portable Runtime layer for non-blocking
+ *     timers. This implementation is for the Linux operating system using
+ *     select with a timeout.
+ *
+ *     Timer Service runs in its own thread and blocks on select
+ *     call with a timeout. The value of timeout is set equal to
+ *     the duration of the earliest expiring timer. The timer list
+ *     is kept sorted by earliest to latest expiration times. Therefore,
+ *     timeout value is simply the duration on the head of the list.
+ *
+ *     For starting or cancelling a timer, the timer library contacts
+ *     the timer service using a local socket based IPC. This is done
+ *     by bringing up a connection between timer service and the client
+ *     during timer init.
+ *     Timer Service thread is the only thread that adds or removes
+ *     timer blocks from the list. When timer library client wants
+ *     to start or cancel a timer, the library sends an IPC message
+ *     to the timer service thread on the socket connection. This
+ *     unblocks the select call. Select also returns the amount of
+ *     time left on the timeout when it gets unblocked. This amount is
+ *     first subtracted from the head timer block duration. Then rest
+ *     of the processing is carried out based on whether the request
+ *     was to add or to remove a timer block and duration
+ *     on all timer blocks is adjusted as required.
+ *     When the select times out it implies the timers at the head
+ *     of the list has expired. The list is scanned for expired timers
+ *     and expiry processing such as posting message to the handler
+ *     etc. is done.
+ *
+ *     When there are no timers in the list, service blocks on select
+ *     forever waiting for at least one timer to be added.
+ *
+ */
+
+/**
+ * @defgroup Timers The Timer implementation module
+ * @ingroup CPR
+ * @brief The module related to Timer abstraction for the pSIPCC
+ * @addtogroup TimerAPIs The Timer APIs
+ * @ingroup Timers
+ * @brief APIs expected by pSIPCC for using Timers
+ *
+ */
+
+#include "cpr.h"
+#include "cpr_socket.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_threads.h"
+#include "cpr_timers.h"
+#include "cpr_string.h"
+#include "phntask.h"
+#include <errno.h>
+#include <unistd.h>
+#include "cpr_android_timers.h"
+#include "platform_api.h"
+
+/*--------------------------------------------------------------------------
+ * Local definitions
+ *--------------------------------------------------------------------------
+ */
+
+typedef struct timer_ipc_cmd_s
+{
+    cpr_timer_t *timer_ptr;
+    void        *user_data_ptr;
+    uint32_t    duration;
+} timer_ipc_cmd_t;
+
+
+typedef struct timer_ipc_s
+{
+    uint32_t  msg_type;
+    union
+    {
+        timer_ipc_cmd_t cmd;
+        cprRC_t         result;
+    }u;
+} timer_ipc_t;
+
+#define TMR_CMD_ADD    1
+#define TMR_CMD_REMOVE 2
+#define TMR_RESULT     3
+
+#define API_RETURN(_val) \
+    {                     \
+        pthread_mutex_unlock(&api_mutex); \
+        return (_val); \
+     }\
+
+#define API_ENTER() \
+{\
+    pthread_mutex_lock(&api_mutex);\
+}\
+
+/* for AF_LOCAL not all implementations return client addr in recvfrom so
+ * using explicit path for client too.
+ */
+#define SERVER_PATH "/tmp/CprTmrServer"
+#define CLIENT_PATH "/tmp/CprTmrClient"
+
+
+/*--------------------------------------------------------------------------
+ * Global data
+ *--------------------------------------------------------------------------
+ */
+
+
+static timerBlk *timerListHead;
+
+static pthread_t timerThreadId;
+
+
+static pthread_mutex_t api_mutex;
+
+
+/* local socket used by timer library */
+static int client_sock = INVALID_SOCKET;
+
+
+/* local socket used by the timer  service */
+static int serv_sock = INVALID_SOCKET;
+
+static struct sockaddr_un tmr_serv_addr;
+static struct sockaddr_un tmr_client_addr;
+
+static fd_set socks; /* descriptor set */
+
+
+/*--------------------------------------------------------------------------
+ * External data references
+ *--------------------------------------------------------------------------
+ */
+
+
+/*--------------------------------------------------------------------------
+ * External function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+/*
+ * Internal CPR function to fill in data in the sysheader.
+ * This is to prevent knowledge of the evil syshdr structure
+ * from spreading to cpr_linux_timers.c This thing is
+ * like kudzu...
+ */
+extern void fillInSysHeader(void *buffer, uint16_t cmd, uint16_t len,
+                            void *timerMsg);
+
+
+
+/*--------------------------------------------------------------------------
+ * Local scope function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+static void     *timerThread(void *data);
+static cprRC_t  start_timer_service_loop();
+static void     process_expired_timers();
+static void     send_api_result(cprRC_t result, struct sockaddr_un *addr, socklen_t len);
+
+
+/**
+ * @addtogroup TimerAPIs The Timer APIs
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * cprSleep
+ *
+ * @brief Suspend the calling thread
+ * The cprSleep function blocks the calling thread for the indicated number of
+ * milliseconds.
+ *
+ * @param[in] duration - Number of milliseconds the thread should sleep
+ *
+ * @return -  none
+ */
+void
+cprSleep (uint32_t duration)
+{
+    /*
+     * usleep() can only support up to one second, so split
+     * between sleep and usleep if one second or more
+     */
+    if (duration >= 1000) {
+        (void) sleep(duration / 1000);
+        (void) usleep((duration % 1000) * 1000);
+    } else {
+        (void) usleep(duration * 1000);
+    }
+}
+
+/**
+  * @}
+  */
+
+/**
+ * @defgroup TimerInternal The Timer internal functions
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * addTimerToList
+ * Send message to timer service to add the timer pointed by cprTimerPtr
+ * to the list. This routine is just sending IPC message to timer service
+ * but the actual addition is done by timer service using the addTimer function.
+ * This function is only called by CPR functions and is not visible to external
+ * applications.
+ * @param[in] cprTimerPtr  - timer pointer
+ * @param[in] duration     - timer duration in msec.
+ * @param[in] data         - opaque data
+ * @return  - CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t addTimerToList (cpr_timer_t *cprTimerPtr, uint32_t duration, void *data)
+{
+
+    static const char fname[] = "addTimerToList";
+    timer_ipc_t tmr_cmd = {0};
+    timer_ipc_t tmr_rsp={0};
+
+    API_ENTER();
+
+    //CPR_INFO("%s: cprTimerptr=0x%x dur=%d user_data=%x\n",
+    //       fname, cprTimerPtr, duration, data);
+    tmr_cmd.msg_type = TMR_CMD_ADD;
+    tmr_cmd.u.cmd.timer_ptr = cprTimerPtr;
+    tmr_cmd.u.cmd.user_data_ptr = data;
+    tmr_cmd.u.cmd.duration = duration;
+
+//CPR_INFO("%s:sending messge of type=%d\n", fname, tmr_cmd.msg_type);
+    /* simply post a request here to the timer service.*/
+    if (client_sock != -1) {
+        if (sendto(client_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
+                   (struct sockaddr *)&tmr_serv_addr, sizeof(tmr_serv_addr)) < 0) {
+            CPR_ERROR("Failed to tx IPC msg to timer service, errno = %s %s\n",
+                   strerror(errno), fname);
+            API_RETURN(CPR_FAILURE);
+        }
+
+    } else {
+        CPR_ERROR("can not make IPC connection, client_sock is invalid %s\n", fname);
+        API_RETURN(CPR_FAILURE);
+    }
+
+    /*
+     * wait for the timer service to excute the request
+     * so that we get result of operation
+     */
+
+    if (recvfrom(client_sock, &tmr_rsp, sizeof(timer_ipc_t),0, NULL, NULL) < 0) {
+        //CPR_INFO("error in recving the result error=%s\n", strerror(errno));
+        API_RETURN(CPR_FAILURE);
+    } else {
+        //CPR_INFO("received response from the timer result=%d\n", tmr_rsp.u.result);
+        API_RETURN(tmr_rsp.u.result);
+    }
+}
+
+
+/**
+ * addTimer
+ *
+ * Add a timer to the timer linked list.
+ * This function is only called by CPR functions and is not visible to external
+ * applications.
+ *
+ * @param[in] cprTimerPtr - pointer to the CPR timer structure
+ * @param[in] duration    - how long before timer expires in milliseconds
+ * @param[in] data        - information to be passed to callback function
+ *
+ * @return  - CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t addTimer (cpr_timer_t *cprTimerPtr, uint32_t duration, void *data)
+{
+    static const char fname[] = "addTimer";
+    timerBlk *timerList;
+    timerBlk *newTimerPtr;
+
+    CPR_INFO("%s:adding timer=0x%x timerblk=%x\n", fname,
+           cprTimerPtr, cprTimerPtr->u.handlePtr);
+
+
+    /* Verify the timer has been initialized */
+    newTimerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+    if (newTimerPtr == NULL) {
+        CPR_ERROR("%s - Timer %s has not been initialized.\n",
+                  fname, cprTimerPtr->name);
+        errno = EINVAL;
+
+        return(CPR_FAILURE);
+    }
+
+    /* Ensure this timer is not already running */
+    if (newTimerPtr->timerActive) {
+        CPR_ERROR("%s - Timer %s is already active.\n", fname, cprTimerPtr->name);
+        errno = EAGAIN;
+        return(CPR_FAILURE);
+
+    }
+
+    /* Sanity tests passed, store the data the application passed in */
+    newTimerPtr->duration = duration;
+    cprTimerPtr->data = data;
+
+    /*
+     * Insert timer into the linked list. The timer code only
+     * decrements the first timer in the list to be efficient.
+     * Therefore, the timer at the top of the list is the timer
+     * that will expire first. Timers are added to the list
+     * in ascending order of time left before expiration and
+     * ticksLeft is calculated to be the difference between
+     * when the timer before them expires and when the newly
+     * inserted timer expires.
+     */
+
+    /* Check for insertion into an empty list */
+    if (timerListHead == NULL) {
+        //CPR_INFO("no timer in the list case..\n");
+        timerListHead = newTimerPtr;
+    } else {
+
+       /* Insert timer into list */
+        timerList = timerListHead;
+        while (timerList != NULL) {
+
+            /*
+             * If the duration on this new timer are less than the
+             * timer in the list, insert this new timer before
+             * it in the list as it will expire first. In doing so
+             * the code must subtract the new timer's duration from
+             * the duration of the timer in the in the list to keep the deltas correct.
+             */
+            if (newTimerPtr->duration < timerList->duration) {
+                //CPR_INFO("less than case..\n");
+                timerList->duration -= newTimerPtr->duration;
+                newTimerPtr->next = timerList;
+                newTimerPtr->previous = timerList->previous;
+                if (newTimerPtr->previous) {
+                    newTimerPtr->previous->next = newTimerPtr;
+                }
+                timerList->previous = newTimerPtr;
+
+                /* Check for insertion at the head of list */
+                if (timerListHead == timerList) {
+                    //CPR_INFO("insert at the head case..\n");
+                    timerListHead = newTimerPtr;
+                }
+                break;
+            } else {
+                /*
+                 * Else this new timer expires after the timer in
+                 * the list. Therefore subtract the timer's duration
+                 * from the new timer's duration. Since only the
+                 * first timer is decremented all other timers added
+                 * expiration must be calculated as a delta from the
+                 * timer in front of them in the list.
+                 */
+                //CPR_INFO("greater than case..\n");
+                newTimerPtr->duration -= timerList->duration;
+
+                /* Check for insertion at the end of list */
+                if (timerList->next == NULL) {
+                    //CPR_INFO("insert at the end sub case..\n");
+                    newTimerPtr->previous = timerList;
+                    timerList->next = newTimerPtr;
+                    newTimerPtr->next = NULL;
+                    break;
+                }
+                timerList = timerList->next;
+            }
+        }
+    }
+
+    newTimerPtr->timerActive = TRUE;
+    return(CPR_SUCCESS);
+}
+
+
+/**
+ * removeTimerFromList
+ * Send message to timer service to remove the timer pointed by cprTimerPtr
+ * from the list. This routine is just sending IPC message to timer service
+ * and the actual removal is done by timer service using the removeTimer function..
+ * This function is only called by CPR functions and is not visible to external
+ * applications.
+ *
+ * @param[in] cprTimerPtr - pointer to the timer to be removed from the list
+ * @return - CPR_SUCCESS or CPR_FAILURE
+ *
+ */
+static cprRC_t
+removeTimerFromList (cpr_timer_t *cprTimerPtr)
+{
+
+    static const char fname[] = "removeTimerFromList";
+    timer_ipc_t tmr_cmd = {0};
+    timer_ipc_t tmr_rsp = {0};
+
+
+    API_ENTER();
+
+    //CPR_INFO("%s:remove timer from list=0x%x\n",fname, cprTimerPtr);
+    tmr_cmd.msg_type = TMR_CMD_REMOVE;
+    tmr_cmd.u.cmd.timer_ptr = cprTimerPtr;
+
+    //CPR_INFO("sending messge of type=%d\n", tmr_cmd.msg_type);
+
+    /* simply post a request here to the timer service.. */
+    if (client_sock != -1) {
+        if (sendto(client_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
+                   (struct sockaddr *)&tmr_serv_addr, sizeof(tmr_serv_addr)) < 0) {
+            CPR_ERROR("%s:failed to tx IPC Msg to timer service, errno = %s\n",
+                      fname, strerror(errno));
+            API_RETURN(CPR_FAILURE);
+        }
+    } else {
+        CPR_ERROR("%s:client_sock invalid, no IPC connection \n", fname);
+        API_RETURN(CPR_FAILURE);
+    }
+
+    /*
+     * wait for the timer service to excute the request
+     * so that we get result of operation
+     */
+
+    if (recvfrom(client_sock, &tmr_rsp, sizeof(timer_ipc_t),0, NULL, NULL) < 0) {
+        //CPR_INFO("error in recving the result error=%s\n", strerror(errno));
+        API_RETURN(CPR_FAILURE);
+    } else {
+        //CPR_INFO("received response from the timer result=%d\n", tmr_rsp.u.result);
+        API_RETURN(tmr_rsp.u.result);
+    }
+}
+
+
+/**
+ * removeTimer
+ *
+ * Remove a timer from the timer linked list. This function is only
+ * called by CPR functions and is not visible to applications.
+ *
+ * @param[in] cprTimerPtr - which timer to cancel
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t
+removeTimer (cpr_timer_t *cprTimerPtr)
+{
+    static const char fname[] = "removeTimer";
+    timerBlk *timerList;
+    timerBlk *previousTimer;
+    timerBlk *nextTimer;
+    timerBlk *timerPtr;
+
+    //CPR_INFO("removing timer..0x%x\n", cprTimerPtr);
+
+    /*
+     * No need to sanitize the cprTimerPtr data as only
+     * internal CPR functions call us and they have already
+     * sanitized that data. In addition those functions
+     * have already grabbed the timer list mutex so no need
+     * to do that here.
+     */
+    timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+    //CPR_INFO("%s: timer ptr=%x\n", fname, timerPtr);
+
+    if (timerPtr != NULL) {
+        /* Walk the list looking for this timer to cancel. */
+        timerList = timerListHead;
+        while (timerList != NULL) {
+            if (timerList->cprTimerPtr->cprTimerId == cprTimerPtr->cprTimerId) {
+                /* Removing only element in the list */
+                if ((timerList->previous == NULL) &&
+                    (timerList->next == NULL)) {
+                    timerListHead = NULL;
+
+                /* Removing head of the list */
+                } else if (timerList->previous == NULL) {
+                    nextTimer = timerList->next;
+                    nextTimer->previous = NULL;
+                    timerListHead = nextTimer;
+
+                /* Removing tail of the list */
+                } else if (timerList->next == NULL) {
+                    previousTimer = timerList->previous;
+                    previousTimer->next = NULL;
+
+                /* Removing from middle of the list */
+                } else {
+                    nextTimer = timerList->next;
+                    previousTimer = timerList->previous;
+                    previousTimer->next = nextTimer;
+                    nextTimer->previous = previousTimer;
+                }
+
+                /* Add time back to next timer in the list */
+                if (timerList->next) {
+                    timerList->next->duration += timerList->duration;
+                }
+
+                /*
+                 * Reset timer values
+                 */
+                timerList->next = NULL;
+                timerList->previous = NULL;
+                timerList->duration = -1;
+                timerList->timerActive = FALSE;
+                cprTimerPtr->data = NULL;
+
+                return(CPR_SUCCESS);
+
+            }
+
+            /* Walk the list */
+            timerList = timerList->next;
+        }
+
+        /*
+         * Either the timer was not active or it was marked active, but not
+         * found on the timer list. If the timer was inactive then that is OK,
+         * but let user know about an active timer not found in the timer list.
+         */
+        if ((timerPtr->next != NULL) || (timerPtr->previous != NULL)) {
+            CPR_ERROR("%s - Timer %s marked as active, "
+                      "but was not found on the timer list.\n",
+                      fname, cprTimerPtr->name);
+            timerPtr->next = NULL;
+            timerPtr->previous = NULL;
+        }
+        timerPtr->duration = -1;
+        timerPtr->cprTimerPtr->data = NULL;
+        timerPtr->timerActive = FALSE;
+
+        return(CPR_SUCCESS);
+
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - Timer not initialized.\n", fname);
+    errno = EINVAL;
+    return(CPR_FAILURE);
+
+}
+
+/**
+ * @}
+ * @addtogroup TimerAPIs The Timer APIs
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * cprCreateTimer
+ *
+ * @brief Initialize a timer
+ *
+ * The cprCreateTimer function is called to allow the OS to perform whatever
+ * work is needed to create a timer. The input name parameter is optional. If present, CPR assigns
+ * this name to the timer to assist in debugging. The callbackMsgQueue is the
+ * address of a message queue created with cprCreateMsgQueue. This is the
+ * queue where the timer expire message will be sent.
+ * So, when this timer expires a msg of type "applicationMsgId" will be sent to the msg queue
+ * "callbackMsgQueue" indicating that timer applicationTimerId has expired.
+ *
+ * @param[in]   name               -  name of the timer
+ * @param[in]   applicationTimerId - ID for this timer from the application's
+ *                                  perspective
+ * @param[in]   applicationMsgId   - ID for syshdr->cmd when timer expire msg
+ *                                  is sent
+ * @param[in]   callBackMsgQueue   - where to send a msg when this timer expires
+ *
+ * @return  Timer handle or NULL if creation failed.
+ */
+cprTimer_t
+cprCreateTimer (const char *name,
+                uint16_t applicationTimerId,
+                uint16_t applicationMsgId,
+                cprMsgQueue_t callBackMsgQueue)
+{
+    static const char fname[] = "cprCreateTimer";
+    static uint32_t cprTimerId = 0;
+    cpr_timer_t *cprTimerPtr;
+    timerBlk *timerPtr;
+
+    /*
+     * Malloc memory for a new timer. Need to
+     * malloc memory for the generic CPR view and
+     * one for the CNU specific version.
+     */
+    cprTimerPtr = (cpr_timer_t *) cpr_malloc(sizeof(cpr_timer_t));
+    timerPtr = (timerBlk *) cpr_malloc(sizeof(timerBlk));
+    if ((cprTimerPtr != NULL) && (timerPtr != NULL)) {
+        /* Assign name (Optional) */
+        cprTimerPtr->name = name;
+
+        /* Set timer ids, msg id and callback msg queue (Mandatory) */
+        cprTimerPtr->applicationTimerId = applicationTimerId;
+        cprTimerPtr->applicationMsgId = applicationMsgId;
+        cprTimerPtr->cprTimerId = cprTimerId++;
+        if (callBackMsgQueue == NULL) {
+            CPR_ERROR("%s - Callback msg queue for timer %s is NULL.\n",
+                      fname, name);
+            cpr_free(timerPtr);
+            cpr_free(cprTimerPtr);
+            return NULL;
+        }
+        cprTimerPtr->callBackMsgQueue = callBackMsgQueue;
+
+        /*
+         * Set remaining values in both structures to defaults
+         */
+        timerPtr->next = NULL;
+        timerPtr->previous = NULL;
+        timerPtr->duration = -1;
+        timerPtr->timerActive = FALSE;
+        cprTimerPtr->data = NULL;
+
+        /*
+         * TODO - It would be nice for CPR to keep a linked
+         * list of active timers for debugging purposes
+         * such as a show command or walking the list to ensure
+         * that an application does not attempt to create
+         * the same timer twice.
+         *
+         * TODO - It would be nice to initialize of pool of
+         * timers at init time and have this function just
+         * return a timer from the pool. Then when the
+         * timer expired or cancel the code would not free
+         * it, but just return it to the pool.
+         */
+        timerPtr->cprTimerPtr = cprTimerPtr;
+        cprTimerPtr->u.handlePtr = timerPtr;
+        //CPR_INFO("cprTimerCreate: timer_t=%x blk=%x\n",cprTimerPtr, timerPtr);
+
+        return cprTimerPtr;
+    }
+
+    /*
+     * If we get here there has been a malloc failure.
+     */
+    if (timerPtr) {
+        cpr_free(timerPtr);
+    }
+    if (cprTimerPtr) {
+        cpr_free(cprTimerPtr);
+    }
+
+    /* Malloc failed */
+    CPR_ERROR("%s - Malloc for timer %s failed.\n", fname, name);
+    errno = ENOMEM;
+    return NULL;
+}
+
+
+/**
+ * cprStartTimer
+ *
+ * @brief Start a system timer
+ *
+ * The cprStartTimer function starts a previously created timer referenced by
+ * the parameter timer. CPR timer granularity is 10ms. The "timer" input
+ * parameter is the handle returned from a previous successful call to
+ * cprCreateTimer.
+ *
+ * @param[in]  timer    - which timer to start
+ * @param[in]  duration - how long before timer expires in milliseconds
+ * @param[in]  data     - information to be passed to callback function
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprStartTimer (cprTimer_t timer,
+               uint32_t duration,
+               void *data)
+{
+    static const char fname[] = "cprStartTimer";
+    cpr_timer_t *cprTimerPtr;
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        /* add timer to the list */
+        return addTimerToList(cprTimerPtr, duration, data);
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprIsTimerRunning
+ *
+ * @brief Determine if a timer is active
+ *
+ * This function determines whether the passed in timer is currently active. The
+ * "timer" parameter is the handle returned from a previous successful call to
+ *  cprCreateTimer.
+ *
+ * @param[in] timer - which timer to check
+ *
+ * @return True is timer is active, False otherwise
+ */
+boolean
+cprIsTimerRunning (cprTimer_t timer)
+{
+    static const char fname[] = "cprIsTimerRunning";
+    cpr_timer_t *cprTimerPtr;
+    timerBlk *timerPtr;
+
+    //CPR_INFO("istimerrunning(): timer=0x%x\n", timer);
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+        if (timerPtr == NULL) {
+            CPR_ERROR("%s - Timer %s has not been initialized.\n",
+                      fname, cprTimerPtr->name);
+            errno = EINVAL;
+            return FALSE;
+        }
+
+        if (timerPtr->timerActive) {
+            return TRUE;
+        }
+    } else {
+        /* Bad application! */
+        CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+        errno = EINVAL;
+    }
+
+    return FALSE;
+}
+
+
+/**
+ * cprCancelTimer
+ *
+ * @brief Cancels a running timer
+ *
+ * The cprCancelTimer function cancels a previously started timer referenced by
+ * the parameter timer.
+ *
+ * @param[in] timer - which timer to cancel
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprCancelTimer (cprTimer_t timer)
+{
+    static const char fname[] = "cprCancelTimer";
+    timerBlk *timerPtr;
+    cpr_timer_t *cprTimerPtr;
+    cprRC_t rc = CPR_SUCCESS;
+
+    //CPR_INFO("cprCancelTimer: timer ptr=%x\n", timer);
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+        if (timerPtr == NULL) {
+            CPR_ERROR("%s - Timer %s has not been initialized.\n",
+                      fname, cprTimerPtr->name);
+            errno = EINVAL;
+            return CPR_FAILURE;
+        }
+
+        /*
+         * Ensure timer is active before trying to remove it.
+         * If already inactive then just return SUCCESS.
+         */
+        if (timerPtr->timerActive) {
+            //CPR_INFO("removing timer from the list=%x\n", timerPtr);
+            rc = removeTimerFromList(timer);
+        }
+        return rc;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprUpdateTimer
+ *
+ * @brief Updates the expiration time for a running timer
+ *
+ * The cprUpdateTimer function cancels a previously started timer referenced by
+ * the parameter timer and then restarts the same timer with the duration passed
+ * in.
+ *
+ * @param[in]   timer    - which timer to update
+ * @param[in]   duration - how long before timer expires in milliseconds
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprUpdateTimer (cprTimer_t timer, uint32_t duration)
+{
+    static const char fname[] = "cprUpdateTimer";
+    cpr_timer_t *cprTimerPtr;
+    void *timerData;
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        /* Grab data before cancelling timer */
+        timerData = cprTimerPtr->data;
+    } else {
+        CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    if (cprCancelTimer(timer) == CPR_SUCCESS) {
+        if (cprStartTimer(timer, duration, timerData) == CPR_SUCCESS) {
+            return CPR_SUCCESS;
+        } else {
+            CPR_ERROR("%s - Failed to start timer %s\n",
+                      fname, cprTimerPtr->name);
+            return CPR_FAILURE;
+        }
+    }
+
+    CPR_ERROR("%s - Failed to cancel timer %s\n", fname, cprTimerPtr->name);
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprDestroyTimer
+ *
+ * @brief Destroys a timer.
+ *
+ * This function will cancel the timer and then destroy it. It sets
+ * all links to NULL and then frees the timer block.
+ *
+ * @param[in] timer - which timer to destroy
+ *
+ * @return  CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprDestroyTimer (cprTimer_t timer)
+{
+    static const char fname[] = "cprDestroyTimer";
+    cpr_timer_t *cprTimerPtr;
+    cprRC_t rc;
+
+    //CPR_INFO("cprDestroyTimer:destroying timer=%x\n", timer);
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        rc = cprCancelTimer(timer);
+        if (rc == CPR_SUCCESS) {
+            cprTimerPtr->cprTimerId = 0;
+            cpr_free(cprTimerPtr->u.handlePtr);
+            cpr_free(cprTimerPtr);
+            return CPR_SUCCESS;
+        } else {
+            CPR_ERROR("%s - Cancel of Timer %s failed.\n",
+                      fname, cprTimerPtr->name);
+            return CPR_FAILURE;
+        }
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+/**
+ * @}
+ * @addtogroup TimerInternal The Timer internal functions
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * cpr_timer_pre_init
+ *
+ * @brief Initalize timer service and client IPC
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cpr_timer_pre_init (void)
+{
+    static const char fname[] = "cpr_timer_pre_init";
+    int32_t returnCode;
+
+    /* start the timer service first */
+    returnCode = (int32_t)pthread_create(&timerThreadId, NULL, timerThread, NULL);
+    if (returnCode == -1) {
+        CPR_ERROR("%s: Failed to create Timer Thread : %s\n", fname, strerror(errno));
+        return CPR_FAILURE;
+    }
+
+    /*
+     * wait some time so that timer service thread is up
+     * TBD:we should really implement wait on timerthread using condvar.
+     */
+    cprSleep(1000);
+
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * cpr_timer_de_init
+ *
+ * @brief De-Initalize timer service and client IPC
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cpr_timer_de_init(void)
+{
+    // close all sockets..
+    close(client_sock);
+    close(serv_sock);
+
+
+    // destroy api mutex
+    pthread_mutex_destroy(&api_mutex);
+
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * Timer service thread
+ *
+ */
+/**
+ * timerThread
+ *
+ * @brief Timer service thread
+ *
+ * This is the start function for the timer server thread.
+ *
+ * @param[in] data - The data passed in (UNUSED)
+ *
+ * @return  This function eventually starts an infinite loop on a "select".
+ */
+void *timerThread (void *data)
+{
+    static const char fname[] = "timerThread";
+
+    //CPR_INFO("timerThread:started..\n");
+#ifndef HOST
+#ifndef PTHREAD_SET_NAME
+#define PTHREAD_SET_NAME(s)     do { } while (0)
+#endif
+    PTHREAD_SET_NAME("CPR Timertask");
+#endif
+
+    /*
+     * Increase the timer thread priority from default priority.
+     * This is required to make sure timers fire with reasonable precision.
+     *
+     * NOTE: always make sure the priority is higher than sip/gsm threads;
+     * otherwise, we must use mutex around the following while loop.
+     */
+    (void) cprAdjustRelativeThreadPriority(TIMER_THREAD_RELATIVE_PRIORITY);
+
+    /* get ready to listen for timer commands and service them */
+    if (start_timer_service_loop() == CPR_FAILURE) {
+        CPR_ERROR("%s: timer service loop failed\n", fname);
+    }
+
+    return NULL;
+}
+
+
+/**
+ * local_bind
+ * Function used to do a bind on the local socket.
+ *
+ * @param[in]  sock  - socket descriptor to bind
+ * @param[in]  name  - name to use for binding local socket
+ * @return 0 if success, -1 on error (errno will be set)
+ *
+ */
+static int local_bind (int sock, char *name)
+{
+    struct sockaddr_un addr;
+
+    /* construct the address structure */
+    addr.sun_family = AF_LOCAL;
+    sstrncpy(addr.sun_path, name, sizeof(addr.sun_path));
+    /* make sure file doesn't already exist */
+    unlink(addr.sun_path);
+
+    return bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+}
+
+/**
+ * select_sockets
+ *
+ * Set the socket descriptors to be used for reading.
+ * Only server side uses select.
+ *
+ * @return The server socket number
+ */
+static int select_sockets (void)
+{
+    FD_ZERO(&socks);
+
+    FD_SET(serv_sock, &socks);
+
+    return (serv_sock);
+}
+
+
+/**
+ * read_timer_cmd
+ * read message received on the IPC from the client
+ * the only messages are timer commands {add, remove}
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t read_timer_cmd ()
+{
+    static const char fname[] = "read_timer_cmd";
+    int  rcvlen;
+    timer_ipc_t tmr_cmd ={0};
+    cprRC_t ret = CPR_FAILURE;
+
+
+
+    rcvlen =recvfrom(serv_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
+                     NULL, NULL);
+
+    if (rcvlen > 0) {
+        //CPR_INFO("got message type=%d\n", tmr_cmd.msg_type);
+        switch(tmr_cmd.msg_type) {
+       case TMR_CMD_ADD:
+            //CPR_INFO("request to add timer ptr=%x duration=%d datptr=%x\n",
+            //       tmr_cmd.u.cmd.timer_ptr, tmr_cmd.u.cmd.duration, tmr_cmd.u.cmd.user_data_ptr);
+
+            ret = addTimer(tmr_cmd.u.cmd.timer_ptr,tmr_cmd.u.cmd.duration,
+                     (void *)tmr_cmd.u.cmd.user_data_ptr);
+
+            break;
+
+       case TMR_CMD_REMOVE:
+            //CPR_INFO("request to remove timer ptr=%x\n", tmr_cmd.u.cmd.timer_ptr);
+            ret = removeTimer(tmr_cmd.u.cmd.timer_ptr);
+            break;
+
+        default:
+            CPR_ERROR("%s:invalid ipc command = %d\n", tmr_cmd.msg_type);
+            ret = CPR_FAILURE;
+            break;
+        }
+    } else {
+        CPR_ERROR("%s:while reading serv_sock err =%s: Closing Socket..Timers not operational !!! \n",
+                  fname, strerror(errno));
+        (void) close(serv_sock);
+        serv_sock = INVALID_SOCKET;
+        ret = CPR_FAILURE;
+    }
+
+    /* send the result back */
+    send_api_result(ret, &tmr_client_addr, sizeof(tmr_client_addr));
+
+    return (ret);
+
+}
+
+/**
+ * send_api_result back to client via a socket sendto operation
+ * @param[in] retVal - value of result
+ * @param[in] addr   - address to send the result to
+ * @param[in] len    - length of addr
+ */
+void send_api_result(cprRC_t retVal, struct sockaddr_un *addr, socklen_t len)
+{
+    static const char fname[] = "send_api_result";
+    timer_ipc_t tmr_rsp = {0};
+
+    tmr_rsp.msg_type = TMR_RESULT;
+    tmr_rsp.u.result = retVal;
+    if (sendto(serv_sock, &tmr_rsp, sizeof(timer_ipc_t),0, (struct sockaddr *)addr, len) < 0) {
+        CPR_ERROR("%s: error in sending on serv_sock err=%s\n", fname, strerror(errno));
+    }
+}
+
+/**
+ * Start the timer service loop.
+ * Service loop waits for timer commands from timer clients processes them.
+ * Waiting is done by issuing a select call with a timeout. This is the main
+ * function that implements the timer functionality using the select call.
+ * timeout value = duration on the head of the timer list.
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t start_timer_service_loop (void)
+{
+    static const char fname[] = "start_timer_service_loop";
+    int lsock = -1;
+    struct timeval tv;
+    int ret;
+    boolean use_timeout;
+
+
+    /* initialize server and client addresses used for sending.*/
+    cpr_set_sockun_addr((cpr_sockaddr_un_t *) &tmr_serv_addr,   SERVER_PATH, getpid());
+    cpr_set_sockun_addr((cpr_sockaddr_un_t *) &tmr_client_addr, CLIENT_PATH, getpid());
+
+    /*
+     * init mutex and cond var.
+     * these are used for making API synchronous etc..
+     */
+    if (pthread_mutex_init(&api_mutex, NULL) != 0) {
+        CPR_ERROR("%s: failed to initialize api_mutex err=%s\n", fname,
+                  strerror(errno));
+        return CPR_FAILURE;
+    }
+
+
+    /* open a unix datagram socket for client library */
+    client_sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
+    if (client_sock == INVALID_SOCKET) {
+        CPR_ERROR("%s:could not create client socket error=%s\n", fname, strerror(errno));
+        return CPR_FAILURE;
+    }
+
+    /* bind service name to the socket */
+    if (local_bind(client_sock,tmr_client_addr.sun_path) < 0) {
+        CPR_ERROR("%s:could not bind local socket:error=%s\n", fname, strerror(errno));
+        (void) close(client_sock);
+        client_sock = INVALID_SOCKET;
+        return CPR_FAILURE;
+    }
+
+    /* open another unix datagram socket for timer service */
+    serv_sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
+    if (serv_sock == INVALID_SOCKET) {
+        CPR_ERROR("%s:could not create server socket error=%s\n", fname, strerror(errno));
+        serv_sock = INVALID_SOCKET;
+        close(client_sock);
+        client_sock = INVALID_SOCKET;
+        return CPR_FAILURE;
+    }
+
+    if (local_bind(serv_sock, tmr_serv_addr.sun_path) < 0) {
+        CPR_ERROR("%s:could not bind serv socket:error=%s\n", fname, strerror(errno));
+        (void) close(serv_sock);
+        (void) close(client_sock);
+        client_sock = serv_sock = INVALID_SOCKET;
+        return CPR_FAILURE;
+    }
+
+
+    while (1) {
+
+        lsock = select_sockets();
+
+       /* set the timer equal to duration on head */
+       if (timerListHead != NULL) {
+            tv.tv_sec = (timerListHead->duration)/1000;
+            tv.tv_usec = (timerListHead->duration%1000)*1000;
+            //CPR_INFO("%s:time duration on head =%d sec:%d usec (or %d msec)\n",
+            //       fname, tv.tv_sec, tv.tv_usec,
+            //       timerListHead->duration);
+            use_timeout = TRUE;
+       } else {
+            //CPR_INFO("%s:no timer in the list.. will block until there is one\n",
+            //       fname);
+            use_timeout = FALSE;
+       }
+
+        ret = select(lsock + 1, &socks, NULL, NULL, (use_timeout == TRUE) ? &tv:NULL);
+
+        if (ret == -1) {
+            CPR_ERROR("%s:error in select err=%s\n", fname,
+                      strerror(errno));
+            return(CPR_FAILURE);
+        } else if (ret == 0) {
+            /*
+             * this means the head timer has expired..there could be others
+             */
+            timerListHead->duration = 0;
+            process_expired_timers();
+        } else {
+
+            if (FD_ISSET(serv_sock, &socks)) {
+                //CPR_INFO("Got something on serv_sock..\n");
+                /* first reduce the duration of the head by current run time */
+                if (timerListHead != NULL) {
+                    //CPR_INFO("set head duration to =%d prev was= %d\n",
+                    //       tv.tv_sec * 1000 + (tv.tv_usec/1000),
+                    //       timerListHead->duration);
+                    /* set the head with the remaining duration(tv) as indicated by select */
+                    timerListHead->duration = tv.tv_sec * 1000 + (tv.tv_usec/1000);
+                }
+                /* read the ipc message to remove or add a timer */
+                (void) read_timer_cmd();
+            }
+       }
+    }
+
+}
+
+/**
+ *
+ * Process the timers expired. Generally this is called when head timer
+ * has expired.
+ * @note we need to process the list as there could be
+ * other timers too in the list which have expired.
+ *
+ */
+void process_expired_timers() {
+    static const char fname[] = "process_expired_timer";
+    cprCallBackTimerMsg_t *timerMsg;
+    void *syshdr;
+    boolean processingTimers;
+
+    /* nothing to do if no timers running */
+    if (timerListHead == NULL) {
+        return;
+    }
+
+    /* nothing to do if head has not expired */
+    if (timerListHead->duration > 0) {
+        return;
+    }
+
+
+    /* There are one or more expired timers on the list */
+    processingTimers = TRUE;
+    while (processingTimers) {
+        if (timerListHead != NULL) {
+            /*
+             * Send msg to queue to indicate this timer has expired
+             */
+            if (timerListHead->duration <= 0) {
+                timerMsg = (cprCallBackTimerMsg_t *)
+                    cpr_malloc(sizeof(cprCallBackTimerMsg_t));
+                if (timerMsg) {
+                    timerMsg->expiredTimerName =
+                        timerListHead->cprTimerPtr->name;
+                    //CPR_INFO("%s: timer %s expired..\n",fname,
+                    //       timerMsg->expiredTimerName);
+
+                    timerMsg->expiredTimerId =
+                        timerListHead->cprTimerPtr->applicationTimerId;
+                    timerMsg->usrData =
+                        timerListHead->cprTimerPtr->data;
+                    syshdr = cprGetSysHeader(timerMsg);
+                    if (syshdr) {
+                        fillInSysHeader(syshdr,
+                                        timerListHead->cprTimerPtr->applicationMsgId,
+                                        sizeof(cprCallBackTimerMsg_t), timerMsg);
+                        if (cprSendMessage(timerListHead->cprTimerPtr->callBackMsgQueue,
+                                           timerMsg, (void **) &syshdr) == CPR_FAILURE) {
+                            cprReleaseSysHeader(syshdr);
+                            cpr_free(timerMsg);
+                            CPR_ERROR("%s - Call to cprSendMessage failed\n", fname);
+                            CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
+                                      fname, timerListHead->cprTimerPtr->name);
+                        }
+                    } else {
+                        cpr_free(timerMsg);
+                        CPR_ERROR("%s - Call to cprGetSysHeader failed\n", fname);
+                        CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
+                                  fname, timerListHead->cprTimerPtr->name);
+                    }
+                } else {
+                    CPR_ERROR("%s - Call to cpr_malloc failed\n", fname);
+                    CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
+                              fname, timerListHead->cprTimerPtr->name);
+                }
+                (void) removeTimer(timerListHead->cprTimerPtr);
+            } else {
+                /* The rest of the timers on the list have not yet expired */
+                processingTimers = FALSE;
+            }
+        } else {
+            /* The timer list is now empty */
+            processingTimers = FALSE;
+        }
+    } /* still more to process */
+}
+
+/**
+  * @}
+  */
diff --git a/libs/sipcc/cpr/android/cpr_android_tst.c b/libs/sipcc/cpr/android/cpr_android_tst.c
new file mode 100644 (file)
index 0000000..5fb7787
--- /dev/null
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_linux_tst.h"
+#include "cpr_errno.h"
+#include <stdarg.h>
+#include "plat_api.h"
+
+void
+err_exit (void)
+{
+    *(volatile int *) 0xdeadbeef = 0x12345678;
+}
+
+
+/* main process entry function */
+int
+main (int argc, char **argv, char **env)
+{
+    int ret;
+    char *q;
+
+    buginf("CPR test...\n");
+    //err_exit();
+
+    cprTestCmd(argc, argv);
+
+    return 0;
+}
+
+
+long
+strlib_mem_used (void)
+{
+    return (0);
+}
+
+#define LOG_MAX 255
+
+int
+debugif_printf (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    int rc;
+    va_list ap;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+
+    if (rc <= 0) {
+        return rc;
+    }
+    printf("%s", fmt_buf);
+
+    return rc;
+}
+
diff --git a/libs/sipcc/cpr/android/cpr_android_tst.h b/libs/sipcc/cpr/android/cpr_android_tst.h
new file mode 100644 (file)
index 0000000..11b9e72
--- /dev/null
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_TST_H_
+#define _CPR_ANDROID_TST_H_
+
+#define APP_NAME "SIPCC-"
+#define DEB_F_PREFIX APP_NAME"%s: %s: "
+#define CPR "CPR"
+#define DEB_F_PREFIX_ARGS(msg_name, func_name) msg_name, func_name
+
+#endif
diff --git a/libs/sipcc/cpr/android/cpr_android_types.h b/libs/sipcc/cpr/android/cpr_android_types.h
new file mode 100644 (file)
index 0000000..699461c
--- /dev/null
@@ -0,0 +1,157 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ANDROID_TYPES_H_
+#define _CPR_ANDROID_TYPES_H_
+
+#include "sys/types.h"
+#include "stddef.h"
+#include "inttypes.h"
+
+/**
+ * @typedef boolean
+ *
+ * Define boolean as an unsigned byte
+ *
+ * @note There are differences within TNP header files
+ *    @li curses.h:   bool => char
+ *    @li types.h:    boolean_t => enum
+ *    @li dki_lock.h: bool_t => int
+ */
+typedef uint8_t boolean;
+
+/*
+ * Define size_t
+ *    defined in numerous header files
+ */
+/* DONE (sys/types.h => unsigned int) */
+
+/*
+ * Define ssize_t
+ */
+/* DONE (sys/types.h => int) */
+
+/*
+ * Define MIN/MAX
+ *    defined in param.h
+ *
+ * The GNU versions of the MAX and MIN macros do two things better than
+ * the old versions:
+ * 1. they are more optimal as they only evaluate a & b once by creating a
+ *    a variable of each type on the local stack.
+ * 2. they fix potential errors due to side-effects where a and b were
+ *    evaluated twice, i.e. MIN(i++,j++)
+ *
+ * @note b could be cast to a's type, to help with usage where the code
+ *       compares signed and unsigned types.
+ */
+#ifndef MIN
+#ifdef __GNUC__
+#define MIN(a,b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; })
+#else
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#endif
+
+#ifndef MAX
+#ifdef __GNUC__
+#define MAX(a,b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; })
+#else
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#endif
+
+
+/*
+ * Define NULL
+ *    defined in numerous header files
+ */
+/* DONE (stddef.h) */
+
+/**
+ * @def NUL
+ *
+ * Define NUL for string termination
+ */
+#ifndef NUL
+#define NUL '\0'
+#endif
+
+/**
+ * @def RESTRICT
+ *
+ * If suppoprting the ISO/IEC 9899:1999 standard,
+ * use the '__restrict' keyword
+ */
+#if defined(_POSIX_C_SOURCE) && defined(__GNUC__)
+#define RESTRICT __restrict
+#else
+#define RESTRICT
+#endif
+
+/**
+ * @def CONST
+ *
+ * Define CONST as @c const, if supported
+ */
+#define CONST const
+
+/**
+ * @def INLINE
+ *
+ * Define the appropriate setting for inlining functions
+ */
+#ifdef __STRICT_ANSI__
+#define INLINE
+#else
+#define INLINE __inline__
+#endif
+
+/**
+ * __BEGIN_DECLS and __END_DECLS
+ *
+ * Define macros for compilation by C++ compiler
+ */
+#ifndef __BEGIN_DECLS
+#ifdef __cplusplus
+#define __BEGIN_DECLS extern "C" {
+#else
+#define __BEGIN_DECLS
+#endif
+#endif
+
+#ifndef __END_DECLS
+#ifdef __cplusplus
+#define __END_DECLS   }
+#else
+#define __END_DECLS
+#endif
+#endif
+
+/**
+ * Define TRUE/FALSE
+ *     defined in several header files
+ */
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/*
+ * Define offsetof
+ */
+/* DONE (stddef.h) */
+
+/**
+ * @def FIELDOFFSET(struct name, field name)
+ *
+ * Macro to generate offset from a given field in a structure
+ */
+#define FIELDOFFSET(struct_name, field_name) (size_t)(&(((struct_name *)0)->field_name))
+
+
+#endif
diff --git a/libs/sipcc/cpr/common/cpr_string.c b/libs/sipcc/cpr/common/cpr_string.c
new file mode 100644 (file)
index 0000000..13276e9
--- /dev/null
@@ -0,0 +1,205 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "mozilla/Assertions.h"
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_strings.h"
+
+/**
+ * sstrncpy
+ *
+ * This is Cisco's *safe* version of strncpy.  The string will always
+ * be NUL terminated (which is not ANSI compliant).
+ *
+ * Parameters: s1  - first string
+ *             s2  - second string
+ *             max - maximum length in octets to concat.
+ *
+ * Return:     Pointer to the *end* of the string
+ *
+ * Remarks:    Modified to be explicitly safe for all inputs.
+ *             Also return the number of characters copied excluding the
+ *             NUL terminator vs. the original string s1.  This simplifies
+ *             code where sstrncat functions follow.
+ */
+unsigned long
+sstrncpy (char *dst, const char *src, unsigned long max)
+{
+    unsigned long cnt = 0;
+
+    if (dst == NULL) {
+        return 0;
+    }
+
+    if (src) {
+        while ((max-- > 1) && (*src)) {
+            *dst = *src;
+            dst++;
+            src++;
+            cnt++;
+        }
+    }
+
+#if defined(CPR_SSTRNCPY_PAD)
+    /*
+     * To be equivalent to the TI compiler version
+     * v2.01, SSTRNCPY_PAD needs to be defined
+     */
+    while (max-- > 1) {
+        *dst = '\0';
+        dst++;
+    }
+#endif
+    *dst = '\0';
+
+    return cnt;
+}
+
+/**
+ * sstrncat
+ *
+ * This is Cisco's *safe* version of strncat.  The string will always
+ * be NUL terminated (which is not ANSI compliant).
+ *
+ * Parameters: s1  - first string
+ *             s2  - second string
+ *             max - maximum length in octets to concatenate
+ *
+ * Return:     Pointer to the *end* of the string
+ *
+ * Remarks:    Modified to be explicitly safe for all inputs.
+ *             Also return the end vs. the beginning of the string s1
+ *             which is useful for multiple sstrncat calls.
+ */
+char *
+sstrncat (char *s1, const char *s2, unsigned long max)
+{
+    if (s1 == NULL)
+        return (char *) NULL;
+
+    while (*s1)
+        s1++;
+
+    if (s2) {
+        while ((max-- > 1) && (*s2)) {
+            *s1 = *s2;
+            s1++;
+            s2++;
+        }
+    }
+    *s1 = '\0';
+
+    return s1;
+}
+
+/*
+ * flex_string
+ */
+
+/*
+ * flex_string_init
+ *
+ * Not thread-safe
+ */
+void flex_string_init(flex_string *fs) {
+  fs->buffer_length = FLEX_STRING_CHUNK_SIZE;
+  fs->string_length = 0;
+  fs->buffer = cpr_malloc(fs->buffer_length);
+  fs->buffer[0] = '\0';
+}
+
+/*
+ * flex_string_free
+ *
+ * Not thread-safe
+ */
+void flex_string_free(flex_string *fs) {
+  fs->buffer_length = 0;
+  fs->string_length = 0;
+  cpr_free(fs->buffer);
+  fs->buffer = NULL;
+}
+
+/* For sanity check before alloc */
+#define FLEX_STRING_MAX_SIZE (10 * 1024 * 1024) /* 10MB */
+
+/*
+ * flex_string_check_alloc
+ *
+ * Allocate enough chunks to hold the new minimum size.
+ *
+ * Not thread-safe
+ */
+void flex_string_check_alloc(flex_string *fs, size_t new_min_length) {
+  if (new_min_length > fs->buffer_length) {
+    /* Oversize, allocate more */
+
+    /* Sanity check on allocation size */
+    if (new_min_length > FLEX_STRING_MAX_SIZE) {
+      MOZ_CRASH();
+    }
+
+    /* Alloc to nearest chunk */
+    fs->buffer_length = (((new_min_length - 1) / FLEX_STRING_CHUNK_SIZE) + 1) * FLEX_STRING_CHUNK_SIZE;
+
+    fs->buffer = cpr_realloc(fs->buffer, fs->buffer_length);
+  }
+}
+
+/*
+ * flex_string_append
+ *
+ * Not thread-safe
+ */
+void flex_string_append(flex_string *fs, const char *more) {
+  fs->string_length += strlen(more);
+
+  flex_string_check_alloc(fs, fs->string_length + 1);
+
+  sstrncat(fs->buffer, more, fs->buffer_length - strlen(fs->buffer));
+}
+
+/*
+ * flex_string_sprintf
+ *
+ * Not thread-safe
+ */
+void flex_string_sprintf(flex_string *fs, const char *format, ...) {
+  va_list ap;
+  int vsnprintf_result;
+
+  va_start(ap, format);
+  vsnprintf_result = vsnprintf(fs->buffer + fs->string_length, fs->buffer_length - fs->string_length, format, ap);
+  va_end(ap);
+
+  /* Special case just for Windows where vsnprintf is broken
+     and returns -1 if buffer too large unless you size it 0. */
+  if (vsnprintf_result < 0) {
+    va_start(ap, format);
+    vsnprintf_result = vsnprintf(NULL, 0, format, ap);
+    va_end(ap);
+  }
+
+  if (fs->string_length + vsnprintf_result >= fs->buffer_length) {
+    /* Buffer overflow, resize */
+    flex_string_check_alloc(fs, fs->string_length + vsnprintf_result + 1);
+
+    /* Try again with new buffer */
+    va_start(ap, format);
+    vsnprintf_result = vsnprintf(fs->buffer + fs->string_length, fs->buffer_length - fs->string_length, format, ap);
+    MOZ_ASSERT(vsnprintf_result > 0 && vsnprintf_result < (fs->buffer_length - fs->string_length));
+    va_end(ap);
+  }
+
+  if (vsnprintf_result > 0) {
+    fs->string_length += vsnprintf_result;
+  }
+}
+
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_align.h b/libs/sipcc/cpr/darwin/cpr_darwin_align.h
new file mode 100644 (file)
index 0000000..7c91ff3
--- /dev/null
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __CPR_DARWIN_ALIGN_H__
+#define __CPR_DARWIN_ALIGN_H__
+#include "cpr_types.h"
+
+/*
+ * Macros to determine how an address is aligned
+ */
+#define is_16bit_aligned(addr) (!((uint32_t)(addr) & 0x01))
+#define is_32bit_aligned(addr) (!((uint32_t)(addr) & 0x03))
+#define is_64bit_aligned(addr) (!((uint32_t)(addr) & 0x07))
+
+/*
+ * Macro to get a mask for a specified byte alignment
+ */
+#define ALIGN_MASK(alignment)  (~((alignment) - 1))
+
+/*
+ * Macro to set the minimum alignment
+ */
+#define ALIGN_MIN(align,align_min) (((align) > (align_min)) ? (align) : (align_min))
+
+/*
+ * Macro to round up or down "val" to be a multiple of "align", assuming
+ * "align" is a power of 2. if "align" is zero then no action will
+ * be performed
+ */
+#ifdef __typeof__
+#define ALIGN_UP(val,align)   ((align == 0) ? (val) : (__typeof__(val))(((uint32_t)(val)  +  ((align) - 1)) & ~((align) - 1)))
+#define ALIGN_DOWN(val,align) ((align == 0) ? (val) : (__typeof__(val))(((uint32_t)(val)) & ~((align) - 1)))
+#else
+#define ALIGN_UP(val,align)   ((align == 0) ? (val) : (uint32_t)(((uint32_t)(val)  +  ((align) - 1)) & ~((align) - 1)))
+#define ALIGN_DOWN(val,align) ((align == 0) ? (val) : (uint32_t)(((uint32_t)(val)) & ~((align) - 1)))
+#endif
+
+/**
+ * Macro to safely write 4 bytes based on memory alignment setting
+ */
+#ifndef CPR_MEMORY_LITTLE_ENDIAN
+#define WRITE_4BYTES_UNALIGNED(mem, value) \
+    { ((char *)mem)[0] = (uint8_t)(((value) & 0xff000000) >> 24); \
+      ((char *)mem)[1] = (uint8_t)(((value) & 0x00ff0000) >> 16); \
+      ((char *)mem)[2] = (uint8_t)(((value) & 0x0000ff00) >>  8); \
+      ((char *)mem)[3] = (uint8_t) ((value) & 0x000000ff);       \
+    }
+#else
+#define WRITE_4BYTES_UNALIGNED(mem, value) \
+    { ((char *)mem)[3] = (uint8_t)(((value) & 0xff000000) >> 24); \
+      ((char *)mem)[2] = (uint8_t)(((value) & 0x00ff0000) >> 16); \
+      ((char *)mem)[1] = (uint8_t)(((value) & 0x0000ff00) >>  8); \
+      ((char *)mem)[0] = (uint8_t) ((value) & 0x000000ff);       \
+    }
+#endif
+/**
+ * Macro to safely read 4 bytes based on memory alignment setting
+ */
+#ifndef CPR_MEMORY_LITTLE_ENDIAN
+#ifdef __typeof__
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    __typeof__(value)((((uint8_t *)mem)[0] << 24) | (((uint8_t *)mem)[1] << 16) | \
+                      (((uint8_t *)mem)[2] <<  8) |  ((uint8_t *)mem)[3])
+#else
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    (uint32_t)((((uint8_t *)mem)[0] << 24) | (((uint8_t *)mem)[1] << 16) | \
+               (((uint8_t *)mem)[2] <<  8) |  ((uint8_t *)mem)[3])
+#endif
+#else
+#ifdef __typeof__
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    __typeof__(value)((((uint8_t *)mem)[3] << 24) | (((uint8_t *)mem)[2] << 16) | \
+                      (((uint8_t *)mem)[1] <<  8) |  ((uint8_t *)mem)[0])
+#else
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    (uint32_t)((((uint8_t *)mem)[3] << 24) | (((uint8_t *)mem)[2] << 16) | \
+               (((uint8_t *)mem)[1] <<  8) |  ((uint8_t *)mem)[0])
+#endif
+#endif
+
+#endif /* __CPR_DARWIN_ALIGN_H__ */
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_assert.h b/libs/sipcc/cpr/darwin/cpr_darwin_assert.h
new file mode 100644 (file)
index 0000000..b180925
--- /dev/null
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_ASSERT_H_
+#define _CPR_DARWIN_ASSERT_H_
+
+#include "assert.h"
+
+/*--------------------------------------
+ *
+ * Macros
+ *
+ */
+
+/**
+ * CPR assert macro which calls cpr_assert_msg instead of abort
+ *
+ * The macro is dependent on the setting of FILE_ID which is used
+ * to override the __FILE__ setting.  For certain compilers, i.e.
+ * read 'Diab 4.4b', the __FILE__ is set to a Windows type path
+ * name which can contain backslashes that can cause odd output.
+ */
+#ifdef FILE_ID
+#define cpr_assert(expr) \
+    ((expr) ? (void)0 : cpr_assert_msg(FILE_ID, __LINE__, #expr))
+#else
+#define cpr_assert(expr) \
+    ((expr) ? (void)0 : cpr_assert_msg(__FILE__, __LINE__, #expr))
+#endif
+
+#define cpr_assert_debug(expr)
+
+/*
+ * A side note if somehow concerned about performance.
+ *
+ * This method will pre-render the string via the compiler,
+ * but will use more space due to larger strings.  Basically,
+ * good for speed and bad for memory.
+ *
+ * This is coded mostly as an example so if performance was an issue
+ * that the asserts could be low impact.
+ *
+ * #define cpr_assert_debug(expr) \
+ *   ((expr) ? (void)0 : cpr_assert_msg( \
+ *             __FILE__ ": line " __LINE__ ": assertion failed: " #expr))
+ *
+ * Note that this is not allowed when using __STRING_ANSI__
+ */
+
+#define cpr_assert_debug_rtn(expr)
+
+
+/*--------------------------------------
+ *
+ * Structures
+ *
+ */
+
+/**
+ * CPR assert modes of operation
+ */
+typedef enum {
+    CPR_ASSERT_MODE_NONE,            /**< Off, no message ouput          */
+    CPR_ASSERT_MODE_WARNING_LIMITED, /**< Warnings to syslog are limited */
+    CPR_ASSERT_MODE_WARNING_ALL,     /**< All warnings sent to syslog    */
+    CPR_ASSERT_MODE_ABORT            /**< Assert failure will call abort */
+} cpr_assert_mode_e;
+
+
+/*--------------------------------------
+ *
+ * Globals
+ *
+ */
+extern uint32_t cpr_assert_count;
+
+/*--------------------------------------
+ *
+ * Prototypes
+ *
+ */
+void
+cpr_assert_msg(const char *file, const int line, const char *expression);
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_errno.c b/libs/sipcc/cpr/darwin/cpr_darwin_errno.c
new file mode 100644 (file)
index 0000000..5241eae
--- /dev/null
@@ -0,0 +1,146 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_errno.h"
+#include <errno.h>
+
+/**
+ * @addtogroup OSAPIs The CPR OS Abstractions
+ * @brief Misc OS API Abstractions in CPR
+ *
+ * @{
+ */
+int8_t errno_table[] =
+{
+    CPR_EPERM,
+    CPR_ENOENT,
+    CPR_ESRCH,
+    CPR_EINTR,
+    CPR_EIO,
+    CPR_ENXIO,
+    CPR_E2BIG,
+    CPR_ENOEXEC,
+    CPR_EBADF,
+    CPR_ECHILD,
+    CPR_EDEADLK, /*10*/
+    CPR_ENOMEM,
+    CPR_EACCES,
+    CPR_EFAULT,
+    CPR_ENOTBLK,
+    CPR_EBUSY,
+    CPR_EEXIST,
+    CPR_EXDEV,
+    CPR_ENODEV,
+    CPR_ENOTDIR,
+    CPR_EISDIR,/*20*/
+    CPR_EINVAL,
+    CPR_ENFILE,
+    CPR_EMFILE,
+    CPR_ENOTTY,
+    CPR_ETXTBSY,
+    CPR_EFBIG,
+    CPR_ENOSPC,
+    CPR_ESPIPE,
+    CPR_EROFS,
+    CPR_EMLINK,/*30*/
+    CPR_EPIPE,
+    CPR_EDOM,
+    CPR_ERANGE,
+    CPR_EAGAIN,
+    CPR_EINPROGRESS,
+    CPR_EALREADY,
+    CPR_ENOTSOCK,
+    CPR_EDESTADDRREQ,
+    CPR_EMSGSIZE,
+    CPR_EPROTOTYPE,/*40*/
+    CPR_ENOPROTOOPT,
+    CPR_EPROTONOSUPPORT,
+    CPR_ESOCKTNOSUPPORT,
+    CPR_ENOTSUP,
+    CPR_EPFNOSUPPORT,
+    CPR_EAFNOSUPPORT,
+    CPR_EADDRINUSE,
+    CPR_EADDRNOTAVAIL,
+    CPR_ENETDOWN,
+    CPR_ENETUNREACH,/*50*/
+    CPR_ENETRESET,
+    CPR_ECONNABORTED,
+    CPR_ECONNRESET,
+    CPR_ENOBUFS,
+    CPR_EISCONN,
+    CPR_ENOTCONN,
+    CPR_ESHUTDOWN,
+    CPR_ETOOMANYREFS,
+    CPR_ETIMEDOUT,
+    CPR_ECONNREFUSED,/*60*/
+    CPR_ELOOP,
+    CPR_ENAMETOOLONG,
+    CPR_EHOSTDOWN,
+    CPR_EHOSTUNREACH,
+    CPR_ENOTEMPTY,
+    CPR_UNKNOWN_ERR,            /* empty 66 */
+    CPR_EUSERS,
+    CPR_EDQUOT,
+    CPR_ESTALE,
+    CPR_EREMOTE,/*70*/
+    CPR_UNKNOWN_ERR,            /* empty 71 */
+    CPR_UNKNOWN_ERR,            /* empty 72 */
+    CPR_UNKNOWN_ERR,            /* empty 73 */
+    CPR_UNKNOWN_ERR,            /* empty 74 */
+    CPR_UNKNOWN_ERR,            /* empty 75 */
+    CPR_ENOLCK,
+    CPR_ENOSYS,
+    CPR_UNKNOWN_ERR,            /* empty 78 */
+    CPR_UNKNOWN_ERR,            /* empty 79 */
+    CPR_UNKNOWN_ERR,/*80*/
+    CPR_UNKNOWN_ERR,            /* empty 81 */
+    CPR_UNKNOWN_ERR,            /* empty 82 */
+    CPR_EOVERFLOW,
+    CPR_UNKNOWN_ERR,            /* empty 84 */
+    CPR_UNKNOWN_ERR,            /* empty 85 */
+    CPR_UNKNOWN_ERR,            /* empty 86 */
+    CPR_UNKNOWN_ERR,            /* empty 87 */
+    CPR_ECANCELED,
+    CPR_EIDRM,
+    CPR_ENOMSG,/*90*/
+    CPR_EILSEQ,
+    CPR_UNKNOWN_ERR,            /* empty 92 */
+    CPR_EBADMSG,
+    CPR_EMULTIHOP,
+    CPR_ENODATA,
+    CPR_ENOLINK,
+    CPR_ENOSR,
+    CPR_ENOSTR,
+    CPR_EPROTO,
+    CPR_ETIME,/*100*/
+    CPR_EOPNOTSUPP,
+    CPR_UNKNOWN_ERR
+}; // Soya: the errno_table should be consistent with <errno.h>
+
+/**
+ *
+ * @brief Translates to "cpr_errno" Macro
+ *
+ * pSIPCC uses the cpr_errno macro to print the errno
+ * for error conditions. This function is used to map the standard
+ * errno to standard CPR errors
+ *
+ * @return The CPR error number
+ *
+ */
+int16_t
+cprTranslateErrno (void)
+{
+    int16_t e = (int16_t) errno;
+
+    /*
+     * Verify against MIN and MAX errno numbers
+     */
+    if ( e < 1 || e > (int16_t) (sizeof(errno_table)/sizeof(errno_table[0])) )
+       {
+        return CPR_UNKNOWN_ERR;
+    }
+    return (int16_t) errno_table[e - 1];
+}
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_errno.h b/libs/sipcc/cpr/darwin/cpr_darwin_errno.h
new file mode 100644 (file)
index 0000000..7e7dad6
--- /dev/null
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_ERRNO_H_
+#define _CPR_DARWIN_ERRNO_H_
+
+#include <errno.h>
+
+/*
+ * Maintain re-entrant nature by wrapping 'errno'
+ */
+/** @def cpr_errno is used by pSIPCC. MUST be defined by the CPR layer.
+  */
+#define cpr_errno cprTranslateErrno()
+
+int16_t cprTranslateErrno(void);
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_in.h b/libs/sipcc/cpr/darwin/cpr_darwin_in.h
new file mode 100644 (file)
index 0000000..f057e08
--- /dev/null
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_IN_H_
+#define _CPR_DARWIN_IN_H_
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_init.c b/libs/sipcc/cpr/darwin/cpr_darwin_init.c
new file mode 100644 (file)
index 0000000..6985b0e
--- /dev/null
@@ -0,0 +1,214 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *
+ *  @mainpage Application Programming Using the Cisco Portable Runtime (CPR) OS Abstraction Layer
+ *
+ *  @section intro_sec Introduction
+ *  CPR is an OS-abstraction layer which provides the functionality set of an OS
+ *  while hiding all the operational details from the applications. CPR also
+ *  provides additional functionality when certain operating-systems prove
+ *  deficient providing a consistent set of functionality for the applications
+ *  to use. The pSipCC library uses the CPR routines to achieve portablity. It
+ *  is the responsibility of any vendor requiring to port pSipCC into their
+ *  platforms to have the correct implementation of the CPR layer.
+ *
+ *  The CPR delivery includes two "archives/binary" for memory and string
+ *  related operations. These binaries contain functions that the pSIPCC uses
+ *  for it's functionality. The header files that contain the external functions
+ *  (APIs) from these binaries are also distributed.
+ *
+ *  @section sub_sec Functionality
+ *  CPR consists of a number of functional subsystems to achieve OS abstraction. The main
+ *  functionality provided by CPR to the pSIPCC library are
+ *  @li Memory Management - Provided as a binary that needs to be linked into CPR
+ *  @li Timers
+ *  @li Inter Process Communication - Sockets, Message Queues, Mutex, Semaphores
+ *  @li String Handling - Provided as a binary that needs to be linked into CPR
+ *  @li Thread Abstraction
+ *  @li Other Platform/OS Abstractions
+ *  @li Debug/Logging Abstraction
+ *
+ *  @section file_list EXTERNAL APIS
+ *   The External APIs that need to be exposed by CPR to the pSIPCC application are
+ *   defined in the following header files. The documentation within
+ *   each header file lists the functions/macros that @b NEED to be defined for
+ *   pSIPCC to work correctly. Example functions (and an implementation for
+ *   Linux) is available for most functions defined in these headers. Look under
+ *   the "Modules" tab to find the various subsystems and associated APIs and
+ *   helper functions.
+ *   @li cpr_debug.h
+ *   @li cpr_errno.h
+ *   @li cpr_in.h
+ *   @li cpr_locks.h
+ *   @li cpr_rand.h
+ *   @li cpr_socket.h
+ *   @li cpr_stdio.h
+ *   @li cpr_threads.h
+ *   @li cpr_timers.h
+ *   @li cpr.h
+ *   @li cpr_ipc.h
+ *   @li cpr_stddef.h
+ *   @li cpr_time.h
+ *   @li cpr_types.h
+ *
+ *   The function prototypes in these header files are implemented in the
+ *   binaries related to memory and string functionality. The prototypes are
+ *   given so that vendors can use these functions for implementation of other
+ *   CPR parts.
+ *   @li cpr_memory.h
+ *   @li cpr_stdlib.h
+ *   @li cpr_string.h
+ *   @li cpr_strings.h
+ *
+ *  @section standard_headers Standard Header Files
+ *  The pSIPCC component expects some standard header files and associated
+ *  functionality to be present in the native operating system of the vendors
+ *  porting it. These header files contain some basic functionality that pSIPCC
+ *  uses for proper operation. A list of the standard headers (from a standard
+ *  Linux distribution) are -
+ *   @li <assert.h>
+ *   @li <errno.h>
+ *   @li <arpa/inet.h>
+ *   @li <netinet/in.h>
+ *   @li <netinet/tcp.h>
+ *   @li <pthread.h>
+ *   @li <sys/socket.h>
+ *   @li <sys/select.h>
+ *   @li <sys/un.h>
+ *   @li <unistd.h>
+ *   @li <stdarg.h>
+ *   @li <stdio.h>
+ *   @li <stdlib.h>
+ *   @li <string.h>
+ *   @li <ctype.h>
+ *   @li <strings.h>
+ *   @li <time.h>
+ *
+ *
+ *  @file cpr_darwin_init.c
+ *  @brief This file contains CPR initialization routines
+ *
+ *  DESCRIPTION
+ *     Initialization routine for the Cisco Portable Runtime layer
+ *     running in the Linux operating System.
+ */
+
+/**
+  * @addtogroup Initialization The initialization module
+  * @ingroup CPR
+  * @brief The intialization module consists of APIs used to initialize or destroy the CPR layer by pSipCC
+  *
+  * @{
+  */
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_timers.h"
+#include "cpr_darwin_locks.h"
+#include "cpr_darwin_timers.h"
+#include "plat_api.h"
+#include "plat_debug.h"
+
+#include <errno.h>
+#include <sys/msg.h>
+#include <sys/ipc.h>
+
+/**
+  * Mutex to manage message queue list.
+  */
+extern pthread_mutex_t msgQueueListMutex;
+
+/**
+  * Boolean to check that cprPreInit been called
+  */
+static boolean pre_init_called = FALSE;
+
+/**
+ * cprInfo prints out informational messages that are
+ * not necessarily errors, but maybe of interest to
+ * someone debugging a problem. Examples of this are
+ * requesting a msg from a msg queue that is empty,
+ * stopping a timer that is not running, etc...
+ */
+int32_t cprInfo = TRUE;
+
+
+/**
+ * cprPreInit
+ *
+ * @brief The cprPreInit function IS called from pSIPCC @b before any components are initialized.
+ *
+ * This function @b SHOULD initialize those portions of the CPR that
+ * are needed before applications can start using it. The memory subsystem
+ * (sandbox) is initialized from this routine.
+ *
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ * @note pSIPCC will NOT continue and stop initialization if the return value is CPR_FAILURE.
+ */
+cprRC_t
+cprPreInit (void)
+{
+    static const char fname[] = "cprPreInit";
+    int32_t returnCode;
+
+    /*
+     * Make function reentreant
+     */
+    if (pre_init_called == TRUE) {
+        return CPR_SUCCESS;
+    }
+    pre_init_called = TRUE;
+
+    /*
+     * Create message queue list mutex
+     */
+    returnCode = pthread_mutex_init(&msgQueueListMutex, NULL);
+    if (returnCode != 0) {
+        CPR_ERROR("%s: MsgQueue Mutex init failure %d\n", fname, returnCode);
+        return CPR_FAILURE;
+    }
+
+    returnCode = cpr_timer_pre_init();
+    if (returnCode != 0) {
+        CPR_ERROR("%s: timer pre init failed %d\n", fname, returnCode);
+        return CPR_FAILURE;
+    }
+
+
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * cprPostInit
+ *
+ * @brief The cprPostInit function IS called from pSIPCC @b after all the components are initialized.
+ *
+ * This function @b SHOULD complete any CPR activities before the phone is
+ * operational. In other words a call to this function will be the last line of
+ * the phone initializtion routine from pSIPCC. This function implementation
+ * ties in a couple of debug commands to get more information on the CPR from
+ * the shell.
+ *
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ * @note pSIPCC will NOT continue and stop initialization if the return value is CPR_FAILURE.
+ */
+cprRC_t
+cprPostInit (void)
+{
+    /*
+     * Bind in debug commands to toggle CPR debug printfs
+     * from the phone cli.
+     */
+
+    debug_bind_keyword("cpr-info", &cprInfo);
+    //bind_show_keyword("cpr-msgq", cprShowMessageQueueStats);
+
+    return CPR_SUCCESS;
+}
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_ipc.c b/libs/sipcc/cpr/darwin/cpr_darwin_ipc.c
new file mode 100644 (file)
index 0000000..a7c47e1
--- /dev/null
@@ -0,0 +1,685 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include <cpr_stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include <plat_api.h>
+#include "cpr_string.h"
+
+/*
+ * If building with debug test interface,
+ * allow access to internal CPR functions
+ */
+#define STATIC static
+
+#define OS_MSGTQL 31 /* need to check number for MV linux and put here */
+
+/*
+ * Internal CPR API
+ */
+extern pthread_t cprGetThreadId(cprThread_t thread);
+
+/*
+ * Extended internal message queue node
+ *
+ * A double-linked list holding the nessasary message information
+ */
+typedef struct cpr_msgq_node_s
+{
+    struct cpr_msgq_node_s *next;
+    struct cpr_msgq_node_s *prev;
+    void *msg;
+    void *pUserData;
+} cpr_msgq_node_t;
+
+/*
+ * Msg queue information needed to hide OS differences in implementation.
+ * To use msg queues, the application code may pass in a name to the
+ * create function for msg queues. CPR does not use this field, it is
+ * solely for the convenience of the application and to aid in debugging.
+ *
+ * Note: Statistics are not protected by a mutex; therefore, there exists
+ * the possibility that the results may not be accurate.
+ *
+ * Note:if the depth supplied by OS is insufficient,a message queue owner may
+ * increase the message queue depth via cprCreateMessageQueue's depth
+ * parameter where the value can range from MSGTQL to CPR_MAX_MSG_Q_DEPTH.
+ */
+typedef struct cpr_msg_queue_s
+{
+    struct cpr_msg_queue_s *next;
+    const char *name;
+    pthread_t thread;
+    int32_t queueId;
+    uint16_t maxCount;
+    uint16_t currentCount;
+    uint32_t totalCount;
+    uint32_t sendErrors;
+    uint32_t reTries;
+    uint32_t highAttempts;
+    uint32_t selfQErrors;
+    uint16_t extendedQDepth;
+    uint16_t maxExtendedQDepth;
+    pthread_mutex_t mutex;       /* lock for managing extended queue     */
+       pthread_cond_t cond;             /* signal for queue/dequeue */
+    cpr_msgq_node_t *head;       /* extended queue head (newest element) */
+    cpr_msgq_node_t *tail;       /* extended queue tail (oldest element) */
+} cpr_msg_queue_t;
+
+/*
+ * A enumeration used to report the result of posting a message to
+ * a message queue
+ */
+typedef enum
+{
+    CPR_MSGQ_POST_SUCCESS,
+    CPR_MSGQ_POST_FAILED,
+    CPR_MSGQ_POST_PENDING
+} cpr_msgq_post_result_e;
+
+
+/*
+ * Head of list of message queues
+ */
+static cpr_msg_queue_t *msgQueueList = NULL;
+
+/*
+ * Mutex to manage message queue list
+ */
+pthread_mutex_t msgQueueListMutex;
+
+/*
+ * String to represent message queue name when it is not provided
+ */
+static const char unnamed_string[] = "unnamed";
+
+
+/*
+ * CPR_MAX_MSG_Q_DEPTH
+ *
+ * The maximum queue depth supported by the CPR layer.  This value
+ * is arbitrary though the purpose is to limit the memory usage
+ * by CPR and avoid (nearly) unbounded situations.
+ *
+ * Note: This value should be greater than MSGTQL which is currently
+ *       defined as 31
+ */
+#define CPR_MAX_MSG_Q_DEPTH 256
+
+/*
+ * CPR_SND_TIMEOUT_WAIT_INTERVAL
+ *
+ * The interval of time to wait in milliseconds between attempts to
+ * send a message to the message queue
+ *
+ * Note: 20 ms. to avoid less than a tick wake up since on most
+ *       OSes 10ms is one 1 tick
+ *       this should really be OS_TICK_MS * 2 or OS_TICK_MS + X
+ */
+#define CPR_SND_TIMEOUT_WAIT_INTERVAL 20
+
+/*
+ * CPR_ATTEMPTS_TO_SEND
+ *
+ * The number of attempts made to send a message when the message
+ * would otherwise be blocked.  Note in this condition the thread
+ * will sleep the timeout interval to allow the msg queue to be
+ * drained.
+ *
+ * Note: 25 attempts for upto .5 seconds at the interval of
+ *       CPR_SND_TIMEOUT_WAIT_INTERVAL worst case.
+ */
+#define CPR_ATTEMPTS_TO_SEND 25
+
+/*
+ * Also, important to note that the total timeout interval must be
+ * greater than the SIP's select call timeout value which is 25msec.
+ * This is necessary to cover the case where the SIP message queue
+ * is full and the select timeout occurs.
+ *
+ * Total timeout interval = CPR_SND_TIMEOUT_WAIT_INTERVAL *
+ *                          CPR_ATTEMPTS_TO_SEND;
+ */
+
+
+/*
+ * Prototype declarations
+ */
+static cpr_msgq_post_result_e
+cprPostMessage(cpr_msg_queue_t *msgq, void *msg, void **ppUserData);
+static void
+cprPegSendMessageStats(cpr_msg_queue_t *msgq, uint16_t numAttempts);
+
+
+/*
+ * Functions
+ */
+
+/**
+ * Creates a message queue
+ *
+ * @param name  - name of the message queue
+ * @param depth - the message queue depth, optional field which will
+ *                default if set to zero(0)
+ *
+ * @return Msg queue handle or NULL if init failed, errno provided
+ *
+ * @note the actual message queue depth will be bounded by the
+ *       standard system message queue depth and CPR_MAX_MSG_Q_DEPTH.
+ *       If 'depth' is outside of the bounds, the value will be
+ *       reset automatically.
+ */
+cprMsgQueue_t
+cprCreateMessageQueue (const char *name, uint16_t depth)
+{
+    static const char fname[] = "cprCreateMessageQueue";
+    cpr_msg_queue_t *msgq;
+    static int key_id = 100; /* arbitrary starting number */
+
+    msgq = cpr_calloc(1, sizeof(cpr_msg_queue_t));
+    if (msgq == NULL) {
+        printf("%s: Malloc failed: %s\n", fname,
+                  name ? name : unnamed_string);
+        errno = ENOMEM;
+        return NULL;
+    }
+
+    msgq->name = name ? name : unnamed_string;
+       msgq->queueId = key_id++;
+
+       pthread_cond_t _cond = PTHREAD_COND_INITIALIZER;
+       msgq->cond = _cond;
+       pthread_mutex_t _lock = PTHREAD_MUTEX_INITIALIZER;
+       msgq->mutex = _lock;
+
+    /*
+     * Add message queue to list for statistics reporting
+     */
+    pthread_mutex_lock(&msgQueueListMutex);
+    msgq->next = msgQueueList;
+    msgQueueList = msgq;
+    pthread_mutex_unlock(&msgQueueListMutex);
+
+    return msgq;
+}
+
+
+/**
+ * Removes all messages from the queue and then destroy the message queue
+ *
+ * @param msgQueue - message queue to destroy
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE, errno provided
+ */
+cprRC_t
+cprDestroyMessageQueue (cprMsgQueue_t msgQueue)
+{
+    static const char fname[] = "cprDestroyMessageQueue";
+    cpr_msg_queue_t *msgq;
+    void *msg;
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+    if (msgq == NULL) {
+        /* Bad application! */
+        CPR_ERROR("%s: Invalid input\n", fname);
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    /* Drain message queue */
+    msg = cprGetMessage(msgQueue, FALSE, NULL);
+    while (msg != NULL) {
+        cpr_free(msg);
+        msg = cprGetMessage(msgQueue, FALSE, NULL);
+    }
+
+    /* Remove message queue from list */
+    pthread_mutex_lock(&msgQueueListMutex);
+    if (msgq == msgQueueList) {
+        msgQueueList = msgq->next;
+    } else {
+        cpr_msg_queue_t *msgql = msgQueueList;
+
+        while ((msgql->next != NULL) && (msgql->next != msgq)) {
+            msgql = msgql->next;
+        }
+        if (msgql->next == msgq) {
+            msgql->next = msgq->next;
+        }
+    }
+    pthread_mutex_unlock(&msgQueueListMutex);
+
+    /* Remove message queue mutex */
+    if (pthread_mutex_destroy(&msgq->mutex) != 0) {
+        CPR_ERROR("%s: Failed to destroy msg queue (%s) mutex: %d\n",
+                  fname, msgq->name, errno);
+    }
+
+    cpr_free(msgq);
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * Associate a thread with the message queue
+ *
+ * @param msgQueue  - msg queue to set
+ * @param thread    - CPR thread to associate with queue
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ *
+ * @note Nothing is done to prevent overwriting the thread ID
+ *       when the value has already been set.
+ */
+cprRC_t
+cprSetMessageQueueThread (cprMsgQueue_t msgQueue, cprThread_t thread)
+{
+    static const char fname[] = "cprSetMessageQueueThread";
+    cpr_msg_queue_t *msgq;
+
+    if ((!msgQueue) || (!thread)) {
+        CPR_ERROR("%s: Invalid input\n", fname);
+        return CPR_FAILURE;
+    }
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+    if (msgq->thread != 0) {
+        CPR_ERROR("%s: over-writing previously msgq thread name for %s",
+                  fname, msgq->name);
+    }
+
+    msgq->thread = cprGetThreadId(thread);
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * Retrieve a message from a particular message queue
+ *
+ * @param[in]  msgQueue    - msg queue from which to retrieve the message
+ * @param[in]  waitForever - boolean to either wait forever (TRUE) or not
+ *                           wait at all (FALSE) if the msg queue is empty.
+ * @param[out] ppUserData  - pointer to a pointer to user defined data
+ *
+ * @return Retrieved message buffer or NULL if failure occurred or
+ *         the waitForever flag was set to false and no messages were
+ *         on the queue.
+ *
+ * @note   If ppUserData is defined, the value will be initialized to NULL
+ */
+void *
+cprGetMessage (cprMsgQueue_t msgQueue, boolean waitForever, void **ppUserData)
+{
+    static const char fname[] = "cprGetMessage";
+
+    void *buffer = 0;
+    cpr_msg_queue_t *msgq;
+    cpr_msgq_node_t *node;
+       struct timespec timeout;
+       struct timeval tv;
+       // On the iPhone, there is a DarwinAlias problem with "timezone"
+       struct _timezone {
+               int     tz_minuteswest; /* of Greenwich */
+               int     tz_dsttime;     /* type of dst correction to apply */
+       } tz;
+
+    /* Initialize ppUserData */
+    if (ppUserData) {
+        *ppUserData = NULL;
+    }
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+    if (msgq == NULL) {
+        /* Bad application! */
+        CPR_ERROR("%s: Invalid input\n", fname);
+        errno = EINVAL;
+        return NULL;
+    }
+
+    /*
+     * If waitForever is set, block on the message queue
+     * until a message is received, else return after
+        * 25msec of waiting
+     */
+       pthread_mutex_lock(&msgq->mutex);
+
+       if (!waitForever)
+       {
+               // We'll wait till 25uSec from now
+               gettimeofday(&tv, &tz);
+               timeout.tv_nsec = (tv.tv_usec * 1000) + 25000;
+               timeout.tv_sec = tv.tv_sec;
+
+               pthread_cond_timedwait(&msgq->cond, &msgq->mutex, &timeout);
+
+       }
+       else
+       {
+               while(msgq->tail==NULL)
+               {
+                       pthread_cond_wait(&msgq->cond, &msgq->mutex);
+               }
+       }
+
+       // If there is a message on the queue, de-queue it
+       if (msgq->tail)
+       {
+               node = msgq->tail;
+               msgq->tail = node->prev;
+               if (msgq->tail) {
+                       msgq->tail->next = NULL;
+               }
+               if (msgq->head == node) {
+                       msgq->head = NULL;
+               }
+               msgq->currentCount--;
+               /*
+                * Pull out the data
+                */
+               if (ppUserData) {
+                       *ppUserData = node->pUserData;
+               }
+               buffer = node->msg;
+
+       }
+
+       pthread_mutex_unlock(&msgq->mutex);
+
+    return buffer;
+}
+
+
+/**
+ * Place a message on a particular queue.  Note that caller may
+ * block (see comments below)
+ *
+ * @param msgQueue   - msg queue on which to place the message
+ * @param msg        - pointer to the msg to place on the queue
+ * @param ppUserData - pointer to a pointer to user defined data
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE, errno provided
+ *
+ * @note 1. Messages queues are set to be non-blocking, those cases
+ *       where the system call fails with a would-block error code
+ *       (EAGAIN) the function will attempt other mechanisms described
+ *       below.
+ * @note 2. If enabled with an extended message queue, either via a
+ *       call to cprCreateMessageQueue with depth value or a call to
+ *       cprSetExtendMessageQueueDepth() (when unit testing), the message
+ *       will be added to the extended message queue and the call will
+ *       return successfully.  When room becomes available on the
+ *       system's message queue, those messages will be added.
+ * @note 3. If the message queue becomes full and no space is availabe
+ *       on the extended message queue, then the function will attempt
+ *       to resend the message up to CPR_ATTEMPTS_TO_SEND and the
+ *       calling thread will *BLOCK* CPR_SND_TIMEOUT_WAIT_INTERVAL
+ *       milliseconds after each failed attempt.  If unsuccessful
+ *       after all attempts then EGAIN error code is returned.
+ * @note 4. This applies to all CPR threads, including the timer thread.
+ *       So it is possible that the timer thread would be forced to
+ *       sleep which would have the effect of delaying all active
+ *       timers.  The work to fix this rare situation is not considered
+ *       worth the effort to fix....so just leaving as is.
+ */
+cprRC_t
+cprSendMessage (cprMsgQueue_t msgQueue, void *msg, void **ppUserData)
+{
+    static const char fname[] = "cprSendMessage";
+    static const char error_str[] = "%s: Msg not sent to %s queue: %s\n";
+    cpr_msgq_post_result_e rc;
+    cpr_msg_queue_t *msgq;
+    int16_t attemptsToSend = CPR_ATTEMPTS_TO_SEND;
+    uint16_t numAttempts   = 0;
+
+    /* Bad application? */
+    if (msgQueue == NULL) {
+        CPR_ERROR(error_str, fname, "undefined", "invalid input");
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+
+    /*
+     * Attempt to send message
+     */
+    do {
+
+               /*
+                * Post the message to the Queue
+                */
+               rc = cprPostMessage(msgq, msg, ppUserData);
+
+               if (rc == CPR_MSGQ_POST_SUCCESS) {
+                       cprPegSendMessageStats(msgq, numAttempts);
+                       return CPR_SUCCESS;
+               } else if (rc == CPR_MSGQ_POST_FAILED) {
+                       CPR_ERROR("%s: Msg not sent to %s queue: %d\n",
+                                         fname, msgq->name, errno);
+                       msgq->sendErrors++;
+                       /*
+                        * If posting to calling thread's own queue,
+                        * then peg the self queue error.
+                        */
+                       if (pthread_self() == msgq->thread) {
+                               msgq->selfQErrors++;
+                       }
+
+                       return CPR_FAILURE;
+               }
+
+
+        /*
+         * Did not succeed in sending the message, so continue
+         * to attempt up to the CPR_ATTEMPTS_TO_SEND.
+         */
+        attemptsToSend--;
+        if (attemptsToSend > 0) {
+            /*
+             * Force a context-switch of the thread attempting to
+             * send the message, in order to help the case where
+             * the msg queue is full and the owning thread may get
+             * a a chance be scheduled so it can drain it (Note:
+             * no guarantees, more of a "last-ditch effort" to
+             * recover...especially when temporarily over-whelmed).
+             */
+            cprSleep(CPR_SND_TIMEOUT_WAIT_INTERVAL);
+            msgq->reTries++;
+            numAttempts++;
+        }
+    } while (attemptsToSend > 0);
+
+    CPR_ERROR(error_str, fname, msgq->name, "FULL");
+    msgq->sendErrors++;
+    return CPR_FAILURE;
+}
+
+/**
+ * Peg the statistics for successfully posting a message
+ *
+ * @param msgq        - message queue
+ * @param numAttempts - number of attempts to post message to message queue
+ *
+ * @return none
+ *
+ * @pre (msgq not_eq NULL)
+ */
+static void
+cprPegSendMessageStats (cpr_msg_queue_t *msgq, uint16_t numAttempts)
+{
+    /*
+     * Collect statistics
+     */
+    msgq->totalCount++;
+    if (msgq->currentCount > msgq->maxCount) {
+        msgq->maxCount = msgq->currentCount;
+    }
+
+    if (numAttempts > msgq->highAttempts) {
+        msgq->highAttempts = numAttempts;
+    }
+}
+
+/**
+ * Post message to system message queue
+ *
+ * @param msgq       - message queue
+ * @param msg        - message to post
+ * @param ppUserData - ptr to ptr to option user data
+ *
+ * @return the post result which is CPR_MSGQ_POST_SUCCESS,
+ *         CPR_MSGQ_POST_FAILURE or CPR_MSGQ_POST_PENDING
+ *
+ * @pre (msgq not_eq NULL)
+ * @pre (msg not_eq NULL)
+ */
+static cpr_msgq_post_result_e
+cprPostMessage (cpr_msg_queue_t *msgq, void *msg, void **ppUserData)
+{
+       cpr_msgq_node_t *node;
+
+       /*
+        * Allocate new message queue node
+        */
+       node = cpr_malloc(sizeof(*node));
+       if (!node) {
+               errno = ENOMEM;
+               return CPR_MSGQ_POST_FAILED;
+       }
+
+       pthread_mutex_lock(&msgq->mutex);
+
+       /*
+        * Fill in data
+        */
+       node->msg = msg;
+       if (ppUserData != NULL) {
+               node->pUserData = *ppUserData;
+       } else {
+               node->pUserData = NULL;
+       }
+
+       /*
+        * Push onto list
+        */
+       node->prev = NULL;
+       node->next = msgq->head;
+       msgq->head = node;
+
+       if (node->next) {
+               node->next->prev = node;
+       }
+
+       if (msgq->tail == NULL) {
+               msgq->tail = node;
+       }
+       msgq->currentCount++;
+
+       pthread_cond_signal(&msgq->cond);
+       pthread_mutex_unlock(&msgq->mutex);
+
+       return CPR_MSGQ_POST_SUCCESS;
+
+}
+
+
+/**
+ * cprGetMessageQueueStats
+ *
+ * Get statistics for a given message queue
+ *
+ * @param msgQueue - message queue on which to gather stats
+ * @param stats    - pointer to struct to place statistics
+ *
+ * @return none
+ */
+STATIC void
+cprGetMessageQueueStats (cprMsgQueue_t msgQueue, cprMsgQueueStats_t *stats)
+{
+    cpr_msg_queue_t *msgq;
+
+    if (msgQueue && stats) {
+        msgq = (cpr_msg_queue_t *) msgQueue;
+
+        sstrncpy(stats->name, msgq->name ? msgq->name : "undefined",
+                sizeof(stats->name));
+
+        stats->extendedDepth = msgq->maxExtendedQDepth;
+        stats->maxCount = msgq->maxCount;
+        stats->currentCount = msgq->currentCount;
+        stats->totalCount = msgq->totalCount;
+        stats->reTries = msgq->reTries;
+        stats->sendErrors = msgq->sendErrors;
+        stats->highAttempts = msgq->highAttempts;
+        stats->selfQErrors = msgq->selfQErrors;
+    }
+}
+
+
+/**
+ * Report statistics for all message queues
+ *
+ * @param argc - not used
+ * @param argv - not used
+ *
+ * @return zero(0)
+ *
+ * @note Prototype is 'canned' so return of zero is necessary
+ */
+int32_t
+cprShowMessageQueueStats (int32_t argc, const char *argv[])
+{
+    cpr_msg_queue_t *msgq;
+    cprMsgQueueStats_t stats;
+
+    debugif_printf("CPR Message Queues\n");
+
+    pthread_mutex_lock(&msgQueueListMutex);
+    msgq = msgQueueList;
+    while (msgq != NULL) {
+        memset(&stats, 0, sizeof(stats));
+        cprGetMessageQueueStats(msgq, &stats);
+
+        debugif_printf("Name: %s\n", stats.name);
+        debugif_printf("   extended depth: %d\n", stats.extendedDepth);
+        debugif_printf("   max: %d\n", stats.maxCount);
+        debugif_printf("   active: %d\n", stats.currentCount);
+        debugif_printf("   total: %d\n", stats.totalCount);
+        debugif_printf("   retries: %d\n", stats.reTries);
+        debugif_printf("   high attempts: %d\n", stats.highAttempts);
+        debugif_printf("   send errors: %d\n", stats.sendErrors);
+        debugif_printf("   self queue errors: %d\n\n", stats.selfQErrors);
+
+        msgq = msgq->next;
+    }
+    pthread_mutex_unlock(&msgQueueListMutex);
+
+    return 0;
+}
+
+/**
+ * cprGetDepth
+ *
+ * @brief get depth of a message queue
+ *
+ * The pSIPCC uses this API to look at the depth of a message queue for internal
+ * routing and throttling decision
+ *
+ * @param[in] msgQueue - message queue
+ *
+ * @return depth of msgQueue
+ *
+ * @pre (msgQueue not_eq NULL)
+ */
+uint16_t cprGetDepth (cprMsgQueue_t msgQueue)
+{
+        cpr_msg_queue_t *msgq;
+        msgq = (cpr_msg_queue_t *) msgQueue;
+        return msgq->currentCount;
+}
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_ipc.h b/libs/sipcc/cpr/darwin/cpr_darwin_ipc.h
new file mode 100644 (file)
index 0000000..e8f8514
--- /dev/null
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_IPC_H_
+#define _CPR_DARWIN_IPC_H_
+
+#include "cpr_threads.h"
+#include <pthread.h>
+
+/* Enable support for cprSetMessageQueueThread API */
+#define CPR_USE_SET_MESSAGE_QUEUE_THREAD
+
+/* Maximum message size allowed by CNU */
+#define CPR_MAX_MSG_SIZE  8192
+
+/* Our CNU msgtype */
+#define PHONE_IPC_MSG 1
+
+
+/* For gathering statistics regarding message queues */
+typedef struct {
+    char name[16];
+    uint16_t maxCount;
+    uint16_t currentCount;
+    uint32_t totalCount;
+    uint32_t rcvTimeouts;
+    uint32_t sendErrors;
+    uint32_t reTries;
+    uint32_t highAttempts;
+    uint32_t selfQErrors;
+    uint16_t extendedDepth;
+} cprMsgQueueStats_t;
+
+/*
+ * Mutex for updating the message queue list
+ */
+extern pthread_mutex_t msgQueueListMutex;
+
+/**
+ * cprShowMessageQueueStats
+ *
+ * Display statistics for all message queues
+ *
+ * Comments: Function only applicable to CNU
+ */
+int32_t
+cprShowMessageQueueStats(int32_t argc, const char *argv[]);
+
+/**
+ * cprGetDepth
+ *
+ * Get depth of a message queue
+ */
+uint16_t cprGetDepth(cprMsgQueue_t msgQueue);
+
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_locks.c b/libs/sipcc/cpr/darwin/cpr_darwin_locks.c
new file mode 100644 (file)
index 0000000..5ac0cb8
--- /dev/null
@@ -0,0 +1,200 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include <errno.h>
+#include <pthread.h>
+
+/**
+ * cprCreateMutex
+ *
+ * @brief Creates a mutual exclusion block
+ *
+ * The cprCreateMutex function is called to allow the OS to perform whatever
+ * work is needed to create a mutex.
+ *
+ * @param[in] name  - name of the mutex. If present, CPR assigns this name to
+ * the mutex to assist in debugging.
+ *
+ * @return Mutex handle or NULL if creation failed. If NULL, set errno
+ */
+cprMutex_t
+cprCreateMutex (const char *name)
+{
+    static const char fname[] = "cprCreateMutex";
+    static uint16_t id = 0;
+    int32_t returnCode;
+    cpr_mutex_t *cprMutexPtr;
+    pthread_mutex_t *linuxMutexPtr;
+
+    /*
+     * Malloc memory for a new mutex. CPR has its' own
+     * set of mutexes so malloc one for the generic
+     * CPR view and one for the CNU specific version.
+     */
+    cprMutexPtr = (cpr_mutex_t *) cpr_malloc(sizeof(cpr_mutex_t));
+    linuxMutexPtr = (pthread_mutex_t *) cpr_malloc(sizeof(pthread_mutex_t));
+    if ((cprMutexPtr != NULL) && (linuxMutexPtr != NULL)) {
+        /* Assign name */
+        cprMutexPtr->name = name;
+
+        /*
+         * Use default mutex attributes. TBD: if we do not
+         * need cnuMutexAttributes global get rid of it
+         */
+        returnCode = pthread_mutex_init(linuxMutexPtr, NULL);
+        if (returnCode != 0) {
+            CPR_ERROR("%s - Failure trying to init Mutex %s: %d\n",
+                      fname, name, returnCode);
+            cpr_free(linuxMutexPtr);
+            cpr_free(cprMutexPtr);
+            return (cprMutex_t)NULL;
+        }
+
+        /*
+         * TODO - It would be nice for CPR to keep a linked
+         * list of active mutexes for debugging purposes
+         * such as a show command or walking the list to ensure
+         * that an application does not attempt to create
+         * the same mutex twice.
+         */
+        cprMutexPtr->u.handlePtr = linuxMutexPtr;
+        cprMutexPtr->lockId = ++id;
+        return (cprMutex_t)cprMutexPtr;
+    }
+
+    /*
+     * Since the code malloced two pointers ensure both
+     * are freed since one malloc call could have worked
+     * and the other failed.
+     */
+    if (linuxMutexPtr != NULL) {
+        cpr_free(linuxMutexPtr);
+    } else if (cprMutexPtr != NULL) {
+        cpr_free(cprMutexPtr);
+    }
+
+    /* Malloc failed */
+    CPR_ERROR("%s - Malloc for mutex %s failed.\n", fname, name);
+    errno = ENOMEM;
+    return (cprMutex_t)NULL;
+}
+
+
+/**
+ * cprDestroyMutex
+ *
+ * @brief Destroys the mutex passed in.
+ *
+ * The cprDestroyMutex function is called to destroy a mutex. It is the
+ * application's responsibility to ensure that the mutex is unlocked when
+ * destroyed. Unpredictiable behavior will occur if an application
+ * destroys a locked mutex.
+ *
+ * @param[in] mutex - mutex to destroy
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE. errno should be set for CPR_FAILURE.
+ */
+cprRC_t
+cprDestroyMutex (cprMutex_t mutex)
+{
+    static const char fname[] = "cprDestroyMutex";
+    cpr_mutex_t *cprMutexPtr;
+    int32_t rc;
+
+    cprMutexPtr = (cpr_mutex_t *) mutex;
+    if (cprMutexPtr != NULL) {
+        rc = pthread_mutex_destroy(cprMutexPtr->u.handlePtr);
+        if (rc != 0) {
+            CPR_ERROR("%s - Failure destroying Mutex %s: %d\n",
+                      fname, cprMutexPtr->name, rc);
+            return CPR_FAILURE;
+        }
+        cprMutexPtr->lockId = 0;
+        cpr_free(cprMutexPtr->u.handlePtr);
+        cpr_free(cprMutexPtr);
+        return CPR_SUCCESS;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprGetMutex
+ *
+ * @brief Acquire ownership of a mutex
+ *
+ * This function locks the mutex referenced by the mutex parameter. If the mutex
+ * is locked by another thread, the calling thread will block until the mutex is
+ * released.
+ *
+ * @param[in] mutex - Which mutex to acquire
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprGetMutex (cprMutex_t mutex)
+{
+    static const char fname[] = "cprGetMutex";
+    cpr_mutex_t *cprMutexPtr;
+    int32_t rc;
+
+    cprMutexPtr = (cpr_mutex_t *) mutex;
+    if (cprMutexPtr != NULL) {
+        rc = pthread_mutex_lock((pthread_mutex_t *) cprMutexPtr->u.handlePtr);
+        if (rc != 0) {
+            CPR_ERROR("%s - Error acquiring mutex %s: %d\n",
+                      fname, cprMutexPtr->name, rc);
+            return CPR_FAILURE;
+        }
+        return CPR_SUCCESS;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprReleaseMutex
+ *
+ * @brief Release ownership of a mutex
+ *
+ * This function unlocks the mutex referenced by the mutex parameter.
+ * @param[in] mutex - Which mutex to release
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprReleaseMutex (cprMutex_t mutex)
+{
+    static const char fname[] = "cprReleaseMutex";
+    cpr_mutex_t *cprMutexPtr;
+    int32_t rc;
+
+    cprMutexPtr = (cpr_mutex_t *) mutex;
+    if (cprMutexPtr != NULL) {
+        rc = pthread_mutex_unlock((pthread_mutex_t *) cprMutexPtr->u.handlePtr);
+        if (rc != 0) {
+            CPR_ERROR("%s - Error releasing mutex %s: %d\n",
+                      fname, cprMutexPtr->name, rc);
+            return CPR_FAILURE;
+        }
+        return CPR_SUCCESS;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_locks.h b/libs/sipcc/cpr/darwin/cpr_darwin_locks.h
new file mode 100644 (file)
index 0000000..440f09e
--- /dev/null
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_LOCKS_H_
+#define _CPR_DARWIN_LOCKS_H_
+
+/*
+ * System Mutexes.
+ */
+extern pthread_mutex_t linuxThreadMutex;
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_private.h b/libs/sipcc/cpr/darwin/cpr_darwin_private.h
new file mode 100644 (file)
index 0000000..887b58d
--- /dev/null
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_PRIVATE_H_
+#define _CPR_DARWIN_PRIVATE_H_
+
+extern pthread_mutexattr_t cprMutexAttributes;
+
+cpr_status_e cprLockInit(void);
+cpr_status_e cprTimerInit(void);
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_rand.h b/libs/sipcc/cpr/darwin/cpr_darwin_rand.h
new file mode 100644 (file)
index 0000000..4a0c9cf
--- /dev/null
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_RAND_H_
+#define _CPR_DARWIN_RAND_H_
+
+/* @def Defines to generate a random number */
+#define cpr_srand(seed) srand(seed)
+#define cpr_rand()      rand()
+
+
+
+#endif
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_socket.c b/libs/sipcc/cpr/darwin/cpr_darwin_socket.c
new file mode 100644 (file)
index 0000000..1f47907
--- /dev/null
@@ -0,0 +1,990 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_assert.h"
+#include "cpr_socket.h"
+#include "cpr_debug.h"
+#include "cpr_rand.h"
+#include "cpr_timers.h"
+#include "cpr_errno.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include <sys/syslog.h>
+#include <sys/fcntl.h>
+#include <ctype.h>
+
+
+const cpr_ip_addr_t ip_addr_invalid = {0};
+
+#define IN6ADDRSZ   16
+#define INT16SZ     2
+#define        INADDRSZ        4
+
+#define MAX_RETRY_FOR_EAGAIN 10
+
+/* Forward declarations of internal (helper) functions */
+static int cpr_inet_pton4(const char *src, uint8_t *dst, int pton);
+static int cpr_inet_pton6(const char *src, uint8_t *dst);
+
+/**
+ * cprBind
+ *
+ * @brief The cprBind function is the CPR wrapper for the "Bind" socket call.
+ *
+ * The cprBind() function shall assign a local socket address address to a
+ * socket identified by descriptor socket that has no local socket address
+ * assigned. Sockets created with the cprSocket() function are initially
+ * unnamed; they are identified only by their address family.
+ *
+ * @param[in] soc  - The socket previously created using cprAccept that is to be
+ *                   bound
+ * @param[out] addr - The address of the socket that is to be bound
+ * @param[in] addr_len Points to a cpr_socklen_t structure which on specifies the length
+ *                   of the supplied cpr_sockaddr_t structure.
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ *         @li [CPR_ENOTSOCK]   The descriptor references a file, not a socket
+ *
+ */
+cpr_status_e
+cprBind (cpr_socket_t soc,
+         CONST cpr_sockaddr_t * RESTRICT addr,
+         cpr_socklen_t addr_len)
+{
+    cprAssert(addr != NULL, CPR_FAILURE);
+
+    return ((bind(soc, (struct sockaddr *)addr, addr_len) != 0) ?
+            CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprCloseSocket
+ *
+ * @brief The cprCloseSocket function shall destroy the socket
+ *
+ * The cprCloseSocket() function shall destroy the socket descriptor indicated
+ * by socket.  The descriptor may be made available for return by subsequent
+ * calls to cprSocket().  If the linger option is set with a non-zero timeout
+ * and the socket has untransmitted data, then cprCloseSocket() shall block for
+ * up to the current linger interval until all data is transmitted.
+ *
+ * @param[in] soc  - The socket that needs to be destroyed
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+cprCloseSocket (cpr_socket_t soc)
+{
+    return ((close(soc) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprConnect
+ *
+ * @brief The cprConnect function is the wrapper for the "connect" socket API
+ *
+ * The cprConnect() function shall attempt to make a connection on a socket.
+ * If the connection cannot be established immediately and non-blocking is set for
+ * the socket, cprConnect() shall fail and set cpr_errno to [CPR_EINPROGRESS], but
+ * the connection request shall not be aborted, and the connection shall be
+ * established asynchronously. When the connection has been established
+ * asynchronously, cprSelect() shall indicate that the file descriptor for the
+ * socket is ready for writing.
+ * If the initiating socket is connection-mode, then cprConnect() shall attempt to
+ * establish a connection to the address specified by the address argument.If the
+ * connection cannot be established immediately and non-blocking is not set for
+ * the socket, cprConnect() shall block for up to an unspecified timeout interval
+ * until the connection is established. If the timeout interval expires before the
+ * connection is established, cprConnect() shall fail and the connection attempt
+ * shall be aborted.
+ * If the initiating socket is connectionless (i.e. SOCK_DGRAM), then cprConnect()
+ * shall set the socket's peer address, and no connection is made. The peeraddress
+ * identifies where all datagrams are sent on subsequent cprSend() functions, and
+ * limits the remote sender for subsequent cprRecv() functions. If address is a
+ * null address for the protocol, the socket's peer address shall be reset.
+ *
+ * @param[in] soc  - Specifies the socket created with cprSocket() to connect
+ * @param[in] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
+ * @param[in] addr_len  - Points to a cpr_socklen_t structure which specifies
+ *                        the length of the supplied cpr_sockaddr_t structure.
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+cprConnect (cpr_socket_t soc,
+            SUPPORT_CONNECT_CONST cpr_sockaddr_t * RESTRICT addr,
+            cpr_socklen_t addr_len)
+{
+    int retry = 0, retval;
+
+    cprAssert(addr != NULL, CPR_FAILURE);
+
+    retval = connect(soc, (struct sockaddr *)addr, addr_len);
+
+    while( retval == -1 && ((errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN) || errno == EINPROGRESS || errno == EALREADY) ) {
+      cprSleep(100);
+      retry++;
+      retval = connect(soc, (struct sockaddr *)addr, addr_len);
+    }
+
+    cpr_status_e returnValue = CPR_FAILURE;
+    if ( retval == 0 || (retval == -1 && errno == EISCONN))
+    {
+        returnValue =  CPR_SUCCESS;
+    }
+
+  return returnValue;
+}
+
+/**
+ * cprGetSockName
+ *
+ * @brief The cprGetSockName retrieves the locally-bound name of the socket
+ *
+ * The cprGetSockName() function shall retrieve the locally-bound name of the
+ * specified socket, store this address in the cpr_sockaddr_t struct
+ * structure pointed to by the "addr" argument, and store the length of this address in
+ * the object pointed to by the "addr_len" argument.  If the actual length
+ * of the address is greater than the length of the supplied cpr_sockaddr_t
+ * structure, the stored address shall be truncated.  If the socket has not been
+ * bound to a local name, the value stored in the object pointed to by address is
+ * unspecified.
+ *
+ * @param[in] soc  - Specifies the socket to get the peer address from
+ * @param[out] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
+ * @param[out] addr_len  - Points to a cpr_socklen_t structure which specifies
+ *                        the length of the supplied cpr_sockaddr_t structure.
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ *  @note If successful, the address argument shall point to the address of the socket
+ *  @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
+ *        @li [CPR_ENOTCONN]  The socket is not connected
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_OPNOTSUPP] The operation is not supported for the socket
+ */
+cpr_status_e
+cprGetSockName (cpr_socket_t soc,
+                cpr_sockaddr_t * RESTRICT addr,
+                cpr_socklen_t * RESTRICT addr_len)
+{
+    cprAssert(addr != NULL, CPR_FAILURE);
+    cprAssert(addr_len != NULL, CPR_FAILURE);
+
+    return ((getsockname(soc, (struct sockaddr *)addr, addr_len) != 0) ?
+            CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprListen
+ *
+ * @brief The cprListen is the CPR wrapper for the "listen" socket API.
+ *
+ * The cprListen() function shall mark a connection-mode socket, specified by
+ * the "soc" argument, as accepting connections.  The "backlog" argument
+ * provides a hint to the implementation which the implementation shall use to
+ * limit the number of outstanding connections in the socket's listen queue.
+ * Implementations may impose a limit on backlog and silently reduce the
+ * specified value. Implementations shall support values of backlog up to
+ * SOMAXCONN.
+ * If listen() is called with a backlog argument value that is less than zero
+ * (0), the function behaves as if it had been called with a backlog argument
+ * value of 0.  A backlog argument of zero (0) may allow the socket to accept
+ * connections, in which case the length of the listen queue may be set to an
+ * implementation-defined minimum value.
+ *
+ * @param[in] soc  - Specifies the socket to get the peer address
+ * @param[in] backlog  - The limit on the number of outstanding connections
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *  @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EDESTADDRREQ]  The socket is not bound to a local address
+ */
+cpr_status_e
+cprListen (cpr_socket_t soc,
+           uint16_t backlog)
+{
+    return ((listen(soc, backlog) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprRecv
+ *
+ * @brief The cprRecv() function shall receive a message from a socket.
+ *
+ * This function is normally used with connected sockets because it does not permit
+ * the application to retrieve the source address of received data.  The cprRecv()
+ * function shall return the length of the message written to the buffer pointed
+ * to by the "buf" argument.
+ * For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
+ * a single operation.  If the message is too long to fit in the supplied buffer
+ * and the "flags" argument does not have MSG_PEEK set, the excess bytes are
+ * discarded.  If the MSG_WAITALL flag is not set, data shall be returned onlyup
+ * to the end of the first message.
+ * For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
+ * data is returned as it becomes available; therefore, no data is discarded.
+ * If no messages are available at the socket and non-blocking is not set on the
+ * socket's file descriptor, cprRecv() shall block until a message arrives. If no
+ * messages are available at the socket and non-blocking is set on the socket's
+ * file descriptor, cprRecv() shall fail and set cpr_errno to [CPR_EAGAIN]or
+ * [CPR_EWOULDBLOCK].
+ * The cprSelect() function can be used to determine when data is available to be
+ * received.  The cprRecv() function is the same as cprRecvFrom() with a zero
+ * address_len argument.
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ * @param[in] flags  - The options used for the recv.
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+cprRecv (cpr_socket_t soc,
+         void * RESTRICT buf,
+         size_t len,
+         int32_t flags)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(buf != NULL, CPR_FAILURE);
+
+    rc = recv(soc, buf, len, flags);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = recv(soc, buf, len, flags);
+    }
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprRecvFrom
+ *
+ * @brief The cprRecvFrom() function shall receive a message from a specific socket.
+ *
+ * The cprRecvFrom() function shall receive a message from a socket and is
+ * normally used with connectionless-mode sockets because it permits the
+ * application to retrieve the source address of received data.  This function
+ * shall return the length of the message written to the buffer pointed to bythe
+ * buffer argument.
+ * For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
+ * a single operation.  If the message is too long to fit in the supplied buffer
+ * and the flags argument does not have MSG_PEEK set, the excess bytes are
+ * discarded.  If the MSG_WAITALL flag is not set, data shall be returned onlyup
+ * to the end of the first message.
+ * For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
+ * data is returned as it becomes available; therefore, no data is discarded.
+ * If no messages are available at the socket and non-blocking is not set on the
+ * socket's file descriptor, cprRecvFrom() shall block until a message arrives. If no
+ * messages are available at the socket and non-blocking is set on the socket's
+ * file descriptor, cprRecvFrom() shall fail and set cpr_errno to [CPR_EAGAIN]or
+ * [CPR_EWOULDBLOCK].
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ * @param[in] flags  - The options used for the recvFrom
+ * @param[out] from - A null pointer or pointer to a cpr_sockaddr_t structure in
+ *                   which the sending address is to be stored.
+ * @param[out] fromlen - The length of the cpr_sockaddr_t structure pointed to by
+ *                    the "from" argument.
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+cprRecvFrom (cpr_socket_t soc,
+             void * RESTRICT buf,
+             size_t len,
+             int32_t flags,
+             cpr_sockaddr_t * RESTRICT from,
+             cpr_socklen_t * RESTRICT fromlen)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(buf != NULL, CPR_FAILURE);
+    cprAssert(from != NULL, CPR_FAILURE);
+    cprAssert(fromlen != NULL, CPR_FAILURE);
+
+    rc = recvfrom(soc, buf, len, flags, (struct sockaddr *)from, fromlen);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = recvfrom(soc, buf, len, flags, (struct sockaddr *)from, fromlen);
+    }
+
+    if (rc == -1) {
+        CPR_INFO("error in recvfrom buf=%x fromlen=%d\n", buf, *fromlen);
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSelect
+ *
+ * @brief The cprSelect() function is the CPR wrapper for the "select" socket API.
+ *
+ * The cprSelect() function returns which of the specified file descriptors is ready for
+ * reading, ready for writing, or has an exception pending.  The function will
+ * block up to the specified timeout interval for one of the conditions to be
+ * true or until interrupted by a signal.
+ *
+ * File descriptor masks of type fd_set can be initialized and tested with
+ * FD_CLR(), FD_ISSET(), FD_SET(), and FD_ZERO().  The OS-implementation may
+ * implement these calls either as a macro definition or an actual function.
+ * void FD_CLR(cpr_socket_t, fd_set *) shall remove the file descriptor fd from the
+ * set. If fd is not a member of this set, there shall be no effect on theset.
+ * int FD_ISSET(cpr_socket_t, fd_set *) shall evaluate to non-zero if the file
+ * descriptor fd is a member of the set, and shall evaluate to zero otherwise.
+ * void FD_SET(cpr_socket_t, fd_set *) shall add the file descriptor fd to the set.
+ * If the file descriptor fd is already in this set, there shall be no effect on
+ * the set.
+ * void FD_ZERO(fd_set *) shall initialize the descriptor set to the null set.
+ *
+ * @param[in] nfds    Specifies the argument range of file descriptors to be tested. The
+ *            descriptors from zero through nfds-1 in the descriptor sets shall be examined.
+ * @param[in] read_fds    If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for being ready to read.
+ *    @li On output this specifies the file descriptors that are ready to read.
+ * @param[in] write_fds   If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for being ready to write.
+ *    @li On output this specifies the file descriptors that are ready to write.
+ * @param[in] except_fds   If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for errors/exceptions pending.
+ *    @li On output this specifies the file descriptors that have errors/exceptions pending.
+ * @param[in] timeout If not a null pointer, this  points to an object of type struct cpr_timeval
+ *       that specifies the maximum time interval to wait for the selection to complete.
+ *       If timeout expires, the function shall return.  If the parameter is a null pointer, the function
+ *       will block indefinitely until at least one file descriptor meets the criteria.
+ *
+ * @note While this function supports multiple file descriptor types, only file descriptors referring to a
+ *      socket are guaranteed to be supported.
+ * @note Note that the "nfds" parameter is not used in Windows.
+ *
+ * @return Upon successful completion, cprSelect() shall return the number of file descriptors ready.
+ *       Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to indicate the error where read_fds,
+ *       write_fds and error_fds are not modified.
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_INTR]   The function was interrupted before an event or
+ *                         timeout occurred
+ *        @li [CPR_INVAL]  An invalid timeout was specified or nfds is less
+ *                        than 0 or greater than FD_SETSIZE
+ *
+ */
+int16_t
+cprSelect (uint32_t nfds,
+           fd_set * RESTRICT read_fds,
+           fd_set * RESTRICT write_fds,
+           fd_set * RESTRICT except_fds,
+           struct cpr_timeval * RESTRICT timeout)
+{
+    int16_t rc;
+    struct timeval t, *t_p;
+
+    if (timeout != NULL) {
+        t.tv_sec  = timeout->tv_sec;
+        t.tv_usec = timeout->tv_usec;
+        t_p       = &t;
+    } else {
+        t_p       = NULL;
+    }
+
+    rc = (int16_t) select(nfds, read_fds, write_fds, except_fds, t_p);
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSend
+ *
+ * @brief The cprSend() function is the CPR wrapper for the "send" socket API.
+ *
+ * The cprSend() function shall transmit a message from the specified socket to
+ * its peer. The cprSend() function shall send a message only when the socket is
+ * connected. The length of the message to be sent is specified by the length
+ * argument. If the message is too long to pass through the underlying protocol,
+ * cprSend() shall fail and no data is transmitted.  Delivery of the message is
+ * not guaranteed.
+ * If space is not available at the sending socket to hold the message to be
+ * transmitted, and the socket does not have non-blocking set, cprSend() shall
+ * block until space is available; otherwise, if non-blocking is set, the
+ * cprSend() call shall fail.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags   The socket options
+ *
+ * @return Upon successful completion, cprSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+cprSend (cpr_socket_t soc,
+         CONST void *buf,
+         size_t len,
+         int32_t flags)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(buf != NULL, CPR_FAILURE);
+
+    rc = send(soc, buf, len, flags);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = send(soc, buf, len, flags);
+    }
+
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSendTo
+ *
+ * @brief The cprSendTo() function is the CPR wrapper for the "send" socket API.
+ *
+ * The cprSendTo() function shall send a message through a socket. If the socket
+ * is connectionless-mode, the message shall be sent to the address specified by
+ * address. If the socket is connection-mode, address shall be ignored.
+ * Delivery of the message is not guaranteed.
+ * If space is not available at the sending socket to hold the message to be
+ * transmitted, and the socket does not have non-blocking set, cprSendTo() shall
+ * block until space is available; otherwise, if non-blocking is set, the
+ * cprSendTo() call shall fail.
+ * The cprSelect() function can be used to determine when it is possible to send
+ * more data.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] msg  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags   The socket options
+ * @param[in] dest_addr Points to a cpr_sockaddr_t structure containing the destination
+ *                    address.
+ * @param[in] dest_len Specifies the length of the cpr_sockaddr_t structure pointed to by
+ *                     the "dest_addr" argument.
+ *
+ * @return Upon successful completion, cprSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+cprSendTo (cpr_socket_t soc,
+           CONST void *msg,
+           size_t len,
+           int32_t flags,
+           CONST cpr_sockaddr_t *dest_addr,
+           cpr_socklen_t dest_len)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(msg != NULL, CPR_FAILURE);
+    cprAssert(dest_addr != NULL, CPR_FAILURE);
+
+    rc = sendto(soc, msg, len, flags, (struct sockaddr *)dest_addr, dest_len);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = sendto(soc, msg, len, flags, (struct sockaddr *)dest_addr, dest_len);
+    }
+
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSetSockOpt
+ *
+ * @brief The cprSetSockOpt() function is used to set the socket options
+ *
+ * The cprSetSockOpt() function shall set the option specified by the
+ * option_name argument, at the protocol level specified by the "level" argument,
+ * to the value pointed to by the "opt_val" argument for the socket specified
+ * by the "soc" argument.
+ * The level argument specifies the protocol level at which the option resides. To
+ * set options at the socket level, specify the level argument as SOL_SOCKET. To
+ * set options at other levels, supply the appropriate level identifier for the
+ * protocol controlling the option. For example, to indicate that an option is
+ * interpreted by the TCP (Transport Control Protocol), set level to IPPROTO_TCP
+ * as defined in the <cpr_in.h> header.
+ * The opt_name argument specifies a single option to set. The option_name
+ * argument and any specified options are passed uninterpreted to the appropriate
+ * protocol module. The <cpr_socket.h> header defines the socket-level options.
+ *
+ * @param[in] soc The socket on which the options need to be set
+ * @param[in] level The protocol level at which the option resides
+ * @param[in] opt_name This specifies the single option that is being set
+ * @param[in] opt_val The values for the option
+ * @param[in] opt_len The length field for the option values
+ *
+ * @return Upon successful completion, CPR_SUCCESS shall be returned;
+ * otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
+ * error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EINVAL]    The specified option is invalid or the socket is
+ *                          shut down
+ *        @li [CPR_EISCONN]   The specified socket is already connected and can
+ *                          not be changed
+ *        @li [CPR_ENOPROTOOPT]   The option is not supported by the protocol
+ *
+ */
+cpr_status_e
+cprSetSockOpt (cpr_socket_t soc,
+               uint32_t level,
+               uint32_t opt_name,
+               CONST void *opt_val,
+               cpr_socklen_t opt_len)
+{
+    cprAssert(opt_val != NULL, CPR_FAILURE);
+
+    return ((setsockopt(soc, (int)level, (int)opt_name, opt_val, opt_len) != 0)
+            ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprSetSockNonBlock
+ *
+ * @brief The cprSetSockNonBlock() function is used to set the socket options
+ *
+ * The cprSetSockNonBlock() function shall set a socket to be non blocking. It
+ * uses the fcntl function on the socket desriptor to achieve this. If the fcntl
+ * operation fails, a CPR_FAILURE is returned and errno is set by the OS
+ * implementation.
+ *
+ * @param[in] soc The socket that needs to be set to non-blocking
+ *
+ * @return Upon successful completion, CPR_SUCCESS shall be returned;
+ * otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
+ * error.
+ */
+cpr_status_e
+cprSetSockNonBlock (cpr_socket_t soc)
+{
+    return ((fcntl(soc, F_SETFL, O_NONBLOCK) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+cpr_status_e
+cprShutDown (cpr_socket_t soc,
+             cpr_shutdown_e how)
+{
+    int os_how;
+
+    switch (how) {
+      case CPR_SHUTDOWN_RECEIVE:
+          os_how = SHUT_RD;
+          break;
+      case CPR_SHUTDOWN_SEND:
+          os_how = SHUT_WR;
+          break;
+      case CPR_SHUTDOWN_BOTH:
+      default:
+          os_how = SHUT_RDWR;
+          break;
+    }
+
+    return ((shutdown(soc, os_how) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprSocket
+ *
+ * @brief The cprSocket() is the CPR wrapper for the "socket" API
+ *
+ * The cprSocket() function shall create an unbound socket in a
+ * communications domain, and return a file descriptor that can be used
+ * in later function calls that operate on sockets.
+ *
+ * @param[in] domain  The communications domain, i.e. address family, in which a socket is to
+ *                   be created
+ * @param[in] type    The type of socket to be created. The following types must
+ *                   be supported:
+ *             @li SOCK_STREAM Provides sequenced, reliable, bidirectional, connection-mode
+ *                               byte streams, i.e. TCP
+ *             @li SOCK_DGRAM  Provides connectionless-mode, unreliable
+ *                           datagrams of fixed maximum length, i.e. UDP
+ *             @li SOCK_SEQPACKET   Provides sequenced, reliable, bidirectional, connection-mode
+ *                          transmission paths for records.  A single operation never transfers part of
+ *                          more than one record.  Record boundaries are visible to the receiver via the
+ *                          MSG_EOR flag.
+ * @param[in] protocol    The protocol to be used with the socket.
+ *
+ * @return Upon successful completion, a socket handle defined by
+ *      cpr_socket_t shall be returned; otherwise, INVALID_SOCKET shall be returned and
+ *       cpr_errno set to indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EINVAL]   The specified option is invalid or the socket is
+ *                           shut down
+ *        @li [CPR_EMFILE]   No more socket descriptors available for the
+ *                           process
+ *        @li [CPR_ENFILE]   No more socket descriptors available for the
+ *                           system
+ *        @li [CPR_EPROTOTYPE] The socket type is not supported by the
+ *                              protocol
+ *        @li [CPR_EPROTONOSUPPORT]   The protocol is not supported for the
+ *                                    domain
+ *
+ */
+cpr_socket_t
+cprSocket (uint32_t domain,
+           uint32_t type,
+           uint32_t protocol)
+{
+    cpr_socket_t s;
+
+    s = socket((int)domain, (int)type, (int)protocol);
+    if (s == -1) {
+        return INVALID_SOCKET;
+    }
+    return s;
+}
+
+/**
+ * @}
+ */
+
+
+
+/* cpr_inet_pton
+ *     Convert from presentation format (which usually means ASCII printable)
+ *     to network format (which is usually some kind of binary format).
+ * @param[in] af The address family IPv4 or IPv6
+ * @param[in] src The address that needs to be converted
+ * @param[out] dst The address after the conversion
+ * @return
+ *     1 if the address was valid for the specified address family
+ *     0 if the address wasn't valid (`dst' is untouched in this case)
+ *     -1 if some other error occurred (`dst' is untouched in this case, too)
+ */
+int
+cpr_inet_pton (int af, const char *src, void *dst)
+{
+
+       switch (af) {
+       case AF_INET:
+               return (cpr_inet_pton4(src, dst, 1));
+       case AF_INET6:
+               return (cpr_inet_pton6(src, dst));
+       default:
+               return (-1);
+       }
+       /* NOTREACHED */
+}
+
+
+/**
+ *  Utility function that sets up the socket address
+ *
+ *  @param[in] addr - socket fd to bind with the IPC address.
+ *  @param[in] name - pointer to the name of socket to bind to.
+ *
+ *
+ *  @pre  (name != NULL)
+ */
+void cpr_set_sockun_addr (cpr_sockaddr_un_t *addr, const char *name, pid_t pid)
+{
+    /* Bind to the local socket */
+    memset(addr, 0, sizeof(cpr_sockaddr_un_t));
+    addr->sun_family = AF_UNIX;
+    snprintf((char *) addr->sun_path, sizeof(addr->sun_path), "%s_%d", name, pid);
+}
+
+/* int
+ * inet_pton4(src, dst, pton)
+ *     when last arg is 0: inet_aton(). with hexadecimal, octal and shorthand.
+ *     when last arg is 1: inet_pton(). decimal dotted-quad only.
+ * return:
+ *     1 if `src' is a valid input, else 0.
+ * notice:
+ *     does not touch `dst' unless it's returning 1.
+ */
+static int
+cpr_inet_pton4(const char *src, uint8_t *dst, int pton)
+{
+       uint32_t val;
+       uint32_t digit;
+       int base, n;
+       unsigned char c;
+       uint32_t parts[4];
+       uint32_t *pp = parts;
+
+       c = *src;
+       for (;;) {
+               /*
+                * Collect number up to ``.''.
+                * Values are specified as for C:
+                * 0x=hex, 0=octal, isdigit=decimal.
+                */
+               if (!isdigit(c))
+                       return (0);
+               val = 0; base = 10;
+               if (c == '0') {
+                       c = *++src;
+                       if (c == 'x' || c == 'X')
+                               base = 16, c = *++src;
+                       else if (isdigit(c) && c != '9')
+                               base = 8;
+               }
+               /* inet_pton() takes decimal only */
+               if (pton && base != 10)
+                       return (0);
+               for (;;) {
+                       if (isdigit(c)) {
+                               digit = c - '0';
+                               if (digit >= (uint16_t)base)
+                                       break;
+                               val = (val * base) + digit;
+                               c = *++src;
+                       } else if (base == 16 && isxdigit(c)) {
+                               digit = c + 10 - (islower(c) ? 'a' : 'A');
+                               if (digit >= 16)
+                                       break;
+                               val = (val << 4) | digit;
+                               c = *++src;
+                       } else
+                               break;
+               }
+               if (c == '.') {
+                       /*
+                        * Internet format:
+                        *      a.b.c.d
+                        *      a.b.c   (with c treated as 16 bits)
+                        *      a.b     (with b treated as 24 bits)
+                        *      a       (with a treated as 32 bits)
+                        */
+                       if (pp >= parts + 3)
+                               return (0);
+                       *pp++ = val;
+                       c = *++src;
+               } else
+                       break;
+       }
+       /*
+        * Check for trailing characters.
+        */
+       if (c != '\0' && !isspace(c))
+               return (0);
+       /*
+        * Concoct the address according to
+        * the number of parts specified.
+        */
+       n = pp - parts + 1;
+       /* inet_pton() takes dotted-quad only.  it does not take shorthand. */
+       if (pton && n != 4)
+               return (0);
+       switch (n) {
+
+       case 0:
+               return (0);             /* initial nondigit */
+
+       case 1:                         /* a -- 32 bits */
+               break;
+
+       case 2:                         /* a.b -- 8.24 bits */
+               if (parts[0] > 0xff || val > 0xffffff)
+                       return (0);
+               val |= parts[0] << 24;
+               break;
+
+       case 3:                         /* a.b.c -- 8.8.16 bits */
+               if ((parts[0] | parts[1]) > 0xff || val > 0xffff)
+                       return (0);
+               val |= (parts[0] << 24) | (parts[1] << 16);
+               break;
+
+       case 4:                         /* a.b.c.d -- 8.8.8.8 bits */
+               if ((parts[0] | parts[1] | parts[2] | val) > 0xff)
+                       return (0);
+               val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+               break;
+       }
+       if (dst) {
+               val = htonl(val);
+               memcpy(dst, &val, INADDRSZ);
+       }
+       return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ *     convert presentation level address to network order binary form.
+ * return:
+ *     1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ *     (1) does not touch `dst' unless it's returning 1.
+ *     (2) :: in a full address is silently ignored.
+ */
+static int
+cpr_inet_pton6(const char *src, uint8_t *dst)
+{
+       static const char xdigits_l[] = "0123456789abcdef",
+                         xdigits_u[] = "0123456789ABCDEF";
+       uint8_t tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+       const char *xdigits, *curtok;
+       int ch, saw_xdigit;
+       unsigned int val;
+
+       memset((tp = tmp), '\0', IN6ADDRSZ);
+       endp = tp + IN6ADDRSZ;
+       colonp = NULL;
+       /* Leading :: requires some special handling. */
+       if (*src == ':')
+               if (*++src != ':')
+                       return (0);
+       curtok = src;
+       saw_xdigit = 0;
+       val = 0;
+       while ((ch = *src++) != '\0') {
+               const char *pch;
+
+               if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+                       pch = strchr((xdigits = xdigits_u), ch);
+               if (pch != NULL) {
+                       val <<= 4;
+                       val |= (pch - xdigits);
+                       if (val > 0xffff)
+                               return (0);
+                       saw_xdigit = 1;
+                       continue;
+               }
+               if (ch == ':') {
+                       curtok = src;
+                       if (!saw_xdigit) {
+                               if (colonp)
+                                       return (0);
+                               colonp = tp;
+                               continue;
+                       } else if (*src == '\0')
+                               return (0);
+                       if (tp + INT16SZ > endp)
+                               return (0);
+                       *tp++ = (uint8_t) (val >> 8) & 0xff;
+                       *tp++ = (uint8_t) val & 0xff;
+                       saw_xdigit = 0;
+                       val = 0;
+                       continue;
+               }
+               if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+                   cpr_inet_pton4(curtok, tp, 1) > 0) {
+                       tp += INADDRSZ;
+                       saw_xdigit = 0;
+                       break;  /* '\0' was seen by inet_pton4(). */
+               }
+               return (0);
+       }
+       if (saw_xdigit) {
+               if (tp + INT16SZ > endp)
+                       return (0);
+               *tp++ = (uint8_t) (val >> 8) & 0xff;
+               *tp++ = (uint8_t) val & 0xff;
+       }
+       if (colonp != NULL) {
+               /*
+                * Since some memmove()'s erroneously fail to handle
+                * overlapping regions, we'll do the shift by hand.
+                */
+               const int n = tp - colonp;
+               int i;
+
+               if (tp == endp)
+                       return (0);
+               for (i = 1; i <= n; i++) {
+                       endp[- i] = colonp[n - i];
+                       colonp[n - i] = 0;
+               }
+               tp = endp;
+       }
+       if (tp != endp)
+               return (0);
+       memcpy(dst, tmp, IN6ADDRSZ);
+       return (1);
+}
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_socket.h b/libs/sipcc/cpr/darwin/cpr_darwin_socket.h
new file mode 100644 (file)
index 0000000..6a126d0
--- /dev/null
@@ -0,0 +1,313 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_SOCKET_H_
+#define _CPR_DARWIN_SOCKET_H_
+
+#include "cpr_types.h"
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+
+/*
+ * Define socket option levels
+ *   SOL_SOCKET
+ *   SOL_IP
+ *   SOL_TCP
+ *   SOL_UDP
+ */
+
+/* defined in netinet/in.h */
+#define SOL_IP      IPPROTO_IP
+#define SOL_TCP     IPPROTO_TCP
+#define SOL_UDP     IPPROTO_UDP
+
+
+/**
+ * Set public CPR header file options
+ */
+#ifdef CPR_USE_SOCKETPAIR
+#undef CPR_USE_SOCKETPAIR
+#endif
+#define SUPPORT_CONNECT_CONST const
+
+/**
+ * Define SOCKET_ERROR
+ */
+#define SOCKET_ERROR   (-1)
+
+/**
+ * Define INVALID_SOCKET
+ */
+#define INVALID_SOCKET (-1)
+
+/**
+ * Define cpr_socket_t
+ */
+typedef int cpr_socket_t;
+
+/**
+ * Define cpr_socklen_t
+ */
+typedef socklen_t cpr_socklen_t;
+
+/**
+ * Address family, defined in sys/socket.h
+ *  AF_UNSPEC
+ *  AF_LOCAL / AF_UNIX
+ *  AF_INET
+ *  AF_INET6
+ *  AF_MAX
+ *
+ *  AF_NETLYR2 (unique to CNU) interface directly to layer 2, bypass IP
+ */
+#ifndef AF_UNIX
+#define AF_UNIX  AF_LOCAL
+#endif
+
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981, based on the BSD file netinet/in.h.
+ * IPv6 additions per RFC 2292.
+ */
+
+
+/*
+ * Define the following socket options as needed
+ *   SO_DEBUG
+ *   SO_ACCEPTCONN
+ *   SO_REUSEADDR / SO_EXCLUSIVEADDRUSE
+ *   SO_KEEPALIVE
+ *   SO_DONTROUTE
+ *   SO_BROADCAST
+ *   SO_USELOOPBACK
+ *   SO_LINGER / SO_DONTLINGER
+ *   SO_OOBINLINE
+ *   SO_SNDBUF
+ *   SO_RCVBUF
+ *   SO_ERROR
+ *   SO_TYPE
+ *
+ * The following options are available for Unix-only variants
+ *   SO_SNDLOWAT
+ *   SO_RCVLOWAT
+ *   SO_SNDTIMEO
+ *   SO_RCVTIMEO
+ *   SO_PROTOTYPE - Not documented as being supported by CNU
+ */
+
+/* defined in netinet/in.h */
+#define SO_DONTLINGER       ((int)(~SO_LINGER))
+#define SO_EXCLUSIVEADDRUSE ((int)(~SO_REUSEADDR))
+
+/*
+ * Protocols (Base),
+ * reference http://www.iana.org/assignments/protocol-numbers.html
+ *   IPPROTO_IP
+ *   IPPROTO_GGP
+ *   IPPROTO_ICMP
+ *   IPPROTO_IGMP
+ *   IPPROTO_IPV4 / IPPROTO_IPIP
+ *   IPPROTO_TCP
+ *   IPPROTO_EGP
+ *   IPPROTO_PUP
+ *   IPPROTO_UDP
+ *   IPPROTO_IDP
+ *   IPPROTO_IPV6
+ *   IPPROTO_ROUTING
+ *   IPPROTO_FRAGMENT
+ *   IPPROTO_ESP
+ *   IPPROTO_AH
+ *   IPPROTO_ICMPV6
+ *   IPPROTO_NONE
+ *   IPPROTO_DSTOPTS
+ *   IPPROTO_ND
+ *   IPPROTO_EON
+ *   IPPROTO_IGRP
+ *   IPPROTO_ENCAP
+ *   IPPROTO_IPCOMP
+ *   IPPROTO_RAW
+ *   IPPROTO_MAX
+ */
+
+/* defined in netinet/in.h */
+#ifndef IPPROTO_IPV4
+#define IPPROTO_IPV4         4
+#endif
+
+#ifndef IPPROTO_IPIP
+#define IPPROTO_IPIP  IPPROTO_IPV4
+#endif
+
+//#define IPPROTO_RSVP        46
+#define IPPROTO_IGRP        88  /* Cisco/GXS IGRP */
+#define IPPROTO_EIGRP       88
+#define IPPROTO_IPCOMP     108  /* IP payload compression */
+
+/*
+ * Protocols (IPv6)
+ *   Assumming if IPV6 is not there, then none of the are
+ */
+#ifndef IPPROTO_IPV6
+#define IPPROTO_HOPOPTS      0  /* IPv6 hop-by-hop options */
+#define IPPROTO_IPV6        41  /* IPv6 */
+#define IPPROTO_ROUTING     43  /* IPv6 routing header */
+#define IPPROTO_FRAGMENT    44  /* IPv6 fragmentation header */
+#define IPPROTO_ICMPV6      58  /* ICMPv6 */
+#define IPPROTO_NONE        59  /* IPv6 no next header */
+#define IPPROTO_DSTOPTS     60  /* IPv6 destination options */
+#endif
+
+/*
+ * Protocols (Local)
+ * reference, RFC 3692
+ *   IPPROTO_UNX       Local sockets Unix protocol
+ *   IPPROTO_CDP       Non-Standard at 254, technially this value
+ *                     is for experimentation and testing
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Port/socket numbers: network standard functions
+ * reference http://www.iana.org/assignments/port-numbers
+ *  IPPORT_ECHO
+ *  IPPORT_DISCARD
+ *  IPPORT_SYSTAT
+ *  IPPORT_DAYTIME
+ *  IPPORT_NETSTAT
+ *  IPPORT_FTP
+ *  IPPORT_SSH
+ *  IPPORT_TELNET
+ *  IPPORT_SMTP
+ *  IPPORT_TIMESERVER
+ *  IPPORT_NAMESERVER
+ *  IPPORT_WHOIS
+ *  IPPORT_MTP
+ *  IPPORT_HTTP
+ *  IPPORT_NTP
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Port/socket numbers: host specific functions
+ *
+ *  IPPORT_TFTP
+ *  IPPORT_RJE
+ *  IPPORT_FINGER
+ *  IPPORT_TTYLINK
+ *  IPPORT_SUPDUP
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * UNIX TCP sockets
+ *
+ *  IPPORT_EXECSERVER
+ *  IPPORT_LOGINSERVER
+ *  IPPORT_CMDSERVER
+ *  IPPORT_EFSSERVER
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * UNIX UDP sockets
+ *
+ *  IPPORT_BIFFUDP
+ *  IPPORT_WHOSERVER
+ *  IPPORT_ROUTESERVER
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * SCCP sockets
+ */
+#define IPPORT_SCCP       2000
+
+/*
+ * tbd: need to finalize placement.
+ * Define range of ephemeral ports used for Cisco IP Phones.
+ */
+#define CIPPORT_EPH_LOW         0xC000
+#define CIPPORT_EPH_HI          0xCFFF
+
+
+/*
+ * Ports < IPPORT_RESERVED are reserved for
+ * privileged processes (e.g. root).
+ *
+ * IPPORT_RESERVED
+ * IPPORT_USERRESERVED
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Define INADDR constants
+ *   INADDR_ANY
+ *   INADDR_LOOPBACK
+ *   INADDR_BROADCAST
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Define IN_CLASS constants/macros
+ *   IN_CLASS{A|B|C|D}(x)
+ *   IN_CLASS{A|B|C|D}_NET
+ *   IN_CLASS{A|B|C|D}_NSHIFT
+ *   IN_CLASS{A|B|C|D}_HOST
+ *   IN_CLASS{A|B|C|D}_MAX
+ */
+
+typedef struct sockaddr_un cpr_sockaddr_un_t;
+
+
+#define cpr_sun_len(a) sizeof(a)
+void cpr_set_sockun_addr(cpr_sockaddr_un_t *addr, const char *name, pid_t pid);
+
+/*
+ * To represent desired sockaddr max alignment for platform, a
+ * type is chosen which may depend on implementation platform architecture.
+ * Type chosen based on alignment size restrictions from <sys/isa_defs.h>.
+ * We desire to force up to (but no more than) 64-bit (8 byte) alignment,
+ * on platforms where it is possible to do so. (e.g not possible on ia32).
+ * For all currently supported platforms by our implementation
+ * in <sys/isa_defs.h>, (i.e. sparc, sparcv9, ia32, ia64)
+ * type "double" is suitable for that intent.
+ *
+ * Note: Type "double" is chosen over the more obvious integer type int64_t.
+ *   int64_t is not a valid type for strict ANSI/ISO C compilation on ILP32.
+ */
+typedef double          sockaddr_maxalign_t;
+
+/*
+ * WinSock 2 extension -- new options
+ */
+#define SO_MAX_MSG_SIZE   0x2003    /* maximum message size */
+
+
+#define SO_NBIO                 0x0400  /* Nonblocking socket I/O operation */
+#define SO_ASYNC                0x0800  /* should send asyn notification of
+                                         * I/O events */
+#define SO_VRFTABLEID           0x1000  /* set VRF routing table id */
+#define SO_SRC_SPECIFIED        0x2000  /* Specified Source Address to be used */
+#define SO_STRICT_ADDR_BIND     0x4000  /* Accept only those packets that have
+                                         * been sent to the address that this
+                                         * socket is bound to */
+/*
+ * Used for getting random port for tls connect
+ */
+#define TCP_PORT_RETRY_CNT      5
+#define TCP_PORT_MASK           0xfff
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_stdio.c b/libs/sipcc/cpr/darwin/cpr_darwin_stdio.c
new file mode 100644 (file)
index 0000000..a708a61
--- /dev/null
@@ -0,0 +1,154 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "CSFLog.h"
+
+
+/**
+ * @def LOG_MAX
+ *
+ * Constant represents the maximum allowed length for a message
+ */
+#define LOG_MAX 1024
+
+
+/**
+ * @addtogroup DebugAPIs The CPR Logging Abstractions
+ * @ingroup CPR
+ * @brief The CPR Debug/Logging APIs
+ *
+ * @{
+ */
+
+/**
+ * Debug message
+ *
+ * @param _format  format string
+ * @param ...      variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+int
+buginf (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    va_list ap;
+    int rc;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+    if (rc <= 0) {
+        return rc;
+    }
+
+  CSFLogDebug("cpr", "%s", fmt_buf);
+
+  return rc;
+}
+
+/**
+ * Debug message that can be larger than #LOG_MAX
+ *
+ * @param str - a fixed constant string
+ *
+ * @return  zero(0)
+ *
+ * @pre (str not_eq NULL)
+ */
+int
+buginf_msg (const char *str)
+{
+    char buf[LOG_MAX + 1];
+    const char *p;
+    int16_t len;
+
+    // terminate buffer
+    buf[LOG_MAX] = NUL;
+
+    len = (int16_t) strlen(str);
+
+    if (len > LOG_MAX) {
+        p = str;
+        do {
+            memcpy(buf, p, LOG_MAX);
+            p += LOG_MAX;
+            len -= LOG_MAX;
+
+            printf("%s",buf);
+        } while (len > LOG_MAX);
+
+        if (len)
+        {
+          CSFLogDebug("cpr", "%s", (char *) p);
+        }
+    }
+    else
+    {
+      CSFLogDebug("cpr", "%s", (char *) str);
+
+    }
+
+    return 0;
+}
+
+/**
+ * Error message
+ *
+ * @param _format  format string
+ * @param ...     variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+void
+err_msg (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    va_list ap;
+    int rc;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+    if (rc <= 0) {
+        return;
+    }
+
+  CSFLogError("cpr", "%s", fmt_buf);
+}
+
+
+/**
+ * Notice message
+ *
+ * @param _format  format string
+ * @param ...     variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+void
+notice_msg (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    va_list ap;
+    int rc;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+    if (rc <= 0) {
+        return;
+    }
+
+  CSFLogInfo("cpr", "%s", fmt_buf);
+}
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_stdio.h b/libs/sipcc/cpr/darwin/cpr_darwin_stdio.h
new file mode 100644 (file)
index 0000000..d0a88e6
--- /dev/null
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_STDIO_H_
+#define _CPR_DARWIN_STDIO_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_string.c b/libs/sipcc/cpr/darwin/cpr_darwin_string.c
new file mode 100644 (file)
index 0000000..8b9a7c2
--- /dev/null
@@ -0,0 +1,174 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_strings.h"
+
+
+/**
+ * cpr_strdup
+ *
+ * @brief The CPR wrapper for strdup
+
+ * The cpr_strdup shall return a pointer to a new string, which is a duplicate
+ * of the string pointed to by "str" argument. A null pointer is returned if the
+ * new string cannot be created.
+ *
+ * @param[in] str  - The string that needs to be duplicated
+ *
+ * @return The duplicated string or NULL in case of no memory
+ *
+ */
+char *
+cpr_strdup (const char *str)
+{
+    char *dup;
+    size_t len;
+
+    if (!str) {
+        return (char *) NULL;
+    }
+
+    len = strlen(str);
+    if (len == 0) {
+        return (char *) NULL;
+    }
+    len++;
+
+    dup = cpr_malloc(len * sizeof(char));
+    if (!dup) {
+        return (char *) NULL;
+    }
+    (void) memcpy(dup, str, len);
+    return dup;
+}
+
+
+/**
+ * cpr_strcasecmp
+ *
+ * @brief The CPR wrapper for strcasecmp
+ *
+ * The cpr_strcasecmp performs case insensitive string comparison of the "s1"
+ * and the "s2" strings.
+ *
+ * @param[in] s1  - The first string
+ * @param[in] s2  - The second string
+ *
+ * @return integer <,=,> 0 depending on whether s1 is <,=,> s2
+ */
+#ifndef CPR_USE_OS_STRCASECMP
+int
+cpr_strcasecmp (const char *s1, const char *s2)
+{
+    const unsigned char *us1 = (const unsigned char *) s1;
+    const unsigned char *us2 = (const unsigned char *) s2;
+
+    /* No match if only one ptr is NULL */
+    if ((!s1 && s2) || (s1 && !s2)) {
+        /*
+         * If one of these is NULL it will be the lesser of the two
+         * values and therefore we'll get the proper sign in the int
+         */
+        return (int) (s1 - s2);
+    }
+
+    /* Match if both ptrs the same (e.g. NULL) */
+    if (s1 == s2)
+        return 0;
+
+    while (*us1 != '\0' && *us2 != '\0' && toupper(*us1) == toupper(*us2)) {
+        us1++;
+        us2++;
+    }
+
+    return (toupper(*us1) - toupper(*us2));
+}
+
+/**
+ * cpr_strncasecmp
+ *
+ * @brief The CPR wrapper for strncasecmp
+ *
+ * The cpr_strncasecmp performs case insensitive string comparison for specific
+ * length "len".
+ *
+ * @param[in] s1  - The first string
+ * @param[in] s2  - The second string
+ * @param[in] len  - The length to be compared
+ *
+ * @return integer <,=,> 0 depending on whether s1 is <,=,> s2
+ */
+int
+cpr_strncasecmp (const char *s1, const char *s2, size_t len)
+{
+    const unsigned char *us1 = (const unsigned char *) s1;
+    const unsigned char *us2 = (const unsigned char *) s2;
+
+    /* No match if only one ptr is NULL */
+    if ((!s1 && s2) || (s1 && !s2))
+        return ((int) (s1 - s2));
+
+    if ((len == 0) || (s1 == s2))
+        return 0;
+
+    while (len-- > 0 && toupper(*us1) == toupper(*us2)) {
+        if (len == 0 || *us1 == '\0' || *us2 == '\0')
+            break;
+        us1++;
+        us2++;
+    }
+
+    return (toupper(*us1) - toupper(*us2));
+}
+#endif
+
+/**
+ * strcasestr
+ *
+ * @brief The same as strstr, but ignores case
+ *
+ * The strcasestr performs the strstr function, but ignores the case.
+ * This function shall locate the first occurrence in the string
+ * pointed to by s1 of the sequence of bytes (excluding the terminating
+ * null byte) in the string pointed to by s2.
+ *
+ * @param[in] s1  - The input string
+ * @param[in] s2  - The pattern to be matched
+ *
+ * @return A pointer to the first occurrence of string s2 found
+ *           in string s1 or NULL if not found.  If s2 is an empty
+ *           string then s1 is returned.
+ */
+char *
+strcasestr (const char *s1, const char *s2)
+{
+    unsigned int i;
+
+    if (!s1)
+        return (char *) NULL;
+
+    if (!s2 || (s1 == s2) || (*s2 == '\0'))
+        return (char *) s1;
+
+    while (*s1) {
+        i = 0;
+        do {
+            if (s2[i] == '\0')
+                return (char *) s1;
+            if (s1[i] == '\0')
+                return (char *) NULL;
+            if (toupper(s1[i]) != toupper(s2[i]))
+                break;
+            i++;
+        } while (1);
+        s1++;
+    }
+
+    return (char *) NULL;
+}
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_string.h b/libs/sipcc/cpr/darwin/cpr_darwin_string.h
new file mode 100644 (file)
index 0000000..6e54592
--- /dev/null
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_STRING_H_
+#define _CPR_DARWIN_STRING_H_
+
+#include <string.h>
+#include <ctype.h>
+
+/**
+ * cpr_strdup
+ *
+ * @brief The CPR wrapper for strdup
+
+ * The cpr_strdup shall return a pointer to a new string, which is a duplicate
+ * of the string pointed to by "str" argument. A null pointer is returned if the
+ * new string cannot be created.
+ *
+ * @param[in] str  - The string that needs to be duplicated
+ *
+ * @return The duplicated string or NULL in case of no memory
+ *
+ */
+char *
+cpr_strdup(const char *str);
+
+/**
+ * strcasestr
+ *
+ * @brief The same as strstr, but ignores case
+ *
+ * The strcasestr performs the strstr function, but ignores the case.
+ * This function shall locate the first occurrence in the string
+ * pointed to by s1 of the sequence of bytes (excluding the terminating
+ * null byte) in the string pointed to by s2.
+ *
+ * @param[in] s1  - The input string
+ * @param[in] s2  - The pattern to be matched
+ *
+ * @return A pointer to the first occurrence of string s2 found
+ *           in string s1 or NULL if not found.  If s2 is an empty
+ *           string then s1 is returned.
+ */
+char *
+strcasestr(const char *s1, const char *s2);
+
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_strings.h b/libs/sipcc/cpr/darwin/cpr_darwin_strings.h
new file mode 100644 (file)
index 0000000..3dea7fd
--- /dev/null
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_STRINGS_H_
+#define _CPR_DARWIN_STRINGS_H_
+
+#include <strings.h>
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_threads.c b/libs/sipcc/cpr/darwin/cpr_darwin_threads.c
new file mode 100644 (file)
index 0000000..ac7ce8a
--- /dev/null
@@ -0,0 +1,200 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include <pthread.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#define LINUX_MIN_THREAD_PRIORITY (-20)        /* tbd: check MV linux: current val from Larry port */
+#define LINUX_MAX_THREAD_PRIORITY (+19)        /* tbd: check MV linux. current val from Larry port */
+
+
+/**
+ * cprCreateThread
+ *
+ * @brief Create a thread
+ *
+ *  The cprCreateThread function creates another execution thread within the
+ *  current process. If the input parameter "name" is present, then this is used
+ *  for debugging purposes. The startRoutine is the address of the function where
+ *  the thread execution begins. The start routine prototype is defined as
+ *  follows
+ *  @code
+ *     int32_t (*cprThreadStartRoutine)(void* data)
+ *  @endcode
+ *
+ * @param[in]  name         - name of the thread created (optional)
+ * @param[in]  startRoutine - function where thread execution begins
+ * @param[in]  stackSize    - size of the thread's stack
+ * @param[in]  priority     - thread's execution priority
+ * @param[in]  data         - parameter to pass to startRoutine
+ *
+ * Return Value: Thread handle or NULL if creation failed.
+ */
+cprThread_t
+cprCreateThread (const char *name,
+                 cprThreadStartRoutine startRoutine,
+                 uint16_t stackSize,
+                 uint16_t priority,
+                 void *data)
+{
+    static const char fname[] = "cprCreateThread";
+    static uint16_t id = 0;
+    cpr_thread_t *threadPtr;
+    pthread_t threadId;
+    pthread_attr_t attr;
+
+    CPR_INFO("%s: creating '%s' thread\n", fname, name);
+
+    /* Malloc memory for a new thread */
+    threadPtr = (cpr_thread_t *)cpr_malloc(sizeof(cpr_thread_t));
+    if (threadPtr != NULL) {
+        if (pthread_attr_init(&attr) != 0) {
+
+            CPR_ERROR("%s - Failed to init attribute for thread %s\n",
+                      fname, name);
+            cpr_free(threadPtr);
+            return (cprThread_t)NULL;
+        }
+
+        if (pthread_attr_setstacksize(&attr, stackSize) != 0) {
+            CPR_ERROR("%s - Invalid stacksize %d specified for thread %s\n",
+                      fname, stackSize, name);
+            cpr_free(threadPtr);
+            return (cprThread_t)NULL;
+        }
+
+        if (pthread_create(&threadId, &attr, startRoutine, data) != 0) {
+            CPR_ERROR("%s - Creation of thread %s failed: %d\n",
+                      fname, name, errno);
+            cpr_free(threadPtr);
+            return (cprThread_t)NULL;
+        }
+
+        /* Assign name to CPR if one was passed in */
+        if (name != NULL) {
+            threadPtr->name = name;
+        }
+
+        /*
+         * TODO - It would be nice for CPR to keep a linked
+         * list of running threads for debugging purposes
+         * such as a show command or walking the list to ensure
+         * that an application does not attempt to create
+         * the same thread twice.
+         */
+        threadPtr->u.handleInt = (uint64_t)threadId;
+        threadPtr->threadId = ++id;
+        return (cprThread_t)threadPtr;
+    }
+
+    /* Malloc failed */
+    CPR_ERROR("%s - Malloc for thread %s failed.\n", fname, name);
+    errno = ENOMEM;
+    return (cprThread_t)NULL;
+}
+
+
+/**
+ * cprDestroyThread
+ *
+ * @brief Destroys the thread passed in.
+ *
+ * The cprDestroyThread function is called to destroy a thread. The thread
+ * parameter may be any valid thread including the calling thread itself.
+ *
+ * @param[in] thread - thread to destroy.
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE. errno should be set for FAILURE case.
+ *
+ * @note In Linux there will never be a success indication as the
+ *       calling thread will have been terminated.
+ */
+cprRC_t
+cprDestroyThread (cprThread_t thread)
+{
+    static const char fname[] = "cprDestroyThread";
+    cpr_thread_t *cprThreadPtr;
+
+    cprThreadPtr = (cpr_thread_t *) thread;
+    if (cprThreadPtr != NULL) {
+        /*
+         * Make sure thread is trying to destroy itself.
+         */
+        if (cprThreadPtr->u.handlePtr == (void*) pthread_self()) {
+            cprThreadPtr->threadId = 0;
+            cpr_free(cprThreadPtr);
+            pthread_exit(NULL);
+            return CPR_SUCCESS;
+        }
+
+        CPR_ERROR("%s: Thread attempted to destroy another thread, not itself.\n",
+                  fname);
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+/**
+ * cprAdjustRelativeThreadPriority
+ *
+ * @brief The function sets the relative thread priority up or down by the given value.
+ *
+ * This function is used pSIPCC to set up the thread priority. The values of the
+ * priority range from -20 (Maximum priority) to +19 (Minimum priority).
+ *
+ * @param[in] relPri - nice value of the thread -20 is MAX and 19 is MIN
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprAdjustRelativeThreadPriority (int relPri)
+{
+    const char *fname = "cprAdjustRelativeThreadPriority";
+
+    if (setpriority(PRIO_PROCESS, 0, relPri) == -1) {
+        CPR_ERROR("%s: could not set the nice..err=%d\n",
+                  fname, errno);
+        return CPR_FAILURE;
+    }
+    return CPR_SUCCESS;
+}
+
+/**
+ * @}
+ * @addtogroup ThreadInternal Helper functions for implementing threads in CPR
+ * @ingroup Threads
+ * @brief Helper functions used by CPR for thread implementation
+ *
+ * @{
+ */
+
+/**
+ * cprGetThreadId
+ *
+ * @brief Return the pthread ID for the given CPR thread.
+ *
+ * @param[in] thread - thread to query
+ *
+ * @return Thread's Id or zero(0)
+ *
+ */
+pthread_t
+cprGetThreadId (cprThread_t thread)
+{
+    if (thread) {
+        return (pthread_t)(long) ((cpr_thread_t *)thread)->u.handleInt;
+    }
+    return 0;
+}
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_time.h b/libs/sipcc/cpr/darwin/cpr_darwin_time.h
new file mode 100644 (file)
index 0000000..39318e8
--- /dev/null
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_TIME_H_
+#define _CPR_DARWIN_TIME_H_
+
+#include <time.h>
+
+typedef size_t cpr_time_t;
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_timers.h b/libs/sipcc/cpr/darwin/cpr_darwin_timers.h
new file mode 100644 (file)
index 0000000..0c8eefc
--- /dev/null
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_TIMERS_H_
+#define _CPR_DARWIN_TIMERS_H_
+#include <pthread.h>
+
+/*
+ * Linux does not provide native support for non-blocking timers
+ * so CPR provides that functionality for Linux.
+ */
+
+/*
+ * Determine the granularity of the timers in
+ * milliseconds. ie how often does the TickThread
+ * wake up to decrement the timer intervals
+ */
+#define timerGranularity 10
+
+//struct timerBlk_s timerBlk;
+
+typedef struct cpr_timer_s
+{
+  const char *name;
+  uint32_t cprTimerId;
+  cprMsgQueue_t callBackMsgQueue;
+  uint16_t applicationTimerId;
+  uint16_t applicationMsgId;
+  void *data;
+  union {
+    void *handlePtr;
+  }u;
+}cpr_timer_t;
+
+/* Linked List of currently running timers */
+typedef struct timerDef
+{
+    int32_t duration;
+    boolean timerActive;
+    cpr_timer_t *cprTimerPtr;
+    struct timerDef *previous;
+    struct timerDef *next;
+} timerBlk;
+
+/* Timer mutex for updating the timer linked list */
+extern pthread_mutex_t timerMutex;
+
+/* Start routines for the timer threads */
+extern void *linuxTimerTick(void *);
+
+cprRC_t cpr_timer_pre_init(void);
+cprRC_t cpr_timer_de_init(void);
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_timers_using_select.c b/libs/sipcc/cpr/darwin/cpr_darwin_timers_using_select.c
new file mode 100644 (file)
index 0000000..4587307
--- /dev/null
@@ -0,0 +1,1309 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *
+ *  @file cpr_darwin_timers_using_select.c
+ *  @brief CPR layer for Timers.
+ *
+ *     This file contains the Cisco Portable Runtime layer for non-blocking
+ *     timers. This implementation is for the Linux operating system using
+ *     select with a timeout.
+ *
+ *     Timer Service runs in its own thread and blocks on select
+ *     call with a timeout. The value of timeout is set equal to
+ *     the duration of the earliest expiring timer. The timer list
+ *     is kept sorted by earliest to latest expiration times. Therefore,
+ *     timeout value is simply the duration on the head of the list.
+ *
+ *     For starting or cancelling a timer, the timer library contacts
+ *     the timer service using a local socket based IPC. This is done
+ *     by bringing up a connection between timer service and the client
+ *     during timer init.
+ *     Timer Service thread is the only thread that adds or removes
+ *     timer blocks from the list. When timer library client wants
+ *     to start or cancel a timer, the library sends an IPC message
+ *     to the timer service thread on the socket connection. This
+ *     unblocks the select call. Select also returns the amount of
+ *     time left on the timeout when it gets unblocked. This amount is
+ *     first subtracted from the head timer block duration. Then rest
+ *     of the processing is carried out based on whether the request
+ *     was to add or to remove a timer block and duration
+ *     on all timer blocks is adjusted as required.
+ *     When the select times out it implies the timers at the head
+ *     of the list has expired. The list is scanned for expired timers
+ *     and expiry processing such as posting message to the handler
+ *     etc. is done.
+ *
+ *     When there are no timers in the list, service blocks on select
+ *     forever waiting for at least one timer to be added.
+ *
+ */
+
+/**
+ * @defgroup Timers The Timer implementation module
+ * @ingroup CPR
+ * @brief The module related to Timer abstraction for the pSIPCC
+ * @addtogroup TimerAPIs The Timer APIs
+ * @ingroup Timers
+ * @brief APIs expected by pSIPCC for using Timers
+ *
+ */
+
+#include "cpr.h"
+#include "cpr_socket.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_threads.h"
+#include "cpr_timers.h"
+#include "cpr_string.h"
+#include "phntask.h"
+#include <errno.h>
+#include <unistd.h>
+#include "cpr_darwin_timers.h"
+#include "platform_api.h"
+
+/*--------------------------------------------------------------------------
+ * Local definitions
+ *--------------------------------------------------------------------------
+ */
+
+typedef struct timer_ipc_cmd_s
+{
+    void * timer_ptr;
+    void * user_data_ptr;
+    uint32_t duration;
+} timer_ipc_cmd_t;
+
+
+typedef struct timer_ipc_s
+{
+    uint32_t  msg_type;
+    union
+    {
+        timer_ipc_cmd_t cmd;
+        cprRC_t         result;
+    }u;
+} timer_ipc_t;
+
+#define TMR_CMD_ADD    1
+#define TMR_CMD_REMOVE 2
+#define TMR_RESULT     3
+
+#define API_RETURN(_val) \
+    {                     \
+        pthread_mutex_unlock(&api_mutex); \
+        return (_val); \
+     }\
+
+#define API_ENTER() \
+{\
+    pthread_mutex_lock(&api_mutex);\
+}\
+
+/* for AF_LOCAL not all implementations return client addr in recvfrom so
+ * using explicit path for client too.
+ */
+#define SERVER_PATH "/tmp/CprTmrServer"
+#define CLIENT_PATH "/tmp/CprTmrClient"
+
+
+/*--------------------------------------------------------------------------
+ * Global data
+ *--------------------------------------------------------------------------
+ */
+
+
+static timerBlk *timerListHead;
+
+static pthread_t timerThreadId;
+
+
+static pthread_mutex_t api_mutex;
+
+
+/* local socket used by timer library */
+static int client_sock = INVALID_SOCKET;
+
+
+/* local socket used by the timer  service */
+static int serv_sock = INVALID_SOCKET;
+
+static struct sockaddr_un tmr_serv_addr;
+static struct sockaddr_un tmr_client_addr;
+
+static fd_set socks; /* descriptor set */
+
+
+/*--------------------------------------------------------------------------
+ * External data references
+ *--------------------------------------------------------------------------
+ */
+
+
+/*--------------------------------------------------------------------------
+ * External function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+/*
+ * Internal CPR function to fill in data in the sysheader.
+ * This is to prevent knowledge of the evil syshdr structure
+ * from spreading to cpr_darwin_timers.c This thing is
+ * like kudzu...
+ */
+extern void fillInSysHeader(void *buffer, uint16_t cmd, uint16_t len,
+                            void *timerMsg);
+
+
+
+/*--------------------------------------------------------------------------
+ * Local scope function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+static void     *timerThread(void *data);
+static cprRC_t  start_timer_service_loop();
+static void     process_expired_timers();
+static void     send_api_result(cprRC_t result, struct sockaddr_un *addr, socklen_t len);
+
+
+/**
+ * @addtogroup TimerAPIs The Timer APIs
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * cprSleep
+ *
+ * @brief Suspend the calling thread
+ * The cprSleep function blocks the calling thread for the indicated number of
+ * milliseconds.
+ *
+ * @param[in] duration - Number of milliseconds the thread should sleep
+ *
+ * @return -  none
+ */
+void
+cprSleep (uint32_t duration)
+{
+    /*
+     * usleep() can only support up to one second, so split
+     * between sleep and usleep if one second or more
+     */
+    if (duration >= 1000) {
+        (void) sleep(duration / 1000);
+        (void) usleep((duration % 1000) * 1000);
+    } else {
+        (void) usleep(duration * 1000);
+    }
+}
+
+/**
+  * @}
+  */
+
+/**
+ * @defgroup TimerInternal The Timer internal functions
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * addTimerToList
+ * Send message to timer service to add the timer pointed by cprTimerPtr
+ * to the list. This routine is just sending IPC message to timer service
+ * but the actual addition is done by timer service using the addTimer function.
+ * This function is only called by CPR functions and is not visible to external
+ * applications.
+ * @param[in] cprTimerPtr  - timer pointer
+ * @param[in] duration     - timer duration in msec.
+ * @param[in] data         - opaque data
+ * @return  - CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t addTimerToList (cpr_timer_t *cprTimerPtr, uint32_t duration, void *data)
+{
+    // TODO(ekr@rtfm.com): Put this back in when you figure out why it causes crashes
+    return CPR_SUCCESS;
+
+#if 0
+    static const char fname[] = "addTimerToList";
+    timer_ipc_t tmr_cmd = {0};
+    timer_ipc_t tmr_rsp={0};
+
+
+    API_ENTER();
+
+    CPR_INFO("%s: cprTimerptr=0x%x dur=%d user_data=%p\n",
+             fname, cprTimerPtr, duration, data);
+    tmr_cmd.msg_type = TMR_CMD_ADD;
+    tmr_cmd.u.cmd.timer_ptr = cprTimerPtr;
+    tmr_cmd.u.cmd.user_data_ptr = data;
+    tmr_cmd.u.cmd.duration = duration;
+
+//CPR_INFO("%s:sending messge of type=%d\n", fname, tmr_cmd.msg_type);
+    /* simply post a request here to the timer service.*/
+    if (client_sock != -1) {
+        if (sendto(client_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
+                   (struct sockaddr *)&tmr_serv_addr, sizeof(tmr_serv_addr)) < 0) {
+            CPR_ERROR("Failed to tx IPC msg to timer service, errno = %s %s\n",
+                   strerror(errno), fname);
+            API_RETURN(CPR_FAILURE);
+        }
+
+    } else {
+        CPR_ERROR("can not make IPC connection, client_sock is invalid %s\n", fname);
+        API_RETURN(CPR_FAILURE);
+    }
+
+    /*
+     * wait for the timer service to excute the request
+     * so that we get result of operation
+     */
+
+    if (recvfrom(client_sock, &tmr_rsp, sizeof(timer_ipc_t),0, NULL, NULL) < 0) {
+        //CPR_INFO("error in recving the result error=%s\n", strerror(errno));
+        API_RETURN(CPR_FAILURE);
+    } else {
+        //CPR_INFO("received response from the timer result=%d\n", tmr_rsp.u.result);
+        API_RETURN(tmr_rsp.u.result);
+    }
+#endif
+}
+
+
+/**
+ * addTimer
+ *
+ * Add a timer to the timer linked list.
+ * This function is only called by CPR functions and is not visible to external
+ * applications.
+ *
+ * @param[in] cprTimerPtr - pointer to the CPR timer structure
+ * @param[in] duration    - how long before timer expires in milliseconds
+ * @param[in] data        - information to be passed to callback function
+ *
+ * @return  - CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t addTimer (cpr_timer_t *cprTimerPtr, uint32_t duration, void *data)
+{
+    static const char fname[] = "addTimer";
+    timerBlk *timerList;
+    timerBlk *newTimerPtr;
+
+    CPR_INFO("%s:adding timer=0x%p timerblk=%p\n", fname,
+           cprTimerPtr, cprTimerPtr->u.handlePtr);
+
+
+    /* Verify the timer has been initialized */
+    newTimerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+    if (newTimerPtr == NULL) {
+        CPR_ERROR("%s - Timer %s has not been initialized.\n",
+                  fname, cprTimerPtr->name);
+        errno = EINVAL;
+
+        return(CPR_FAILURE);
+    }
+
+    /* Ensure this timer is not already running */
+    if (newTimerPtr->timerActive) {
+        CPR_ERROR("%s - Timer %s is already active.\n", fname, cprTimerPtr->name);
+        errno = EAGAIN;
+        return(CPR_FAILURE);
+
+    }
+
+    /* Sanity tests passed, store the data the application passed in */
+    newTimerPtr->duration = duration;
+    cprTimerPtr->data = data;
+
+    /*
+     * Insert timer into the linked list. The timer code only
+     * decrements the first timer in the list to be efficient.
+     * Therefore, the timer at the top of the list is the timer
+     * that will expire first. Timers are added to the list
+     * in ascending order of time left before expiration and
+     * ticksLeft is calculated to be the difference between
+     * when the timer before them expires and when the newly
+     * inserted timer expires.
+     */
+
+    /* Check for insertion into an empty list */
+    if (timerListHead == NULL) {
+        //CPR_INFO("no timer in the list case..\n");
+        timerListHead = newTimerPtr;
+    } else {
+
+       /* Insert timer into list */
+        timerList = timerListHead;
+        while (timerList != NULL) {
+
+            /*
+             * If the duration on this new timer are less than the
+             * timer in the list, insert this new timer before
+             * it in the list as it will expire first. In doing so
+             * the code must subtract the new timer's duration from
+             * the duration of the timer in the in the list to keep the deltas correct.
+             */
+            if (newTimerPtr->duration < timerList->duration) {
+                //CPR_INFO("less than case..\n");
+                timerList->duration -= newTimerPtr->duration;
+                newTimerPtr->next = timerList;
+                newTimerPtr->previous = timerList->previous;
+                if (newTimerPtr->previous) {
+                    newTimerPtr->previous->next = newTimerPtr;
+                }
+                timerList->previous = newTimerPtr;
+
+                /* Check for insertion at the head of list */
+                if (timerListHead == timerList) {
+                    //CPR_INFO("insert at the head case..\n");
+                    timerListHead = newTimerPtr;
+                }
+                break;
+            } else {
+                /*
+                 * Else this new timer expires after the timer in
+                 * the list. Therefore subtract the timer's duration
+                 * from the new timer's duration. Since only the
+                 * first timer is decremented all other timers added
+                 * expiration must be calculated as a delta from the
+                 * timer in front of them in the list.
+                 */
+                //CPR_INFO("greater than case..\n");
+                newTimerPtr->duration -= timerList->duration;
+
+                /* Check for insertion at the end of list */
+                if (timerList->next == NULL) {
+                    //CPR_INFO("insert at the end sub case..\n");
+                    newTimerPtr->previous = timerList;
+                    timerList->next = newTimerPtr;
+                    newTimerPtr->next = NULL;
+                    break;
+                }
+                timerList = timerList->next;
+            }
+        }
+    }
+
+    newTimerPtr->timerActive = TRUE;
+    return(CPR_SUCCESS);
+}
+
+
+/**
+ * removeTimerFromList
+ * Send message to timer service to remove the timer pointed by cprTimerPtr
+ * from the list. This routine is just sending IPC message to timer service
+ * and the actual removal is done by timer service using the removeTimer function..
+ * This function is only called by CPR functions and is not visible to external
+ * applications.
+ *
+ * @param[in] cprTimerPtr - pointer to the timer to be removed from the list
+ * @return - CPR_SUCCESS or CPR_FAILURE
+ *
+ */
+static cprRC_t
+removeTimerFromList (cpr_timer_t *cprTimerPtr)
+{
+
+    static const char fname[] = "removeTimerFromList";
+    timer_ipc_t tmr_cmd = {0};
+    timer_ipc_t tmr_rsp = {0};
+
+
+    API_ENTER();
+
+    //CPR_INFO("%s:remove timer from list=0x%x\n",fname, cprTimerPtr);
+    tmr_cmd.msg_type = TMR_CMD_REMOVE;
+    tmr_cmd.u.cmd.timer_ptr = cprTimerPtr;
+
+    //CPR_INFO("sending messge of type=%d\n", tmr_cmd.msg_type);
+
+    /* simply post a request here to the timer service.. */
+    if (client_sock != -1) {
+        if (sendto(client_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
+                   (struct sockaddr *)&tmr_serv_addr, sizeof(tmr_serv_addr)) < 0) {
+            CPR_ERROR("%s:failed to tx IPC Msg to timer service, errno = %s\n",
+                      fname, strerror(errno));
+            API_RETURN(CPR_FAILURE);
+        }
+    } else {
+        CPR_ERROR("%s:client_sock invalid, no IPC connection \n", fname);
+        API_RETURN(CPR_FAILURE);
+    }
+
+    /*
+     * wait for the timer service to excute the request
+     * so that we get result of operation
+     */
+
+    if (recvfrom(client_sock, &tmr_rsp, sizeof(timer_ipc_t),0, NULL, NULL) < 0) {
+        //CPR_INFO("error in recving the result error=%s\n", strerror(errno));
+        API_RETURN(CPR_FAILURE);
+    } else {
+        //CPR_INFO("received response from the timer result=%d\n", tmr_rsp.u.result);
+        API_RETURN(tmr_rsp.u.result);
+    }
+}
+
+
+/**
+ * removeTimer
+ *
+ * Remove a timer from the timer linked list. This function is only
+ * called by CPR functions and is not visible to applications.
+ *
+ * @param[in] cprTimerPtr - which timer to cancel
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t
+removeTimer (cpr_timer_t *cprTimerPtr)
+{
+    static const char fname[] = "removeTimer";
+    timerBlk *timerList;
+    timerBlk *previousTimer;
+    timerBlk *nextTimer;
+    timerBlk *timerPtr;
+
+    //CPR_INFO("removing timer..0x%x\n", cprTimerPtr);
+
+    /*
+     * No need to sanitize the cprTimerPtr data as only
+     * internal CPR functions call us and they have already
+     * sanitized that data. In addition those functions
+     * have already grabbed the timer list mutex so no need
+     * to do that here.
+     */
+    timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+    //CPR_INFO("%s: timer ptr=%x\n", fname, timerPtr);
+
+    if (timerPtr != NULL) {
+        /* Walk the list looking for this timer to cancel. */
+        timerList = timerListHead;
+        while (timerList != NULL) {
+            if (timerList->cprTimerPtr->cprTimerId == cprTimerPtr->cprTimerId) {
+                /* Removing only element in the list */
+                if ((timerList->previous == NULL) &&
+                    (timerList->next == NULL)) {
+                    timerListHead = NULL;
+
+                /* Removing head of the list */
+                } else if (timerList->previous == NULL) {
+                    nextTimer = timerList->next;
+                    nextTimer->previous = NULL;
+                    timerListHead = nextTimer;
+
+                /* Removing tail of the list */
+                } else if (timerList->next == NULL) {
+                    previousTimer = timerList->previous;
+                    previousTimer->next = NULL;
+
+                /* Removing from middle of the list */
+                } else {
+                    nextTimer = timerList->next;
+                    previousTimer = timerList->previous;
+                    previousTimer->next = nextTimer;
+                    nextTimer->previous = previousTimer;
+                }
+
+                /* Add time back to next timer in the list */
+                if (timerList->next) {
+                    timerList->next->duration += timerList->duration;
+                }
+
+                /*
+                 * Reset timer values
+                 */
+                timerList->next = NULL;
+                timerList->previous = NULL;
+                timerList->duration = -1;
+                timerList->timerActive = FALSE;
+                cprTimerPtr->data = NULL;
+
+                return(CPR_SUCCESS);
+
+            }
+
+            /* Walk the list */
+            timerList = timerList->next;
+        }
+
+        /*
+         * Either the timer was not active or it was marked active, but not
+         * found on the timer list. If the timer was inactive then that is OK,
+         * but let user know about an active timer not found in the timer list.
+         */
+        if ((timerPtr->next != NULL) || (timerPtr->previous != NULL)) {
+            CPR_ERROR("%s - Timer %s marked as active, "
+                      "but was not found on the timer list.\n",
+                      fname, cprTimerPtr->name);
+            timerPtr->next = NULL;
+            timerPtr->previous = NULL;
+        }
+        timerPtr->duration = -1;
+        timerPtr->cprTimerPtr->data = NULL;
+        timerPtr->timerActive = FALSE;
+
+        return(CPR_SUCCESS);
+
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - Timer not initialized.\n", fname);
+    errno = EINVAL;
+    return(CPR_FAILURE);
+
+}
+
+/**
+ * @}
+ * @addtogroup TimerAPIs The Timer APIs
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * cprCreateTimer
+ *
+ * @brief Initialize a timer
+ *
+ * The cprCreateTimer function is called to allow the OS to perform whatever
+ * work is needed to create a timer. The input name parameter is optional. If present, CPR assigns
+ * this name to the timer to assist in debugging. The callbackMsgQueue is the
+ * address of a message queue created with cprCreateMsgQueue. This is the
+ * queue where the timer expire message will be sent.
+ * So, when this timer expires a msg of type "applicationMsgId" will be sent to the msg queue
+ * "callbackMsgQueue" indicating that timer applicationTimerId has expired.
+ *
+ * @param[in]   name               -  name of the timer
+ * @param[in]   applicationTimerId - ID for this timer from the application's
+ *                                  perspective
+ * @param[in]   applicationMsgId   - ID for syshdr->cmd when timer expire msg
+ *                                  is sent
+ * @param[in]   callBackMsgQueue   - where to send a msg when this timer expires
+ *
+ * @return  Timer handle or NULL if creation failed.
+ */
+cprTimer_t
+cprCreateTimer (const char *name,
+                uint16_t applicationTimerId,
+                uint16_t applicationMsgId,
+                cprMsgQueue_t callBackMsgQueue)
+{
+    static const char fname[] = "cprCreateTimer";
+    static uint32_t cprTimerId = 0;
+    cpr_timer_t *cprTimerPtr;
+    timerBlk *timerPtr;
+
+    /*
+     * Malloc memory for a new timer. Need to
+     * malloc memory for the generic CPR view and
+     * one for the CNU specific version.
+     */
+    cprTimerPtr = (cpr_timer_t *) cpr_malloc(sizeof(cpr_timer_t));
+    timerPtr = (timerBlk *) cpr_malloc(sizeof(timerBlk));
+    if ((cprTimerPtr != NULL) && (timerPtr != NULL)) {
+        /* Assign name (Optional) */
+        cprTimerPtr->name = name;
+
+        /* Set timer ids, msg id and callback msg queue (Mandatory) */
+        cprTimerPtr->applicationTimerId = applicationTimerId;
+        cprTimerPtr->applicationMsgId = applicationMsgId;
+        cprTimerPtr->cprTimerId = cprTimerId++;
+        if (callBackMsgQueue == NULL) {
+            CPR_ERROR("%s - Callback msg queue for timer %s is NULL.\n",
+                      fname, name);
+            cpr_free(timerPtr);
+            cpr_free(cprTimerPtr);
+            return NULL;
+        }
+        cprTimerPtr->callBackMsgQueue = callBackMsgQueue;
+
+        /*
+         * Set remaining values in both structures to defaults
+         */
+        timerPtr->next = NULL;
+        timerPtr->previous = NULL;
+        timerPtr->duration = -1;
+        timerPtr->timerActive = FALSE;
+        cprTimerPtr->data = NULL;
+
+        /*
+         * TODO - It would be nice for CPR to keep a linked
+         * list of active timers for debugging purposes
+         * such as a show command or walking the list to ensure
+         * that an application does not attempt to create
+         * the same timer twice.
+         *
+         * TODO - It would be nice to initialize of pool of
+         * timers at init time and have this function just
+         * return a timer from the pool. Then when the
+         * timer expired or cancel the code would not free
+         * it, but just return it to the pool.
+         */
+        timerPtr->cprTimerPtr = cprTimerPtr;
+        cprTimerPtr->u.handlePtr = timerPtr;
+        //CPR_INFO("cprTimerCreate: timer_t=%x blk=%x\n",cprTimerPtr, timerPtr);
+
+        return cprTimerPtr;
+    }
+
+    /*
+     * If we get here there has been a malloc failure.
+     */
+    if (timerPtr) {
+        cpr_free(timerPtr);
+    }
+    if (cprTimerPtr) {
+        cpr_free(cprTimerPtr);
+    }
+
+    /* Malloc failed */
+    CPR_ERROR("%s - Malloc for timer %s failed.\n", fname, name);
+    errno = ENOMEM;
+    return NULL;
+}
+
+
+/**
+ * cprStartTimer
+ *
+ * @brief Start a system timer
+ *
+ * The cprStartTimer function starts a previously created timer referenced by
+ * the parameter timer. CPR timer granularity is 10ms. The "timer" input
+ * parameter is the handle returned from a previous successful call to
+ * cprCreateTimer.
+ *
+ * @param[in]  timer    - which timer to start
+ * @param[in]  duration - how long before timer expires in milliseconds
+ * @param[in]  data     - information to be passed to callback function
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprStartTimer (cprTimer_t timer,
+               uint32_t duration,
+               void *data)
+{
+    static const char fname[] = "cprStartTimer";
+    cpr_timer_t *cprTimerPtr;
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        /* add timer to the list */
+        return addTimerToList(cprTimerPtr, duration, data);
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprIsTimerRunning
+ *
+ * @brief Determine if a timer is active
+ *
+ * This function determines whether the passed in timer is currently active. The
+ * "timer" parameter is the handle returned from a previous successful call to
+ *  cprCreateTimer.
+ *
+ * @param[in] timer - which timer to check
+ *
+ * @return True is timer is active, False otherwise
+ */
+boolean
+cprIsTimerRunning (cprTimer_t timer)
+{
+    static const char fname[] = "cprIsTimerRunning";
+    cpr_timer_t *cprTimerPtr;
+    timerBlk *timerPtr;
+
+    //CPR_INFO("istimerrunning(): timer=0x%x\n", timer);
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+        if (timerPtr == NULL) {
+            CPR_ERROR("%s - Timer %s has not been initialized.\n",
+                      fname, cprTimerPtr->name);
+            errno = EINVAL;
+            return FALSE;
+        }
+
+        if (timerPtr->timerActive) {
+            return TRUE;
+        }
+    } else {
+        /* Bad application! */
+        CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+        errno = EINVAL;
+    }
+
+    return FALSE;
+}
+
+
+/**
+ * cprCancelTimer
+ *
+ * @brief Cancels a running timer
+ *
+ * The cprCancelTimer function cancels a previously started timer referenced by
+ * the parameter timer.
+ *
+ * @param[in] timer - which timer to cancel
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprCancelTimer (cprTimer_t timer)
+{
+    static const char fname[] = "cprCancelTimer";
+    timerBlk *timerPtr;
+    cpr_timer_t *cprTimerPtr;
+    cprRC_t rc = CPR_SUCCESS;
+
+    //CPR_INFO("cprCancelTimer: timer ptr=%x\n", timer);
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+        if (timerPtr == NULL) {
+            CPR_ERROR("%s - Timer %s has not been initialized.\n",
+                      fname, cprTimerPtr->name);
+            errno = EINVAL;
+            return CPR_FAILURE;
+        }
+
+        /*
+         * Ensure timer is active before trying to remove it.
+         * If already inactive then just return SUCCESS.
+         */
+        if (timerPtr->timerActive) {
+            //CPR_INFO("removing timer from the list=%x\n", timerPtr);
+            rc = removeTimerFromList(timer);
+        }
+        return rc;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprUpdateTimer
+ *
+ * @brief Updates the expiration time for a running timer
+ *
+ * The cprUpdateTimer function cancels a previously started timer referenced by
+ * the parameter timer and then restarts the same timer with the duration passed
+ * in.
+ *
+ * @param[in]   timer    - which timer to update
+ * @param[in]   duration - how long before timer expires in milliseconds
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprUpdateTimer (cprTimer_t timer, uint32_t duration)
+{
+    static const char fname[] = "cprUpdateTimer";
+    cpr_timer_t *cprTimerPtr;
+    void *timerData;
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        /* Grab data before cancelling timer */
+        timerData = cprTimerPtr->data;
+    } else {
+        CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    if (cprCancelTimer(timer) == CPR_SUCCESS) {
+        if (cprStartTimer(timer, duration, timerData) == CPR_SUCCESS) {
+            return CPR_SUCCESS;
+        } else {
+            CPR_ERROR("%s - Failed to start timer %s\n",
+                      fname, cprTimerPtr->name);
+            return CPR_FAILURE;
+        }
+    }
+
+    CPR_ERROR("%s - Failed to cancel timer %s\n", fname, cprTimerPtr->name);
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprDestroyTimer
+ *
+ * @brief Destroys a timer.
+ *
+ * This function will cancel the timer and then destroy it. It sets
+ * all links to NULL and then frees the timer block.
+ *
+ * @param[in] timer - which timer to destroy
+ *
+ * @return  CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprDestroyTimer (cprTimer_t timer)
+{
+    static const char fname[] = "cprDestroyTimer";
+    cpr_timer_t *cprTimerPtr;
+    cprRC_t rc;
+
+    //CPR_INFO("cprDestroyTimer:destroying timer=%x\n", timer);
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        rc = cprCancelTimer(timer);
+        if (rc == CPR_SUCCESS) {
+            cprTimerPtr->cprTimerId = 0;
+            cpr_free(cprTimerPtr->u.handlePtr);
+            cpr_free(cprTimerPtr);
+            return CPR_SUCCESS;
+        } else {
+            CPR_ERROR("%s - Cancel of Timer %s failed.\n",
+                      fname, cprTimerPtr->name);
+            return CPR_FAILURE;
+        }
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+/**
+ * @}
+ * @addtogroup TimerInternal The Timer internal functions
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * cpr_timer_pre_init
+ *
+ * @brief Initalize timer service and client IPC
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cpr_timer_pre_init (void)
+{
+    static const char fname[] = "cpr_timer_pre_init";
+    int32_t returnCode;
+
+    /* start the timer service first */
+    returnCode = (int32_t)pthread_create(&timerThreadId, NULL, timerThread, NULL);
+    if (returnCode == -1) {
+        CPR_ERROR("%s: Failed to create Timer Thread : %s\n", fname, strerror(errno));
+        return CPR_FAILURE;
+    }
+
+    /*
+     * wait some time so that timer service thread is up
+     * TBD:we should really implement wait on timerthread using condvar.
+     */
+    cprSleep(1000);
+
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * cpr_timer_de_init
+ *
+ * @brief De-Initalize timer service and client IPC
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cpr_timer_de_init(void)
+{
+    // close all sockets..
+    close(client_sock);
+    close(serv_sock);
+
+
+    // destroy api mutex
+    pthread_mutex_destroy(&api_mutex);
+
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * Timer service thread
+ *
+ */
+/**
+ * timerThread
+ *
+ * @brief Timer service thread
+ *
+ * This is the start function for the timer server thread.
+ *
+ * @param[in] data - The data passed in (UNUSED)
+ *
+ * @return  This function eventually starts an infinite loop on a "select".
+ */
+void *timerThread (void *data)
+{
+    static const char fname[] = "timerThread";
+
+    //CPR_INFO("timerThread:started..\n");
+#ifndef HOST
+#ifndef PTHREAD_SET_NAME
+#define PTHREAD_SET_NAME(s)    do { } while (0)
+#endif
+    PTHREAD_SET_NAME("CPR Timertask");
+#endif
+
+    /*
+     * Increase the timer thread priority from default priority.
+     * This is required to make sure timers fire with reasonable precision.
+     *
+     * NOTE: always make sure the priority is higher than sip/gsm threads;
+     * otherwise, we must use mutex around the following while loop.
+     */
+    (void) cprAdjustRelativeThreadPriority(TIMER_THREAD_RELATIVE_PRIORITY);
+
+    /* get ready to listen for timer commands and service them */
+    if (start_timer_service_loop() == CPR_FAILURE) {
+        CPR_ERROR("%s: timer service loop failed\n", fname);
+    }
+
+    return NULL;
+}
+
+
+/**
+ * local_bind
+ * Function used to do a bind on the local socket.
+ *
+ * @param[in]  sock  - socket descriptor to bind
+ * @param[in]  name  - name to use for binding local socket
+ * @return 0 if success, -1 on error (errno will be set)
+ *
+ */
+static int local_bind (int sock, char *name)
+{
+    struct sockaddr_un addr;
+
+    /* construct the address structure */
+    addr.sun_family = AF_UNIX;
+    sstrncpy(addr.sun_path, name, sizeof(addr.sun_path));
+    /* make sure file doesn't already exist */
+    unlink(addr.sun_path);
+
+    return bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+}
+
+/**
+ * select_sockets
+ *
+ * Set the socket descriptors to be used for reading.
+ * Only server side uses select.
+ *
+ * @return The server socket number
+ */
+static int select_sockets (void)
+{
+    FD_ZERO(&socks);
+
+    FD_SET(serv_sock, &socks);
+
+    return (serv_sock);
+}
+
+
+/**
+ * read_timer_cmd
+ * read message received on the IPC from the client
+ * the only messages are timer commands {add, remove}
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t read_timer_cmd ()
+{
+    static const char fname[] = "read_timer_cmd";
+    int  rcvlen;
+    timer_ipc_t tmr_cmd ={0};
+    cprRC_t ret = CPR_FAILURE;
+
+
+
+    rcvlen =recvfrom(serv_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
+                     NULL, NULL);
+
+    if (rcvlen > 0) {
+        //CPR_INFO("got message type=%d\n", tmr_cmd.msg_type);
+        switch(tmr_cmd.msg_type) {
+       case TMR_CMD_ADD:
+            //CPR_INFO("request to add timer ptr=%x duration=%d datptr=%x\n",
+            //       tmr_cmd.u.cmd.timer_ptr, tmr_cmd.u.cmd.duration, tmr_cmd.u.cmd.user_data_ptr);
+
+            ret = addTimer((cpr_timer_t *)tmr_cmd.u.cmd.timer_ptr,tmr_cmd.u.cmd.duration,
+              tmr_cmd.u.cmd.user_data_ptr);
+
+            break;
+
+       case TMR_CMD_REMOVE:
+            //CPR_INFO("request to remove timer ptr=%x\n", tmr_cmd.u.cmd.timer_ptr);
+            ret = removeTimer((cpr_timer_t *)tmr_cmd.u.cmd.timer_ptr);
+            break;
+
+        default:
+            CPR_ERROR("%s:invalid ipc command = %d\n", tmr_cmd.msg_type);
+            ret = CPR_FAILURE;
+            break;
+        }
+    } else {
+        CPR_ERROR("%s:while reading serv_sock err =%s: Closing Socket..Timers not operational !!! \n",
+                  fname, strerror(errno));
+        (void) close(serv_sock);
+        serv_sock = INVALID_SOCKET;
+        ret = CPR_FAILURE;
+    }
+
+    /* send the result back */
+    send_api_result(ret, &tmr_client_addr, sizeof(tmr_client_addr));
+
+    return (ret);
+
+}
+
+/**
+ * send_api_result back to client via a socket sendto operation
+ * @param[in] retVal - value of result
+ * @param[in] addr   - address to send the result to
+ * @param[in] len    - length of addr
+ */
+void send_api_result(cprRC_t retVal, struct sockaddr_un *addr, socklen_t len)
+{
+    static const char fname[] = "send_api_result";
+    timer_ipc_t tmr_rsp = {0};
+
+    tmr_rsp.msg_type = TMR_RESULT;
+    tmr_rsp.u.result = retVal;
+    if (sendto(serv_sock, &tmr_rsp, sizeof(timer_ipc_t),0, (struct sockaddr *)addr, len) < 0) {
+        CPR_ERROR("%s: error in sending on serv_sock err=%s\n", fname, strerror(errno));
+    }
+}
+
+/**
+ * Start the timer service loop.
+ * Service loop waits for timer commands from timer clients processes them.
+ * Waiting is done by issuing a select call with a timeout. This is the main
+ * function that implements the timer functionality using the select call.
+ * timeout value = duration on the head of the timer list.
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t start_timer_service_loop (void)
+{
+    static const char fname[] = "start_timer_service_loop";
+    int lsock = -1;
+    struct timeval tv;
+    int ret;
+    boolean use_timeout;
+
+
+    /* initialize server and client addresses used for sending.*/
+    memset(&tmr_serv_addr, 0, sizeof(tmr_serv_addr));
+    tmr_serv_addr.sun_family = AF_UNIX;
+    snprintf(tmr_serv_addr.sun_path, sizeof(tmr_serv_addr.sun_path), "%s_%d",SERVER_PATH, getpid());
+
+    memset(&tmr_client_addr, 0, sizeof(tmr_client_addr));
+    tmr_client_addr.sun_family = AF_UNIX;
+    snprintf(tmr_client_addr.sun_path, sizeof(tmr_client_addr.sun_path), "%s_%d",CLIENT_PATH, getpid());
+
+    /*
+     * init mutex and cond var.
+     * these are used for making API synchronous etc..
+     */
+    if (pthread_mutex_init(&api_mutex, NULL) != 0) {
+        CPR_ERROR("%s: failed to initialize api_mutex err=%s\n", fname,
+                  strerror(errno));
+        return CPR_FAILURE;
+    }
+
+
+    /* open a unix datagram socket for client library */
+    client_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+    if (client_sock == INVALID_SOCKET) {
+        CPR_ERROR("%s:could not create client socket error=%s\n", fname, strerror(errno));
+        return CPR_FAILURE;
+    }
+
+    /* bind service name to the socket */
+    if (local_bind(client_sock,tmr_client_addr.sun_path) < 0) {
+        CPR_ERROR("%s:could not bind local socket:error=%s\n", fname, strerror(errno));
+        (void) close(client_sock);
+        client_sock = INVALID_SOCKET;
+        return CPR_FAILURE;
+    }
+
+    /* open another unix datagram socket for timer service */
+    serv_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+    if (serv_sock == INVALID_SOCKET) {
+        CPR_ERROR("%s:could not create server socket error=%s\n", fname, strerror(errno));
+        serv_sock = INVALID_SOCKET;
+        close(client_sock);
+        client_sock = INVALID_SOCKET;
+        return CPR_FAILURE;
+    }
+
+    if (local_bind(serv_sock, tmr_serv_addr.sun_path) < 0) {
+        CPR_ERROR("%s:could not bind serv socket:error=%s\n", fname, strerror(errno));
+        (void) close(serv_sock);
+        (void) close(client_sock);
+        client_sock = serv_sock = INVALID_SOCKET;
+        return CPR_FAILURE;
+    }
+
+
+    while (1) {
+
+        lsock = select_sockets();
+
+       /* set the timer equal to duration on head */
+       if (timerListHead != NULL) {
+            tv.tv_sec = (timerListHead->duration)/1000;
+            tv.tv_usec = (timerListHead->duration%1000)*1000;
+            //CPR_INFO("%s:time duration on head =%d sec:%d usec (or %d msec)\n",
+            //       fname, tv.tv_sec, tv.tv_usec,
+            //       timerListHead->duration);
+            use_timeout = TRUE;
+       } else {
+            //CPR_INFO("%s:no timer in the list.. will block until there is one\n",
+            //       fname);
+            use_timeout = FALSE;
+       }
+
+        ret = select(lsock + 1, &socks, NULL, NULL, (use_timeout == TRUE) ? &tv:NULL);
+
+        if (ret == -1) {
+            CPR_ERROR("%s:error in select err=%s\n", fname,
+                      strerror(errno));
+            return(CPR_FAILURE);
+        } else if (ret == 0) {
+            /*
+             * this means the head timer has expired..there could be others
+             */
+            timerListHead->duration = 0;
+            process_expired_timers();
+        } else {
+
+            if (FD_ISSET(serv_sock, &socks)) {
+                //CPR_INFO("Got something on serv_sock..\n");
+                /* first reduce the duration of the head by current run time */
+                if (timerListHead != NULL) {
+                    //CPR_INFO("set head duration to =%d prev was= %d\n",
+                    //       tv.tv_sec * 1000 + (tv.tv_usec/1000),
+                    //       timerListHead->duration);
+                    /* set the head with the remaining duration(tv) as indicated by select */
+                    timerListHead->duration = tv.tv_sec * 1000 + (tv.tv_usec/1000);
+                }
+                /* read the ipc message to remove or add a timer */
+                (void) read_timer_cmd();
+            }
+       }
+    }
+
+}
+
+/**
+ *
+ * Process the timers expired. Generally this is called when head timer
+ * has expired.
+ * @note we need to process the list as there could be
+ * other timers too in the list which have expired.
+ *
+ */
+void process_expired_timers() {
+    static const char fname[] = "process_expired_timer";
+    cprCallBackTimerMsg_t *timerMsg;
+    void *syshdr;
+    boolean processingTimers;
+
+    /* nothing to do if no timers running */
+    if (timerListHead == NULL) {
+        return;
+    }
+
+    /* nothing to do if head has not expired */
+    if (timerListHead->duration > 0) {
+        return;
+    }
+
+
+    /* There are one or more expired timers on the list */
+    processingTimers = TRUE;
+    while (processingTimers) {
+        if (timerListHead != NULL) {
+            /*
+             * Send msg to queue to indicate this timer has expired
+             */
+            if (timerListHead->duration <= 0) {
+                timerMsg = (cprCallBackTimerMsg_t *)
+                    cpr_malloc(sizeof(cprCallBackTimerMsg_t));
+                if (timerMsg) {
+                    timerMsg->expiredTimerName =
+                        timerListHead->cprTimerPtr->name;
+                    //CPR_INFO("%s: timer %s expired..\n",fname,
+                    //       timerMsg->expiredTimerName);
+
+                    timerMsg->expiredTimerId =
+                        timerListHead->cprTimerPtr->applicationTimerId;
+                    timerMsg->usrData =
+                        timerListHead->cprTimerPtr->data;
+                    syshdr = cprGetSysHeader(timerMsg);
+                    if (syshdr) {
+                        fillInSysHeader(syshdr,
+                                        timerListHead->cprTimerPtr->applicationMsgId,
+                                        sizeof(cprCallBackTimerMsg_t), timerMsg);
+                        if (cprSendMessage(timerListHead->cprTimerPtr->callBackMsgQueue,
+                                           timerMsg, (void **) &syshdr) == CPR_FAILURE) {
+                            cprReleaseSysHeader(syshdr);
+                            cpr_free(timerMsg);
+                            CPR_ERROR("%s - Call to cprSendMessage failed\n", fname);
+                            CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
+                                      fname, timerListHead->cprTimerPtr->name);
+                        }
+                    } else {
+                        cpr_free(timerMsg);
+                        CPR_ERROR("%s - Call to cprGetSysHeader failed\n", fname);
+                        CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
+                                  fname, timerListHead->cprTimerPtr->name);
+                    }
+                } else {
+                    CPR_ERROR("%s - Call to cpr_malloc failed\n", fname);
+                    CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
+                              fname, timerListHead->cprTimerPtr->name);
+                }
+                (void) removeTimer(timerListHead->cprTimerPtr);
+            } else {
+                /* The rest of the timers on the list have not yet expired */
+                processingTimers = FALSE;
+            }
+        } else {
+            /* The timer list is now empty */
+            processingTimers = FALSE;
+        }
+    } /* still more to process */
+}
+
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_tst.h b/libs/sipcc/cpr/darwin/cpr_darwin_tst.h
new file mode 100644 (file)
index 0000000..4f700b3
--- /dev/null
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_TST_H_
+#define _CPR_DARWIN_TST_H_
+
+#define APP_NAME "SIPCC-"
+#define DEB_F_PREFIX APP_NAME"%s: %s: "
+#define CPR "CPR"
+#define DEB_F_PREFIX_ARGS(msg_name, func_name) msg_name, func_name
+
+
+#endif
diff --git a/libs/sipcc/cpr/darwin/cpr_darwin_types.h b/libs/sipcc/cpr/darwin/cpr_darwin_types.h
new file mode 100644 (file)
index 0000000..1e72d9f
--- /dev/null
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DARWIN_TYPES_H_
+#define _CPR_DARWIN_TYPES_H_
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stddef.h>
+#include "inttypes.h"
+
+
+/**
+ * @typedef boolean
+ *
+ * Define boolean as an unsigned byte
+ *
+ * @note There are differences within TNP header files
+ *    @li curses.h:   bool => char
+ *    @li types.h:    boolean_t => enum
+ *    @li dki_lock.h: bool_t => int
+ */
+typedef uint8_t boolean;
+
+/*
+ * Define min/max
+ *    defined in param.h
+ *
+ * The GNU versions of the MAX and MIN macros do two things better than
+ * the old versions:
+ * 1. they are more optimal as they only evaluate a & b once by creating a
+ *    a variable of each type on the local stack.
+ * 2. they fix potential errors due to side-effects where a and b were
+ *    evaluated twice, i.e. MIN(i++,j++)
+ *
+ * @note b could be cast to a's type, to help with usage where the code
+ *       compares signed and unsigned types.
+ */
+#ifndef MIN
+#ifdef __GNUC__
+#define MIN(a,b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; })
+#else
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#endif
+
+#ifndef MAX
+#ifdef __GNUC__
+#define MAX(a,b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; })
+#else
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#endif
+
+/**
+ * @def NUL
+ *
+ * Define NUL for string termination
+ */
+#ifndef NUL
+#define NUL '\0'
+#endif
+
+/**
+ * @def RESTRICT
+ *
+ * If suppoprting the ISO/IEC 9899:1999 standard,
+ * use the '__restrict' keyword
+ */
+#if defined(_POSIX_C_SOURCE) && defined(__GNUC__)
+#define RESTRICT __restrict
+#else
+#define RESTRICT
+#endif
+
+/**
+ * @def CONST
+ *
+ * Define CONST as @c const, if supported
+ */
+#define CONST const
+
+/**
+ * @def INLINE
+ *
+ * Define the appropriate setting for inlining functions
+ */
+#ifdef __STRICT_ANSI__
+#define INLINE
+#else
+#define INLINE __inline__
+#endif
+
+/**
+ * __BEGIN_DECLS and __END_DECLS
+ *
+ * Define macros for compilation by C++ compiler
+ */
+#ifndef __BEGIN_DECLS
+#ifdef __cplusplus
+#define __BEGIN_DECLS extern "C" {
+#else
+#define __BEGIN_DECLS
+#endif
+#endif
+
+#ifndef __END_DECLS
+#ifdef __cplusplus
+#define __END_DECLS   }
+#else
+#define __END_DECLS
+#endif
+#endif
+
+/**
+ * Define TRUE/FALSE
+ *     defined in several header files
+ */
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/**
+ * @def FIELDOFFSET(struct name, field name)
+ *
+ * Macro to generate offset from a given field in a structure
+ */
+#define FIELDOFFSET(struct_name, field_name) (long)(&(((struct_name *)0)->field_name))
+
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr.h b/libs/sipcc/cpr/include/cpr.h
new file mode 100644 (file)
index 0000000..58951cc
--- /dev/null
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_H_
+#define _CPR_H_
+
+#include "cpr_types.h"
+#include "cpr_ipc.h"
+#include "cpr_locks.h"
+#include "cpr_timers.h"
+#include "cpr_threads.h"
+#include "cpr_debug.h"
+#include "cpr_memory.h"
+
+__BEGIN_DECLS
+
+/*
+ * Function prototypes for CPR init routines
+ */
+/**
+ * cprPreInit
+ *
+ * @brief The cprPreInit function IS called from pSIPCC @b before any components are initialized.
+ *
+ * This function @b SHOULD initialize those portions of the CPR that
+ * are needed before applications can start using it. The memory subsystem
+ * (sandbox) is initialized from this routine.
+ *
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ * @note pSIPCC will NOT continue and stop initialization if the return value is CPR_FAILURE.
+ */
+cprRC_t
+cprPreInit(void);
+
+/**
+ * cprPostInit
+ *
+ * @brief The cprPostInit function IS called from pSIPCC @b after all the components are initialized.
+ *
+ * This function @b SHOULD complete any CPR activities before the phone is
+ * operational. In other words a call to this function will be the last line of
+ * the phone initializtion routine from pSIPCC. This function implementation
+ * ties in a couple of debug commands to get more information on the CPR from
+ * the shell.
+ *
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ * @note pSIPCC will NOT continue and stop initialization if the return value is CPR_FAILURE.
+ */
+cprRC_t
+cprPostInit(void);
+
+__END_DECLS
+
+#endif
+
diff --git a/libs/sipcc/cpr/include/cpr_assert.h b/libs/sipcc/cpr/include/cpr_assert.h
new file mode 100644 (file)
index 0000000..0f0f660
--- /dev/null
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ASSERT_H_
+#define _CPR_ASSERT_H_
+
+#include "cpr_types.h"
+
+
+__BEGIN_DECLS
+
+
+#define __CPRSTRING(x) #x
+
+extern void __cprAssert(const char *str, const char *file, int line);
+
+
+__END_DECLS
+
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_assert.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_assert.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_assert.h"
+#endif
+
+#endif /* _CPR_ASSERT_H_ */
+
+/*
+ * This is outside the scope of the standard #ifdef <=> #endif header
+ * file to allow a per-file definition.  By default, cprAssert is not
+ * enabled.
+ */
+#undef cprAssert
+#define cprAssert(expr, rc)
+
diff --git a/libs/sipcc/cpr/include/cpr_debug.h b/libs/sipcc/cpr/include/cpr_debug.h
new file mode 100644 (file)
index 0000000..87ef13c
--- /dev/null
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_DEBUG_H_
+#define _CPR_DEBUG_H_
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+
+__BEGIN_DECLS
+
+/**
+ * CPR debugging flag - MUST be defined at some place in CPR code so that
+ * debugging/logging is possible.
+ */
+extern int32_t cprInfo;
+
+#define CPR_INFO if (cprInfo) buginf
+#define CPR_ERROR err_msg
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_errno.h b/libs/sipcc/cpr/include/cpr_errno.h
new file mode 100644 (file)
index 0000000..a9f3443
--- /dev/null
@@ -0,0 +1,181 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_ERRNO_H_
+#define _CPR_ERRNO_H_
+
+#include "cpr_types.h"
+
+__BEGIN_DECLS
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_errno.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_errno.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_errno.h"
+#endif
+
+/** The enumerations for the various error types. pSIPCC uses these error values
+ * for checking of the various error conditions and these error type abstractions
+ * MUST be defined here.
+ */
+typedef enum
+{
+    /* First 34 entries are well-known, keep it that way */
+    CPR_EPERM = 1,      /* Not super-user                    1 */
+    CPR_ENOENT,         /* No such file or directory           */
+    CPR_ESRCH,          /* No such process                     */
+    CPR_EINTR,          /* interrupted system call             */
+     //WSAEINTR
+    CPR_EIO,            /* I/O error                           */
+     //WSA_IO_INCOMPLETE
+    CPR_ENXIO,          /* No such device or address           */
+    CPR_E2BIG,          /* Arg list too long                   */
+    CPR_ENOEXEC,        /* Exec format error                   */
+    CPR_EBADF,          /* Bad file number                     */
+    CPR_ECHILD,         /* No children                         */
+    CPR_EAGAIN,         /* Resource temporarily unavailable    */
+     //WSATRY_AGAIN
+    CPR_EWOULDBLOCK = CPR_EAGAIN,
+     //WSAEWOULDBLOCK
+    CPR_ENOMEM,         /* Not enough core                     */
+     //WSA_NOT_ENOUGH_MEMORY
+    CPR_EACCES,         /* Permission denied                   */
+     //WSAEACCES
+    CPR_EFAULT,         /* Bad address                         */
+     //WSAEFAULT
+    CPR_ENOTBLK,        /* Block device required               */
+    CPR_EBUSY,          /* Mount device busy                   */
+    CPR_EEXIST,         /* File exists                         */
+    CPR_EXDEV,          /* Cross-device link                   */
+    CPR_ENODEV,         /* No such device                      */
+    CPR_ENOTDIR,        /* Not a directory                     */
+    CPR_EISDIR,         /* Is a directory                      */
+    CPR_EINVAL,         /* Invalid argument                    */
+     //WSAEINVAL
+     //WSA_INVALID_PARAMETER
+    CPR_ENFILE,         /* File table overflow                 */
+    CPR_EMFILE,         /* Too many open files                 */
+     //WSAEMFILE
+    CPR_ENOTTY,         /* Inappropriate ioctl for device      */
+    CPR_ETXTBSY,        /* Text file busy                      */
+    CPR_EFBIG,          /* File too large                      */
+    CPR_ENOSPC,         /* No space left on device             */
+    CPR_ESPIPE,         /* Illegal seek                        */
+    CPR_EROFS,          /* Read only file system               */
+    CPR_EMLINK,         /* Too many links                      */
+    CPR_EPIPE,          /* Broken pipe                         */
+    CPR_EDOM,           /* Math arg out of domain of func      */
+    CPR_ERANGE,         /* Math result not representable       */
+
+    /*     Socket error conditions */
+    CPR_ENOTSOCK,       /* Socket operation on non-socket   35 */
+    CPR_EDESTADDRREQ,   /* Destination address required        */
+    CPR_EMSGSIZE,       /* Message too long                    */
+    CPR_EPROTOTYPE,     /* Protocol wrong type for socket      */
+    CPR_ENOPROTOOPT,    /* Protocol not available              */
+    CPR_EPROTONOSUPPORT,/* Protocol not supported              */
+    CPR_ESOCKTNOSUPPORT,/* Socket type not supported           */
+    CPR_EOPNOTSUPP,     /* Operation not supported on socket   */
+    CPR_EPFNOSUPPORT,   /* Protocol family not supported       */
+    CPR_EAFNOSUPPORT,   /* Address family not supp. by protocol*/
+    CPR_EADDRINUSE,     /* Address already in use              */
+    CPR_EADDRNOTAVAIL,  /* Can not assign requested address    */
+    CPR_ENETDOWN,       /* Network is down                     */
+    CPR_ENETUNREACH,    /* Network is unreachable              */
+    CPR_ENETRESET,      /* Netwk dropped conn. because of reset*/
+    CPR_ECONNABORTED,   /* Software caused connection abort    */
+    CPR_ECONNRESET,     /* Connection reset by peer            */
+    CPR_ENOBUFS,        /* No buffer space available           */
+    CPR_EISCONN,        /* Socket is already connected         */
+    CPR_ENOTCONN,       /* Socket is not connected             */
+    CPR_ESHUTDOWN,      /* Can not send after socket shutdown  */
+    CPR_ETOOMANYREFS,   /* Too many references: can not splice */
+    CPR_ETIMEDOUT,      /* Connection timed out                */
+    CPR_ECONNREFUSED,   /* Connection refused                  */
+    CPR_EHOSTDOWN,      /* Host is down                        */
+    CPR_EHOSTUNREACH,   /* No route to host                    */
+    CPR_EALREADY,       /* Operation already in progress       */
+    CPR_EINPROGRESS,    /* Operation now in progress           */
+
+    /* The following error conditions are common among */
+    /* CNU, Linux and Solaris environments             */
+
+    CPR_ENOMSG,         /* No message of desired type       63 */
+    CPR_EIDRM,          /* Identifier removed                  */
+    CPR_ECHRNG,         /* Channel number out of range         */
+    CPR_EL2NSYNC,       /* Level 2 not synchronized            */
+    CPR_EL3HLT,         /* Level 3 halted                      */
+    CPR_EL3RST,         /* Level 3 reset                       */
+    CPR_ELNRNG,         /* Link number out of range            */
+    CPR_EUNATCH,        /* Protocol driver not attached        */
+    CPR_ENOCSI,         /* No CSI structure available          */
+    CPR_EL2HLT,         /* Level 2 halted                      */
+    CPR_ENOLCK,         /* No record locks available.          */
+    CPR_EDEADLK,        /* Deadlock condition                  */
+    CPR_ECANCELED,      /* Operation canceled                  */
+    CPR_ENOTSUP,        /* Operation not supported             */
+    CPR_EDQUOT,         /* Disc quota exceeded                 */
+
+    /*    Convergent error conditions */
+    CPR_EBADE,          /* invalid exchange                 78 */
+    CPR_EBADR,          /* invalid request descriptor          */
+    CPR_EXFULL,         /* exchange full                       */
+    CPR_ENOANO,         /* no anode                            */
+    CPR_EBADRQC,        /* invalid request code                */
+    CPR_EBADSLT,        /* invalid slot                        */
+    CPR_EDEADLOCK,      /* file locking deadlock error         */
+    CPR_EBFONT,         /* bad font file fmt                   */
+
+    /*    Stream error conditions */
+    CPR_ENOSTR,         /* Device not a stream              86 */
+    CPR_ENODATA,        /* no data (for no delay io)           */
+    CPR_ETIME,          /* timer expired                       */
+    CPR_ENOSR,          /* out of streams resources            */
+    CPR_ENONET,         /* Machine is not on the network       */
+    CPR_ENOPKG,         /* Package not installed               */
+    CPR_EREMOTE,        /* The object is remote                */
+    CPR_ENOLINK,        /* the link has been severed           */
+    CPR_EADV,           /* advertise error                     */
+    CPR_ESRMNT,         /* srmount error                       */
+    CPR_ECOMM,          /* Communication error on send         */
+    CPR_EPROTO,         /* Protocol error                      */
+    CPR_EMULTIHOP,      /* multihop attempted                  */
+    CPR_EBADMSG,        /* trying to read unreadable message   */
+    CPR_ENAMETOOLONG,   /* path name is too long               */
+    CPR_EOVERFLOW,      /* value too large to be stored        */
+    CPR_ENOTUNIQ,       /* given log. name not unique          */
+    CPR_EBADFD,         /* f.d. invalid for this operation     */
+    CPR_EREMCHG,        /* Remote address changed              */
+
+    /*    Shared library error conditions */
+    CPR_ELIBACC,        /* Can't access a needed shared library*/
+    CPR_ELIBBAD,        /* Accessing a corrupted shared library*/
+    CPR_ELIBSCN,        /* .lib section in a.out corrupted.    */
+    CPR_ELIBMAX,        /* Attempting to link in too many libs */
+    CPR_ELIBEXEC,       /* Attempting to exec a shared library */
+    CPR_EILSEQ,         /* Illegal byte sequence               */
+    CPR_ENOSYS,         /* Unsupported file system operation   */
+    CPR_ELOOP,          /* Symbolic link loop                  */
+    CPR_ERESTART,       /* Restartable system call             */
+    CPR_ESTRPIPE,       /* If pipe, don't sleep in stream head */
+    CPR_ENOTEMPTY,      /* directory not empty                 */
+    CPR_EUSERS,         /* Too many users (for UFS)            */
+
+    CPR_ESTALE,         /* Stale NFS file handle               */
+
+    /* The following error conditions not common among OSs */
+    CPR_ECLOSED,        /* CNU specific, connection closed by host
+                           (may need to re-map to something else,
+                            still TBD) */
+    CPR_EINIT,
+
+   CPR_UNKNOWN_ERR, /* 120 */
+   CPR_ERRNO_MAX
+} cpr_errno_e;
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_in.h b/libs/sipcc/cpr/include/cpr_in.h
new file mode 100644 (file)
index 0000000..745c4f4
--- /dev/null
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_IN_H_
+#define _CPR_IN_H_
+
+#include "cpr_types.h"
+
+__BEGIN_DECLS
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_in.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_in.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_in.h"
+#endif
+
+/**
+ * Definitions for IP type of service. Used by pSIPCC for setting up different
+ * TOS values.
+ */
+#define IP_TOS_LOWDELAY     0x10
+#define IP_TOS_THROUGHPUT   0x08
+#define IP_TOS_RELIABILITY  0x04
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_ipc.h b/libs/sipcc/cpr/include/cpr_ipc.h
new file mode 100644 (file)
index 0000000..8bf62f4
--- /dev/null
@@ -0,0 +1,165 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_IPC_H_
+#define _CPR_IPC_H_
+
+#include "cpr_types.h"
+
+__BEGIN_DECLS
+
+/**
+ * Define handle for message queues
+ */
+typedef void* cprMsgQueue_t;
+
+/*
+ * A timeout value of -1 means wait forever when
+ * attempting to get a message. Just #define something
+ * so the application code is easier to read.
+ */
+#define WAIT_FOREVER -1
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_ipc.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_ipc.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_ipc.h"
+#endif
+
+/* Function prototypes */
+/**
+ * Creates a message queue
+ *
+ * @brief The cprCreateMessageQueue function is called to allow the OS to
+ * perform whatever work is needed to create a message queue.
+
+ * If the name is present, CPR should assign this name to the message queue to assist in
+ * debugging. The message queue depth is the second input parameter and is for
+ * setting the desired queue depth. This parameter may not be supported by all OS.
+ * Its primary intention is to set queue depth beyond the default queue depth
+ * limitation.
+ * On any OS where there is no limit on the message queue depth or
+ * its queue depth is sufficiently large then this parameter is ignored on that
+ * OS.
+ *
+ * @param[in] name  - name of the message queue (optional)
+ * @param[in] depth - the message queue depth, optional field which should
+ *                default if set to zero(0)
+ *
+ * @return Msg queue handle or NULL if init failed, errno should be provided
+ *
+ * @note the actual message queue depth will be bounded by the
+ *       standard system message queue depth and CPR_MAX_MSG_Q_DEPTH.
+ *       If 'depth' is outside of the bounds, the value will be
+ *       reset automatically.
+ */
+cprMsgQueue_t
+cprCreateMessageQueue(const char *name, uint16_t depth);
+
+
+/**
+  * cprDestroyMessageQueue
+ * @brief Removes all messages from the queue and then destroy the message queue
+ *
+ * The cprDestroyMessageQueue function is called to destroy a message queue. The
+ * function drains any messages from the queue and the frees the
+ * message queue. Any messages on the queue are to be deleted, and not sent to the intended
+ * recipient. It is the application's responsibility to ensure that no threads are
+ * blocked on a message queue when it is destroyed.
+ *
+ * @param[in] msgQueue - message queue to destroy
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE, errno should be provided in this case
+ */
+cprRC_t
+cprDestroyMessageQueue(cprMsgQueue_t msgQueue);
+
+#ifdef CPR_USE_SET_MESSAGE_QUEUE_THREAD
+/**
+  * cprSetMessageQueueThread
+ * @brief Associate a thread with the message queue
+ *
+ * This method is used by pSIPCC to associate a thread and a message queue.
+ * @param[in] msgQueue  - msg queue to set
+ * @param[in] thread    - CPR thread to associate with queue
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ *
+ * @note Nothing is done to prevent overwriting the thread ID
+ *       when the value has already been set.
+ */
+cprRC_t
+cprSetMessageQueueThread(cprMsgQueue_t msgQueue, cprThread_t thread);
+#endif
+
+
+/**
+  * cprGetMessage
+ * @brief Retrieve a message from a particular message queue
+ *
+ * The cprGetMessage function retrieves the first message from the message queue
+ * specified and returns a void pointer to that message.
+ *
+ * @param[in]  msgQueue    - msg queue from which to retrieve the message. This
+ * is the handle returned from cprCreateMessageQueue.
+ * @param[in]  waitForever - boolean to either wait forever (TRUE) or not
+ *                           wait at all (FALSE) if the msg queue is empty.
+ * @param[out] ppUserData  - pointer to a pointer to user defined data. This
+ * will be NULL if no user data was present.
+ *
+ * @return Retrieved message buffer or NULL if failure occurred or
+ *         the waitForever flag was set to false and no messages were
+ *         on the queue.
+ *
+ * @note   If ppUserData is defined, the value will be initialized to NULL
+ */
+void *
+cprGetMessage(cprMsgQueue_t msgQueue,
+              boolean waitForever,
+              void** usrPtr);
+
+/**
+  * cprSendMessage
+ * @brief Place a message on a particular queue.  Note that caller may
+ * block (see comments below)
+ *
+ * @param[in] msgQueue   - msg queue on which to place the message
+ * @param[in] msg        - pointer to the msg to place on the queue
+ * @param[in] ppUserData - pointer to a pointer to user defined data
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE, errno should be provided
+ *
+ * @note 1. Messages queues are set to be non-blocking, those cases
+ *       where the system call fails with a would-block error code
+ *       (EAGAIN) the function will attempt other mechanisms described
+ *       below.
+ * @note 2. If enabled with an extended message queue, either via a
+ *       call to cprCreateMessageQueue with depth value or a call to
+ *       cprSetExtendMessageQueueDepth() (when unit testing), the message
+ *       will be added to the extended message queue and the call will
+ *       return successfully.  When room becomes available on the
+ *       system's message queue, those messages will be added.
+ * @note 3. If the message queue becomes full and no space is availabe
+ *       on the extended message queue, then the function will attempt
+ *       to resend the message up to CPR_ATTEMPTS_TO_SEND and the
+ *       calling thread will *BLOCK* CPR_SND_TIMEOUT_WAIT_INTERVAL
+ *       milliseconds after each failed attempt.  If unsuccessful
+ *       after all attempts then EGAIN error code is returned.
+ * @note 4. This applies to all CPR threads, including the timer thread.
+ *       So it is possible that the timer thread would be forced to
+ *       sleep which would have the effect of delaying all active
+ *       timers.  The work to fix this rare situation is not considered
+ *       worth the effort to fix....so just leaving as is.
+ */
+cprRC_t
+cprSendMessage(cprMsgQueue_t msgQueue,
+               void* msg,
+               void** usrPtr);
+
+__END_DECLS
+
+#endif
+
diff --git a/libs/sipcc/cpr/include/cpr_locks.h b/libs/sipcc/cpr/include/cpr_locks.h
new file mode 100644 (file)
index 0000000..39de78b
--- /dev/null
@@ -0,0 +1,129 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LOCKS_H_
+#define _CPR_LOCKS_H_
+
+#include "cpr_types.h"
+#include "cpr_time.h"
+
+__BEGIN_DECLS
+
+/**
+ * Define handle for mutexes
+ */
+typedef void* cprMutex_t;
+
+/**
+ * Mutex information needed to hide OS differences in implementation.
+ * To use mutexes, the application code may pass in a name to the
+ * create function for mutexes. CPR does not use this field, it is
+ * solely for the convience of the application and to aid in debugging.
+ * Upon an application calling the init routine, CPR will malloc the
+ * memory for a mutex, set the handlePtr or handleInt as appropriate
+ * and return a pointer to the mutex structure.
+ */
+typedef struct {
+    const char*  name;
+    uint16_t lockId;
+    union {
+      void* handlePtr;
+      uint32_t handleInt;
+    } u;
+} cpr_mutex_t;
+
+
+/**
+ * cprCreateMutex
+ *
+ * @brief Creates a mutual exclusion block
+ *
+ * The cprCreateMutex function is called to allow the OS to perform whatever
+ * work is needed to create a mutex.
+ *
+ * @param[in] name  - name of the mutex. If present, CPR assigns this name to
+ * the mutex to assist in debugging.
+ *
+ * @return Mutex handle or NULL if creation failed. If NULL, set errno
+ */
+cprMutex_t
+cprCreateMutex(const char * name);
+
+
+/**
+ * cprDestroyMutex
+ *
+ * @brief Destroys the mutex passed in.
+ *
+ * The cprDestroyMutex function is called to destroy a mutex. It is the
+ * application's responsibility to ensure that the mutex is unlocked when
+ * destroyed. Unpredictiable behavior will occur if an application
+ * destroys a locked mutex.
+ *
+ * @param[in] mutex - mutex to destroy
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE. errno should be set for CPR_FAILURE.
+ */
+cprRC_t
+cprDestroyMutex(cprMutex_t mutex);
+
+
+/**
+ * cprGetMutex
+ *
+ * @brief Acquire ownership of a mutex
+ *
+ * This function locks the mutex referenced by the mutex parameter. If the mutex
+ * is locked by another thread, the calling thread will block until the mutex is
+ * released.
+ *
+ * @param[in] mutex - Which mutex to acquire
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprGetMutex(cprMutex_t mutex);
+
+
+/**
+ * cprReleaseMutex
+ *
+ * @brief Release ownership of a mutex
+ *
+ * This function unlocks the mutex referenced by the mutex parameter.
+ * @param[in] mutex - Which mutex to release
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprReleaseMutex(cprMutex_t mutex);
+
+
+/**
+ * Define handle for conditions
+ */
+typedef void* cprSignal_t;
+
+/**
+ * Condition information needed to hide OS differences in implementation.
+ * To use conditions, the application code may pass in a name to the
+ * create function for mutexes. CPR does not use this field, it is
+ * solely for the convience of the application and to aid in debugging.
+ * Upon an application calling the init routine, CPR will malloc the
+ * memory for a condition, set the handlePtr or handleInt as appropriate
+ * and return a pointer to the condition structure.
+ */
+typedef struct {
+    const char *name;
+    uint16_t lockId;
+    union {
+      void *handlePtr;
+      uint32_t handleInt;
+    } u;
+} cpr_signal_t;
+
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_memory.h b/libs/sipcc/cpr/include/cpr_memory.h
new file mode 100644 (file)
index 0000000..c70647e
--- /dev/null
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_MEMORY_H_
+#define _CPR_MEMORY_H_
+
+#include "cpr_types.h"
+
+#ifdef SIP_OS_WINDOWS
+#include "../win32/cpr_win_defines.h"
+#endif
+
+typedef void *cprBuffer_t;
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_rand.h b/libs/sipcc/cpr/include/cpr_rand.h
new file mode 100644 (file)
index 0000000..155aca6
--- /dev/null
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_RAND_H_
+#define _CPR_RAND_H_
+
+__BEGIN_DECLS
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_rand.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_rand.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_rand.h"
+#endif
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_socket.h b/libs/sipcc/cpr/include/cpr_socket.h
new file mode 100644 (file)
index 0000000..e561cb1
--- /dev/null
@@ -0,0 +1,702 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_SOCKET_H_
+#define _CPR_SOCKET_H_
+
+#include "cpr_types.h"
+#include "cpr_time.h"
+
+__BEGIN_DECLS
+
+/*
+ * By default enable use of sockets
+ */
+#define CPR_USE_SOCKETS
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_socket.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_socket.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_socket.h"
+#endif
+
+
+/**
+ * Socket shutdown status
+ */
+typedef enum
+{
+    CPR_SHUTDOWN_RECEIVE,
+    CPR_SHUTDOWN_SEND,
+    CPR_SHUTDOWN_BOTH
+} cpr_shutdown_e;
+
+
+/**
+ * Socket address structures
+ */
+typedef struct
+{
+    uint16_t sa_family;
+    int8_t   sa_data[14];
+} cpr_sockaddr_t;
+
+typedef struct
+{
+    uint16_t         sin_family;
+    uint16_t         sin_port;
+    struct in_addr_s sin_addr;
+    /* Pad to size of cpr_sockaddr_t */
+    uint8_t          sin_zero[sizeof(cpr_sockaddr_t) -
+                              sizeof(struct in_addr_s) -
+                              sizeof(uint16_t) * 2];
+} cpr_sockaddr_in_t;
+
+
+typedef struct
+{
+     uint16_t       sin6_family;
+     uint16_t       sin6_port;
+     uint32_t       sin6_flowinfo;
+     cpr_in6_addr_t sin6_addr;
+     uint32_t       sin6_scope_id;
+} cpr_sockaddr_in6_t;
+
+typedef struct sockaddr_storage cpr_sockaddr_storage; // need uniform the definition of sockaddr_storage in win and linux
+
+/*
+ * Functions
+ */
+
+#ifdef CPR_USE_SOCKETS
+
+
+/**
+ * cprBind
+ *
+ * @brief The cprBind function is the CPR wrapper for the "Bind" socket call.
+ *
+ * The cprBind() function shall assign a local socket address address to a
+ * socket identified by descriptor socket that has no local socket address
+ * assigned. Sockets created with the cprSocket() function are initially
+ * unnamed; they are identified only by their address family.
+ *
+ * @param[in] soc  - The socket previously created using cprAccept that is to be
+ *                   bound
+ * @param[out] addr - The address of the socket that is to be bound
+ * @param[in] addr_len Points to a cpr_socklen_t structure which on specifies the length
+ *                   of the supplied cpr_sockaddr_t structure.
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ *         @li [CPR_ENOTSOCK]   The descriptor references a file, not a socket
+ *
+ */
+cpr_status_e
+cprBind(cpr_socket_t socket,
+        CONST cpr_sockaddr_t * RESTRICT addr,
+        cpr_socklen_t addr_len);
+
+/**
+ * cprCloseSocket
+ *
+ * @brief The cprCloseSocket function shall destroy the socket
+ *
+ * The cprCloseSocket() function shall destroy the socket descriptor indicated
+ * by socket.  The descriptor may be made available for return by subsequent
+ * calls to cprSocket().  If the linger option is set with a non-zero timeout
+ * and the socket has untransmitted data, then cprCloseSocket() shall block for
+ * up to the current linger interval until all data is transmitted.
+ *
+ * @param[in] soc  - The socket that needs to be destroyed
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+cprCloseSocket(cpr_socket_t socket);
+
+/**
+ * cprConnect
+ *
+ * @brief The cprConnect function is the wrapper for the "connect" socket API
+ *
+ * The cprConnect() function shall attempt to make a connection on a socket.
+ * If the connection cannot be established immediately and non-blocking is set for
+ * the socket, cprConnect() shall fail and set cpr_errno to [CPR_EINPROGRESS], but
+ * the connection request shall not be aborted, and the connection shall be
+ * established asynchronously. When the connection has been established
+ * asynchronously, cprSelect() shall indicate that the file descriptor for the
+ * socket is ready for writing.
+ * If the initiating socket is connection-mode, then cprConnect() shall attempt to
+ * establish a connection to the address specified by the address argument.If the
+ * connection cannot be established immediately and non-blocking is not set for
+ * the socket, cprConnect() shall block for up to an unspecified timeout interval
+ * until the connection is established. If the timeout interval expires before the
+ * connection is established, cprConnect() shall fail and the connection attempt
+ * shall be aborted.
+ * If the initiating socket is connectionless (i.e. SOCK_DGRAM), then cprConnect()
+ * shall set the socket's peer address, and no connection is made. The peeraddress
+ * identifies where all datagrams are sent on subsequent cprSend() functions, and
+ * limits the remote sender for subsequent cprRecv() functions. If address is a
+ * null address for the protocol, the socket's peer address shall be reset.
+ *
+ * @param[in] soc  - Specifies the socket created with cprSocket() to connect
+ * @param[in] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
+ * @param[in] addr_len  - Points to a cpr_socklen_t structure which specifies
+ *                        the length of the supplied cpr_sockaddr_t structure.
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+cprConnect(cpr_socket_t socket,
+           SUPPORT_CONNECT_CONST cpr_sockaddr_t * RESTRICT addr,
+           cpr_socklen_t addr_len);
+
+/**
+ * cprGetSockName
+ *
+ * @brief The cprGetSockName retrieves the locally-bound name of the socket
+ *
+ * The cprGetSockName() function shall retrieve the locally-bound name of the
+ * specified socket, store this address in the cpr_sockaddr_t struct
+ * structure pointed to by the "addr" argument, and store the length of this address in
+ * the object pointed to by the "addr_len" argument.  If the actual length
+ * of the address is greater than the length of the supplied cpr_sockaddr_t
+ * structure, the stored address shall be truncated.  If the socket has not been
+ * bound to a local name, the value stored in the object pointed to by address is
+ * unspecified.
+ *
+ * @param[in] soc  - Specifies the socket to get the peer address from
+ * @param[out] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
+ * @param[out] addr_len  - Points to a cpr_socklen_t structure which specifies
+ *                        the length of the supplied cpr_sockaddr_t structure.
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ *  @note If successful, the address argument shall point to the address of the socket
+ *  @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
+ *        @li [CPR_ENOTCONN]  The socket is not connected
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_OPNOTSUPP] The operation is not supported for the socket
+ */
+cpr_status_e
+cprGetSockName(cpr_socket_t socket,
+               cpr_sockaddr_t * RESTRICT addr,
+               cpr_socklen_t * RESTRICT addr_len);
+
+/**
+ * cprListen
+ *
+ * @brief The cprListen is the CPR wrapper for the "listen" socket API.
+ *
+ * The cprListen() function shall mark a connection-mode socket, specified by
+ * the "soc" argument, as accepting connections.  The "backlog" argument
+ * provides a hint to the implementation which the implementation shall use to
+ * limit the number of outstanding connections in the socket's listen queue.
+ * Implementations may impose a limit on backlog and silently reduce the
+ * specified value. Implementations shall support values of backlog up to
+ * SOMAXCONN.
+ * If listen() is called with a backlog argument value that is less than zero
+ * (0), the function behaves as if it had been called with a backlog argument
+ * value of 0.  A backlog argument of zero (0) may allow the socket to accept
+ * connections, in which case the length of the listen queue may be set to an
+ * implementation-defined minimum value.
+ *
+ * @param[in] soc  - Specifies the socket to get the peer address
+ * @param[in] backlog  - The limit on the number of outstanding connections
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *  @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EDESTADDRREQ]  The socket is not bound to a local address
+ */
+cpr_status_e
+cprListen(cpr_socket_t socket,
+          uint16_t backlog);
+
+/**
+ * cprRecv
+ *
+ * @brief The cprRecv() function shall receive a message from a socket.
+ *
+ * This function is normally used with connected sockets because it does not permit
+ * the application to retrieve the source address of received data.  The cprRecv()
+ * function shall return the length of the message written to the buffer pointed
+ * to by the "buf" argument.
+ * For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
+ * a single operation.  If the message is too long to fit in the supplied buffer
+ * and the "flags" argument does not have MSG_PEEK set, the excess bytes are
+ * discarded.  If the MSG_WAITALL flag is not set, data shall be returned onlyup
+ * to the end of the first message.
+ * For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
+ * data is returned as it becomes available; therefore, no data is discarded.
+ * If no messages are available at the socket and non-blocking is not set on the
+ * socket's file descriptor, cprRecv() shall block until a message arrives. If no
+ * messages are available at the socket and non-blocking is set on the socket's
+ * file descriptor, cprRecv() shall fail and set cpr_errno to [CPR_EAGAIN]or
+ * [CPR_EWOULDBLOCK].
+ * The cprSelect() function can be used to determine when data is available to be
+ * received.  The cprRecv() function is the same as cprRecvFrom() with a zero
+ * address_len argument.
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ * @param[in] flags  - The options used for the recv.
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+cprRecv(cpr_socket_t socket,
+        void * RESTRICT buf,
+        size_t len,
+        int32_t flags);
+
+/**
+ * cprRecvFrom
+ *
+ * @brief The cprRecvFrom() function shall receive a message from a specific socket.
+ *
+ * The cprRecvFrom() function shall receive a message from a socket and is
+ * normally used with connectionless-mode sockets because it permits the
+ * application to retrieve the source address of received data.  This function
+ * shall return the length of the message written to the buffer pointed to bythe
+ * buffer argument.
+ * For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
+ * a single operation.  If the message is too long to fit in the supplied buffer
+ * and the flags argument does not have MSG_PEEK set, the excess bytes are
+ * discarded.  If the MSG_WAITALL flag is not set, data shall be returned onlyup
+ * to the end of the first message.
+ * For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
+ * data is returned as it becomes available; therefore, no data is discarded.
+ * If no messages are available at the socket and non-blocking is not set on the
+ * socket's file descriptor, cprRecvFrom() shall block until a message arrives. If no
+ * messages are available at the socket and non-blocking is set on the socket's
+ * file descriptor, cprRecvFrom() shall fail and set cpr_errno to [CPR_EAGAIN]or
+ * [CPR_EWOULDBLOCK].
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ * @param[in] flags  - The options used for the recvFrom
+ * @param[out] from - A null pointer or pointer to a cpr_sockaddr_t structure in
+ *                   which the sending address is to be stored.
+ * @param[out] fromlen - The length of the cpr_sockaddr_t structure pointed to by
+ *                    the "from" argument.
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+cprRecvFrom(cpr_socket_t socket,
+            void * RESTRICT buf,
+            size_t len,
+            int32_t flags,
+            cpr_sockaddr_t * RESTRICT from,
+            cpr_socklen_t * RESTRICT from_len);
+
+/**
+ * cprRecvmsg
+ *
+ * Receive a message from a socket.
+ * +++ NOT SUPPORTED +++
+ * Windows does not support the BSD 4.2 functions recvmsg/sendmsg; therefore,
+ * these functions are not supported within this portable runtime API layer.
+ */
+/* ssize_t cprRecvmsg (cpr_socket_t socket, struct msghdr_t *msg, uint32_t flags) */
+
+/**
+ * cprSelect
+ *
+ * @brief The cprSelect() function is the CPR wrapper for the "select" socket API.
+ *
+ * The cprSelect() function returns which of the specified file descriptors is ready for
+ * reading, ready for writing, or has an exception pending.  The function will
+ * block up to the specified timeout interval for one of the conditions to be
+ * true or until interrupted by a signal.
+ *
+ * File descriptor masks of type fd_set can be initialized and tested with
+ * FD_CLR(), FD_ISSET(), FD_SET(), and FD_ZERO().  The OS-implementation may
+ * implement these calls either as a macro definition or an actual function.
+ * void FD_CLR(cpr_socket_t, fd_set *) shall remove the file descriptor fd from the
+ * set. If fd is not a member of this set, there shall be no effect on theset.
+ * int FD_ISSET(cpr_socket_t, fd_set *) shall evaluate to non-zero if the file
+ * descriptor fd is a member of the set, and shall evaluate to zero otherwise.
+ * void FD_SET(cpr_socket_t, fd_set *) shall add the file descriptor fd to the set.
+ * If the file descriptor fd is already in this set, there shall be no effect on
+ * the set.
+ * void FD_ZERO(fd_set *) shall initialize the descriptor set to the null set.
+ *
+ * @param[in] nfds    Specifies the argument range of file descriptors to be tested. The
+ *            descriptors from zero through nfds-1 in the descriptor sets shall be examined.
+ *
+ * @param[in] read_fds    If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for being ready to read.
+ *    @li On output this specifies the file descriptors that are ready to read.
+ *
+ * @param[in] write_fds   If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for being ready to write.
+ *    @li On output this specifies the file descriptors that are ready to write.
+ *
+ * @note If you specify a non-NULL value for write_fds then note that this function will
+ *       return immediately if the socket is OK to write to, to indicate that a send, sendto,
+ *       (or on Windows a WSASendto()) are guaranteed to succeed. You generally would not keep
+ *       checking for write access in a loop, as cprSelect() would never get a chance to time
+ *       out if the socket was generally available for a write operation. You should really
+ *       only check for writability when you know you have something to write to a socket.
+ *
+ * @param[in] except_fds   If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for errors/exceptions pending.
+ *    @li On output this specifies the file descriptors that have errors/exceptions pending.
+ *
+ * @param[in] timeout If not a null pointer, this  points to an object of type struct cpr_timeval
+ *       that specifies the maximum time interval to wait for the selection to complete.
+ *       If timeout expires, the function shall return.  If the parameter is a null pointer, the function
+ *       will block indefinitely until at least one file descriptor meets the criteria.
+ *
+ * @note While this function supports multiple file descriptor types, only file descriptors referring to a
+ *       socket are guaranteed to be supported.
+ * @note Note that the "nfds" parameter is not used in Windows.
+ *
+ * @return Upon successful completion, cprSelect() shall return the number of file descriptors ready.
+ *       Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to indicate the error where read_fds,
+ *       write_fds and error_fds are not modified.
+ *
+ *        The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_INTR]   The function was interrupted before an event or
+ *                         timeout occurred
+ *        @li [CPR_INVAL]  An invalid timeout was specified or nfds is less
+ *                        than 0 or greater than FD_SETSIZE
+ *
+ */
+int16_t
+cprSelect(uint32_t nfds,
+          fd_set * RESTRICT read_fds,
+          fd_set * RESTRICT write_fds,
+          fd_set * RESTRICT except_fds,
+          struct cpr_timeval * RESTRICT timeout);
+
+/**
+ * Supported cprSelect macros are provided as follows
+ */
+
+/**
+ * FD_CLR(s, *set)
+ *
+ * Removes the descriptor s from set.
+ *
+ * Parameters: s   - the descriptor
+ *             set - pointer to the fd_set
+ *
+ * Return Value: None
+ */
+
+/**
+ * FD_ISSET(s, *set)
+ *
+ * Nonzero if s is a member of the set. Otherwise, zero.
+ *
+ * Parameters: s   - the descriptor
+ *             set - pointer to the fd_set
+ *
+ * Return Value: None
+ */
+
+/**
+ * FD_SET(s, *set)
+ *
+ * Adds descriptor s to set.
+ *
+ * Parameters: s   - the descriptor
+ *             set - pointer to the fd_set
+ *
+ * Return Value: None
+ */
+
+/**
+ * FD_ZERO(*set)
+ *
+ * Initializes the set to the null set.
+ *
+ * Parameters: s   - the descriptor
+ *             set - pointer to the fd_set
+ *
+ * Return Value: None
+ */
+
+/**
+ * cprSend
+ *
+ * @brief The cprSend() function is the CPR wrapper for the "send" socket API.
+ *
+ * The cprSend() function shall transmit a message from the specified socket to
+ * its peer. The cprSend() function shall send a message only when the socket is
+ * connected. The length of the message to be sent is specified by the length
+ * argument. If the message is too long to pass through the underlying protocol,
+ * cprSend() shall fail and no data is transmitted.  Delivery of the message is
+ * not guaranteed.
+ * If space is not available at the sending socket to hold the message to be
+ * transmitted, and the socket does not have non-blocking set, cprSend() shall
+ * block until space is available; otherwise, if non-blocking is set, the
+ * cprSend() call shall fail.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags   The socket options
+ *
+ * @return Upon successful completion, cprSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+cprSend(cpr_socket_t socket,
+        CONST void *buf,
+        size_t len,
+        int32_t flags);
+
+/**
+ * cprSendTo
+ *
+ * @brief The cprSendTo() function is the CPR wrapper for the "send" socket API.
+ *
+ * The cprSendTo() function shall send a message through a socket. If the socket
+ * is connectionless-mode, the message shall be sent to the address specified by
+ * address. If the socket is connection-mode, address shall be ignored.
+ * Delivery of the message is not guaranteed.
+ * If space is not available at the sending socket to hold the message to be
+ * transmitted, and the socket does not have non-blocking set, cprSendTo() shall
+ * block until space is available; otherwise, if non-blocking is set, the
+ * cprSendTo() call shall fail.
+ * The cprSelect() function can be used to determine when it is possible to send
+ * more data.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] msg  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags   The socket options
+ * @param[in] dest_addr Points to a cpr_sockaddr_t structure containing the destination
+ *                    address.
+ * @param[in] dest_len Specifies the length of the cpr_sockaddr_t structure pointed to by
+ *                     the "dest_addr" argument.
+ *
+ * @return Upon successful completion, cprSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+cprSendTo(cpr_socket_t socket,
+          CONST void *msg,
+          size_t len,
+          int32_t flags,
+          CONST cpr_sockaddr_t *dest_addr,
+          cpr_socklen_t dest_len);
+
+
+/**
+ * cprSendmsg
+ *
+ * Send a message from a socket.
+ * +++ NOT SUPPORTED +++
+ * Windows does not support the BSD 4.2 functions recvmsg/sendmsg; therefore,
+ * these functions are not supported within this portable runtime API layer.
+ */
+/* ssize_t sendmsg (cpr_socket_t socket, CONST struct msghdr *msg, uint32_t flags) */
+
+
+/**
+ * cprSetSockOpt
+ *
+ * @brief The cprSetSockOpt() function is used to set the socket options
+ *
+ * The cprSetSockOpt() function shall set the option specified by the
+ * option_name argument, at the protocol level specified by the "level" argument,
+ * to the value pointed to by the "opt_val" argument for the socket specified
+ * by the "soc" argument.
+ * The level argument specifies the protocol level at which the option resides. To
+ * set options at the socket level, specify the level argument as SOL_SOCKET. To
+ * set options at other levels, supply the appropriate level identifier for the
+ * protocol controlling the option. For example, to indicate that an option is
+ * interpreted by the TCP (Transport Control Protocol), set level to IPPROTO_TCP
+ * as defined in the <cpr_in.h> header.
+ * The opt_name argument specifies a single option to set. The option_name
+ * argument and any specified options are passed uninterpreted to the appropriate
+ * protocol module. The <cpr_socket.h> header defines the socket-level options.
+ *
+ * @param[in] soc The socket on which the options need to be set
+ * @param[in] level The protocol level at which the option resides
+ * @param[in] opt_name This specifies the single option that is being set
+ * @param[in] opt_val The values for the option
+ * @param[in] opt_len The length field for the option values
+ *
+ * @return Upon successful completion, CPR_SUCCESS shall be returned;
+ * otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
+ * error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EINVAL]    The specified option is invalid or the socket is
+ *                          shut down
+ *        @li [CPR_EISCONN]   The specified socket is already connected and can
+ *                          not be changed
+ *        @li [CPR_ENOPROTOOPT]   The option is not supported by the protocol
+ *
+ */
+cpr_status_e
+cprSetSockOpt(cpr_socket_t socket,
+              uint32_t level,
+              uint32_t opt_name,
+              CONST void *opt_val,
+              cpr_socklen_t opt_len);
+
+/**
+ * cprSetSockNonBlock
+ *
+ * @brief The cprSetSockNonBlock() function is used to set the socket options
+ *
+ * The cprSetSockNonBlock() function shall set a socket to be non blocking. It
+ * uses the fcntl function on the socket desriptor to achieve this. If the fcntl
+ * operation fails, a CPR_FAILURE is returned and errno is set by the OS
+ * implementation.
+ *
+ * @param[in] soc The socket that needs to be set to non-blocking
+ *
+ * @return Upon successful completion, CPR_SUCCESS shall be returned;
+ * otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
+ * error.
+ */
+cpr_status_e
+cprSetSockNonBlock(cpr_socket_t socket);
+
+/**
+ * cprSocket
+ *
+ * @brief The cprSocket() is the CPR wrapper for the "socket" API
+ *
+ * The cprSocket() function shall create an unbound socket in a
+ * communications domain, and return a file descriptor that can be used
+ * in later function calls that operate on sockets.
+ *
+ * @param[in] domain  The communications domain, i.e. address family, in which a socket is to
+ *                   be created
+ * @param[in] type    The type of socket to be created. The following types must
+ *                   be supported:
+ *             @li SOCK_STREAM Provides sequenced, reliable, bidirectional, connection-mode
+ *                               byte streams, i.e. TCP
+ *             @li SOCK_DGRAM  Provides connectionless-mode, unreliable
+ *                           datagrams of fixed maximum length, i.e. UDP
+ *             @li SOCK_SEQPACKET   Provides sequenced, reliable, bidirectional, connection-mode
+ *                          transmission paths for records.  A single operation never transfers part of
+ *                          more than one record.  Record boundaries are visible to the receiver via the
+ *                          MSG_EOR flag.
+ * @param[in] protocol    The protocol to be used with the socket.
+ *
+ * @return Upon successful completion, a socket handle defined by
+ *      cpr_socket_t shall be returned; otherwise, INVALID_SOCKET shall be returned and
+ *       cpr_errno set to indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EINVAL]   The specified option is invalid or the socket is
+ *                           shut down
+ *        @li [CPR_EMFILE]   No more socket descriptors available for the
+ *                           process
+ *        @li [CPR_ENFILE]   No more socket descriptors available for the
+ *                           system
+ *        @li [CPR_EPROTOTYPE] The socket type is not supported by the
+ *                              protocol
+ *        @li [CPR_EPROTONOSUPPORT]   The protocol is not supported for the
+ *                                    domain
+ *
+ */
+cpr_socket_t
+cprSocket(uint32_t domain,
+          uint32_t type,
+          uint32_t protocol);
+
+/* cpr_inet_pton
+ *     Convert from presentation format (which usually means ASCII printable)
+ *     to network format (which is usually some kind of binary format).
+ * @param[in] af The address family IPv4 or IPv6
+ * @param[in] src The address that needs to be converted
+ * @param[out] dst The address after the conversion
+ * @return
+ *     1 if the address was valid for the specified address family
+ *     0 if the address wasn't valid (`dst' is untouched in this case)
+ *     -1 if some other error occurred (`dst' is untouched in this case, too)
+ */
+int
+cpr_inet_pton (int af, const char *src, void *dst);
+
+
+#endif /* CPR_USE_SOCKETS */
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_stddef.h b/libs/sipcc/cpr/include/cpr_stddef.h
new file mode 100644 (file)
index 0000000..8f63dc5
--- /dev/null
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_STDDEF_H_
+#define _CPR_STDDEF_H_
+
+/*
+ * Support for stddef.h is included in the common types header file
+ */
+#include "cpr_types.h"
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_stdio.h b/libs/sipcc/cpr/include/cpr_stdio.h
new file mode 100644 (file)
index 0000000..73d6cfc
--- /dev/null
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_STDIO_H_
+#define _CPR_STDIO_H_
+
+#include "cpr_types.h"
+
+__BEGIN_DECLS
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_stdio.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_stdio.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_stdio.h"
+#endif
+
+/**
+ * Debug message sent to syslog(#LOG_DEBUG)
+ *
+ * @param[in] _format  format string
+ * @param[in] ...      variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+extern int32_t buginf(const char *_format, ...);
+
+/**
+ * Debug message sent to syslog(#LOG_DEBUG) that can be larger than #LOG_MAX
+ *
+ * @param[in] str - a fixed constant string
+ *
+ * @return  zero(0)
+ *
+ * @pre (str not_eq NULL)
+ */
+extern int32_t buginf_msg(const char *str);
+
+/**
+ * Error message sent to syslog(#LOG_ERR)
+ *
+ * @param[in] _format  format string
+ * @param[in] ...     variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+extern void err_msg(const char *_format, ...);
+
+/**
+ * Notice message sent to syslog(#LOG_INFO)
+ *
+ * @param[in] _format  format string
+ * @param[in] ...     variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+extern void notice_msg(const char *_format, ...);
+
+__END_DECLS
+
+#endif
+
diff --git a/libs/sipcc/cpr/include/cpr_stdlib.h b/libs/sipcc/cpr/include/cpr_stdlib.h
new file mode 100644 (file)
index 0000000..65e4c55
--- /dev/null
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_STDLIB_H_
+#define _CPR_STDLIB_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef SIP_OS_WINDOWS
+#include <crtdbg.h>
+#include <errno.h>
+#endif
+
+#include "mozilla/mozalloc.h"
+
+#define cpr_malloc(a) moz_xmalloc(a)
+#define cpr_calloc(a, b) moz_xcalloc(a, b)
+#define cpr_realloc(a, b) moz_xrealloc(a, b)
+#define cpr_free(a) moz_free(a)
+
+#endif
+
+
diff --git a/libs/sipcc/cpr/include/cpr_string.h b/libs/sipcc/cpr/include/cpr_string.h
new file mode 100644 (file)
index 0000000..3b981b0
--- /dev/null
@@ -0,0 +1,116 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_STRING_H_
+#define _CPR_STRING_H_
+
+#include "cpr_types.h"
+#include "cpr_strings.h"
+
+__BEGIN_DECLS
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_string.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_string.h"
+#define cpr_strdup _strdup
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_string.h"
+#endif
+
+/**
+ * sstrncpy
+ *
+ * @brief The CPR wrapper for strncpy
+ *
+ * This is Cisco's *safe* version of strncpy.  The string will always
+ * be NUL terminated (which is not ANSI compliant).
+ *
+ * @param[in] dst  - The destination string
+ * @param[in] src  - The source
+ * @param[in]  max - maximum length in octets to concatenate
+ *
+ * @return     Pointer to the @b end of the string
+ *
+ * @note       Modified to be explicitly safe for all inputs.
+ *             Also return the number of characters copied excluding the
+ *             NUL terminator vs. the original string s1.  This simplifies
+ *             code where sstrncat functions follow.
+ */
+unsigned long
+sstrncpy(char *dst, const char *src, unsigned long max);
+
+
+/**
+ * sstrncat
+ *
+ * @brief The CPR wrapper for strncat
+ *
+ * This is Cisco's *safe* version of strncat.  The string will always
+ * be NUL terminated (which is not ANSI compliant).
+ *
+ * @param[in] s1  - first string
+ * @param[in] s2  - second string
+ * @param[in]  max - maximum length in octets to concatenate
+ *
+ * @return     Pointer to the @b end of the string
+ *
+ * @note    Modified to be explicitly safe for all inputs.
+ *             Also return the end vs. the beginning of the string s1
+ *             which is useful for multiple sstrncat calls.
+ */
+char *
+sstrncat(char *s1, const char *s2, unsigned long max);
+
+/*
+ * flex_string
+ */
+#define FLEX_STRING_CHUNK_SIZE 256
+
+typedef struct {
+  char *buffer;
+  size_t buffer_length;
+  size_t string_length;
+} flex_string;
+
+/*
+ * flex_string_init
+ *
+ * Not thread-safe
+ */
+void flex_string_init(flex_string *fs);
+
+/*
+ * flex_string_free
+ *
+ * Not thread-safe
+ */
+void flex_string_free(flex_string *fs);
+
+/*
+ * flex_string_check_alloc
+ *
+ * Allocate enough chunks to hold the new minimum size.
+ *
+ * Not thread-safe
+ */
+void flex_string_check_alloc(flex_string *fs, size_t new_min_length);
+
+/*
+ * flex_string_append
+ *
+ * Not thread-safe
+ */
+void flex_string_append(flex_string *fs, const char *more);
+
+/*
+ * flex_string_sprintf
+ *
+ * Not thread-safe
+ */
+void flex_string_sprintf(flex_string *fs, const char *format, ...);
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_strings.h b/libs/sipcc/cpr/include/cpr_strings.h
new file mode 100755 (executable)
index 0000000..c960521
--- /dev/null
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_STRINGS_H_
+#define _CPR_STRINGS_H_
+
+#include "cpr_types.h"
+
+__BEGIN_DECLS
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_strings.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_strings.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_strings.h"
+#endif
+
+#ifdef CPR_USE_OS_STRCASECMP
+/* Use standard library types */
+#define cpr_strcasecmp  strcasecmp
+#define cpr_strncasecmp strncasecmp
+#else
+/* Prototypes */
+/**
+ * cpr_strcasecmp
+ *
+ * @brief The CPR wrapper for strcasecmp
+ *
+ * The cpr_strcasecmp performs case insensitive string comparison of the "s1"
+ * and the "s2" strings.
+ *
+ * @param[in] s1  - The first string
+ * @param[in] s2  - The second string
+ *
+ * @return integer <,=,> 0 depending on whether s1 is <,=,> s2
+ */
+int cpr_strcasecmp(const char *s1, const char *s2);
+
+/**
+ * cpr_strncasecmp
+ *
+ * @brief The CPR wrapper for strncasecmp
+ *
+ * The cpr_strncasecmp performs case insensitive string comparison for specific
+ * length "len".
+ *
+ * @param[in] s1  - The first string
+ * @param[in] s2  - The second string
+ * @param[in] len  - The length to be compared
+ *
+ * @return integer <,=,> 0 depending on whether s1 is <,=,> s2
+ */
+int cpr_strncasecmp(const char *s1, const char *s2, size_t len);
+#endif
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_threads.h b/libs/sipcc/cpr/include/cpr_threads.h
new file mode 100644 (file)
index 0000000..3c74a5b
--- /dev/null
@@ -0,0 +1,126 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_THREADS_H_
+#define _CPR_THREADS_H_
+
+#include "cpr_types.h"
+
+__BEGIN_DECLS
+
+/**
+ * @typedef void *cprThread_t
+ * Define handle for threads
+ */
+typedef void *cprThread_t;
+
+/**
+ * @struct cpr_thread_t
+ * System thread information needed to hide OS differences in implementation.
+ * To use threads, the application code may pass in a name to the
+ * create function for threads. CPR does not use this field, it is
+ * solely for the convenience of the application and to aid in debugging.
+ * Upon an application calling the init routine, CPR will malloc the
+ * memory for a thread, set the handlePtr or handleInt as appropriate
+ * and return a pointer to the thread structure.
+ */
+typedef struct {
+    const char *name;
+    uint32_t threadId;
+    union {
+        void *handlePtr;
+        uint64_t handleInt;
+    } u;
+} cpr_thread_t;
+
+/**
+ * Thread start routine signature
+ */
+typedef void *(*cprThreadStartRoutine)(void *data);
+
+/* Function prototypes */
+
+/**
+ * cprCreateThread
+ *
+ * @brief Create a thread
+ *
+ *  The cprCreateThread function creates another execution thread within the
+ *  current process. If the input parameter "name" is present, then this is used
+ *  for debugging purposes. The startRoutine is the address of the function where
+ *  the thread execution begins. The start routine prototype is defined as
+ *  follows
+ *  @code
+ *     int32_t (*cprThreadStartRoutine)(void* data)
+ *  @endcode
+ *
+ * @param[in]  name         - name of the thread created (optional)
+ * @param[in]  startRoutine - function where thread execution begins
+ * @param[in]  stackSize    - size of the thread's stack (IGNORED)
+ * @param[in]  priority     - thread's execution priority (IGNORED)
+ * @param[in]  data         - parameter to pass to startRoutine
+ *
+ * Return Value: Thread handle or NULL if creation failed.
+ */
+cprThread_t cprCreateThread(const char *name,
+                            cprThreadStartRoutine startRoutine,
+                            uint16_t stackSize,
+                            uint16_t priority,
+                            void *data);
+
+
+/**
+ * cprDestroyThread
+ *
+ * @brief Destroys the thread passed in.
+ *
+ * The cprDestroyThread function is called to destroy a thread. The thread
+ * parameter may be any valid thread including the calling thread itself.
+ *
+ * @param[in] thread - thread to destroy.
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE. errno should be set for FAILURE case.
+ *
+ * @note In Linux there will never be a success indication as the
+ *       calling thread will have been terminated.
+ */
+cprRC_t cprDestroyThread(cprThread_t thread);
+
+/**
+ * cprAdjustRelativeThreadPriority
+ *
+ * @brief The function sets the relative thread priority up or down by the given value.
+ *
+ * pSIPCC specific important notes:
+ * This function is used by pSIPCC to set up the priority for its threads.
+ * When pSIPCC creates threads, it requests default priority
+ * and then subsequently calls this function to adjust the level to desired
+ * value.
+ * relPri ranges from -20 (Maximum priority) to +19 (Minimum priority).
+ * The relPri values are normalized to Linux Nice levels. This implies
+ * that these values must be mapped to a range or values of your OS.
+ * For example:
+ * if your OS supports thread priorities ranging from 1 to 10 then you
+ * could do a simple linear mapping of 40 nice values to 10 values
+ * by mapping 1 to 19 and 10 to -20 and so on.
+ * The mapping is completely determined by your implementation below.
+ * Note that pSIPCC threads must run at a sufficiently higher priority to
+ * provide proper signaling cut through times.
+ * obviously, signaling cut through ultimately affects the media cut through.
+ * pSIPCC also uses virtual timers which require sufficiently high priority
+ * to provide accurate timeout behavior.
+ * A general guideline is to map the relPri passed such that it is somewhere
+ * in between the non critical threads and highly critical threads such as
+ * media or timer processing on your system.
+ *
+ * @param[in] relPri - normalized relative Priority of the thread [-20,+19]
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cprAdjustRelativeThreadPriority(int relPri);
+
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_time.h b/libs/sipcc/cpr/include/cpr_time.h
new file mode 100644 (file)
index 0000000..4b0fb9b
--- /dev/null
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_TIME_H_
+#define _CPR_TIME_H_
+
+#include "cpr_types.h"
+
+__BEGIN_DECLS
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_time.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_time.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_time.h"
+#endif
+
+/**
+ * The generic "time" value strucutre
+ */
+struct cpr_timeval
+{
+    cpr_time_t    tv_sec;
+    unsigned long tv_usec;
+};
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_timers.h b/libs/sipcc/cpr/include/cpr_timers.h
new file mode 100644 (file)
index 0000000..56e884a
--- /dev/null
@@ -0,0 +1,165 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_TIMERS_H_
+#define _CPR_TIMERS_H_
+
+#include "cpr_types.h"
+#include "cpr_ipc.h"
+
+__BEGIN_DECLS
+
+/**
+ * Define handle for timers
+ */
+typedef void *cprTimer_t;
+
+/**
+ * System timer information needed to hide OS differences in implementation.
+ * To use timers, the application code may pass in a name to the
+ * create function for timers. CPR does not use this field, it is
+ * solely for the convenience of the application and to aid in debugging.
+ * Upon an application calling the init routine, CPR will malloc the
+ * memory for a timer, set the handlePtr or handleInt as appropriate
+ * and return a pointer to the timer structure.
+ */
+
+/**
+ * Information CPR needs to send back to the application
+ * when a timer expires.
+ */
+typedef struct {
+    const char *expiredTimerName;
+    uint16_t expiredTimerId;
+    void *usrData;
+} cprCallBackTimerMsg_t;
+
+
+/* Function prototypes */
+
+/**
+ * cprSleep
+ *
+ * @brief Suspend the calling thread
+ * The cprSleep function blocks the calling thread for the indicated number of
+ * milliseconds.
+ *
+ * @param[in] duration - Number of milliseconds the thread should sleep
+ *
+ * @return -  none
+ */
+void cprSleep(uint32_t duration);
+
+
+/**
+ * cprCreateTimer
+ *
+ * @brief Initialize a timer
+ *
+ * The cprCreateTimer function is called to allow the OS to perform whatever
+ * work is needed to create a timer. The input name parameter is optional. If present, CPR assigns
+ * this name to the timer to assist in debugging. The callbackMsgQueue is the
+ * address of a message queue created with cprCreateMsgQueue. This is the
+ * queue where the timer expire message will be sent.
+ * So, when this timer expires a msg of type "applicationMsgId" will be sent to the msg queue
+ * "callbackMsgQueue" indicating that timer applicationTimerId has expired.
+ *
+ * @param[in]   name               -  name of the timer
+ * @param[in]   applicationTimerId - ID for this timer from the application's
+ *                                  perspective
+ * @param[in]   applicationMsgId   - ID for syshdr->cmd when timer expire msg
+ *                                  is sent
+ * @param[in]   callBackMsgQueue   - where to send a msg when this timer expires
+ *
+ * @return  Timer handle or NULL if creation failed.
+ */
+cprTimer_t cprCreateTimer(const char * name,
+                          uint16_t applicationTimerId,
+                          uint16_t applicationMsgId,
+                          cprMsgQueue_t callBackMsgQueue);
+
+/**
+ * cprStartTimer
+ *
+ * @brief Start a system timer
+ *
+ * The cprStartTimer function starts a previously created timer referenced by
+ * the parameter timer. CPR timer granularity is 10ms. The "timer" input
+ * parameter is the handle returned from a previous successful call to
+ * cprCreateTimer.
+ *
+ * @param[in]  timer    - which timer to start
+ * @param[in]  duration - how long before timer expires in milliseconds
+ * @param[in]  data     - information to be passed to callback function
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cprStartTimer(cprTimer_t timer, uint32_t duration, void *data);
+
+
+/**
+ * cprIsTimerRunning
+ *
+ * @brief Determine if a timer is active
+ *
+ * This function determines whether the passed in timer is currently active. The
+ * "timer" parameter is the handle returned from a previous successful call to
+ *  cprCreateTimer.
+ *
+ * @param[in] timer - which timer to check
+ *
+ * @return True is timer is active, False otherwise
+ */
+boolean cprIsTimerRunning(cprTimer_t timer);
+
+
+/**
+ * cprDestroyTimer
+ *
+ * @brief Destroys a timer.
+ *
+ * This function will cancel the timer and then destroy it. It sets
+ * all links to NULL and then frees the timer block.
+ *
+ * @param[in] timer - which timer to destroy
+ *
+ * @return  CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cprDestroyTimer(cprTimer_t timer);
+
+
+/**
+ * cprCancelTimer
+ *
+ * @brief Cancels a running timer
+ *
+ * The cprCancelTimer function cancels a previously started timer referenced by
+ * the parameter timer.
+ *
+ * @param[in] timer - which timer to cancel
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cprCancelTimer(cprTimer_t timer);
+
+
+/**
+ * cprUpdateTimer
+ *
+ * @brief Updates the expiration time for a running timer
+ *
+ * The cprUpdateTimer function cancels a previously started timer referenced by
+ * the parameter timer and then restarts the same timer with the duration passed
+ * in.
+ *
+ * @param[in]   timer    - which timer to update
+ * @param[in]   duration - how long before timer expires in milliseconds
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cprUpdateTimer(cprTimer_t timer, uint32_t duration);
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/include/cpr_types.h b/libs/sipcc/cpr/include/cpr_types.h
new file mode 100644 (file)
index 0000000..33f1707
--- /dev/null
@@ -0,0 +1,109 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_TYPES_H_
+#define _CPR_TYPES_H_
+
+#if defined SIP_OS_LINUX
+#include "../linux/cpr_linux_types.h"
+#elif defined SIP_OS_WINDOWS
+#include "../win32/cpr_win_types.h"
+#elif defined SIP_OS_OSX
+#include "../darwin/cpr_darwin_types.h"
+#endif
+
+__BEGIN_DECLS
+
+/*
+ * CPR Return Codes
+ */
+typedef enum
+{
+    CPR_SUCCESS,
+    CPR_FAILURE
+} cpr_status_e;
+typedef cpr_status_e cprRC_t;
+
+/*
+ * IPv4 address structure
+ */
+typedef uint32_t cpr_in_addr_t;
+
+struct in_addr_s
+{
+#ifdef s_addr
+    /* can occur with Windows winsock.h */
+    union {
+        struct {
+            unsigned char s_b1, s_b2, sb_b3, s_b4;
+        } S_un_b;
+        cpr_in_addr_t S_addr;
+    } S_un;
+#else
+    cpr_in_addr_t s_addr;
+#endif
+};
+
+/*
+ * IPv6 address structure
+ */
+typedef struct
+{
+    union
+    {
+        uint8_t  base8[16];
+        uint16_t base16[8];
+        uint32_t base32[4];
+    } addr;
+} cpr_in6_addr_t;
+
+#ifndef s6_addr
+#define s6_addr   addr.base8
+#endif
+#ifndef s6_addr16
+#define s6_addr16 addr.base16
+#endif
+#ifndef s6_addr32
+#define s6_addr32 addr.base32
+#endif
+
+typedef enum
+{
+     CPR_IP_ADDR_INVALID=0,
+     CPR_IP_ADDR_IPV4,
+     CPR_IP_ADDR_IPV6
+} cpr_ip_type;
+
+typedef enum
+{
+     CPR_IP_MODE_IPV4 = 0,
+     CPR_IP_MODE_IPV6,
+     CPR_IP_MODE_DUAL
+}
+cpr_ip_mode_e;
+/*
+ * IP address structure
+ */
+typedef struct
+{
+    cpr_ip_type type;
+    union
+    {
+        cpr_in_addr_t  ip4;
+        cpr_in6_addr_t ip6;
+    } u;
+} cpr_ip_addr_t;
+
+extern const cpr_ip_addr_t ip_addr_invalid;
+
+#define MAX_IPADDR_STR_LEN 48
+
+
+#define CPR_IP_ADDR_INIT(a) a.type = CPR_IP_ADDR_INVALID;
+
+typedef const char *string_t;
+
+__END_DECLS
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_align.h b/libs/sipcc/cpr/linux/cpr_linux_align.h
new file mode 100755 (executable)
index 0000000..50a63b2
--- /dev/null
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __CPR_LINUX_ALIGN_H__
+#define __CPR_LINUX_ALIGN_H__
+#include "cpr_types.h"
+
+/*
+ * Macros to determine how an address is aligned
+ */
+#define is_16bit_aligned(addr) (!((uint32_t)(addr) & 0x01))
+#define is_32bit_aligned(addr) (!((uint32_t)(addr) & 0x03))
+#define is_64bit_aligned(addr) (!((uint32_t)(addr) & 0x07))
+
+/*
+ * Macro to get a mask for a specified byte alignment
+ */
+#define ALIGN_MASK(alignment)  (~((alignment) - 1))
+
+/*
+ * Macro to set the minimum alignment
+ */
+#define ALIGN_MIN(align,align_min) (((align) > (align_min)) ? (align) : (align_min))
+
+/*
+ * Macro to round up or down "val" to be a multiple of "align", assuming
+ * "align" is a power of 2. if "align" is zero then no action will
+ * be performed
+ */
+#ifdef __typeof__
+#define ALIGN_UP(val,align)   ((align == 0) ? (val) : (__typeof__(val))(((uint32_t)(val)  +  ((align) - 1)) & ~((align) - 1)))
+#define ALIGN_DOWN(val,align) ((align == 0) ? (val) : (__typeof__(val))(((uint32_t)(val)) & ~((align) - 1)))
+#else
+#define ALIGN_UP(val,align)   ((align == 0) ? (val) : (uint32_t)(((uint32_t)(val)  +  ((align) - 1)) & ~((align) - 1)))
+#define ALIGN_DOWN(val,align) ((align == 0) ? (val) : (uint32_t)(((uint32_t)(val)) & ~((align) - 1)))
+#endif
+
+/**
+ * Macro to safely write 4 bytes based on memory alignment setting
+ */
+#ifndef CPR_MEMORY_LITTLE_ENDIAN
+#define WRITE_4BYTES_UNALIGNED(mem, value) \
+    { ((char *)mem)[0] = (uint8_t)(((value) & 0xff000000) >> 24); \
+      ((char *)mem)[1] = (uint8_t)(((value) & 0x00ff0000) >> 16); \
+      ((char *)mem)[2] = (uint8_t)(((value) & 0x0000ff00) >>  8); \
+      ((char *)mem)[3] = (uint8_t) ((value) & 0x000000ff);       \
+    }
+#else
+#define WRITE_4BYTES_UNALIGNED(mem, value) \
+    { ((char *)mem)[3] = (uint8_t)(((value) & 0xff000000) >> 24); \
+      ((char *)mem)[2] = (uint8_t)(((value) & 0x00ff0000) >> 16); \
+      ((char *)mem)[1] = (uint8_t)(((value) & 0x0000ff00) >>  8); \
+      ((char *)mem)[0] = (uint8_t) ((value) & 0x000000ff);       \
+    }
+#endif
+/**
+ * Macro to safely read 4 bytes based on memory alignment setting
+ */
+#ifndef CPR_MEMORY_LITTLE_ENDIAN
+#ifdef __typeof__
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    __typeof__(value)((((uint8_t *)mem)[0] << 24) | (((uint8_t *)mem)[1] << 16) | \
+                      (((uint8_t *)mem)[2] <<  8) |  ((uint8_t *)mem)[3])
+#else
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    (uint32_t)((((uint8_t *)mem)[0] << 24) | (((uint8_t *)mem)[1] << 16) | \
+               (((uint8_t *)mem)[2] <<  8) |  ((uint8_t *)mem)[3])
+#endif
+#else
+#ifdef __typeof__
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    __typeof__(value)((((uint8_t *)mem)[3] << 24) | (((uint8_t *)mem)[2] << 16) | \
+                      (((uint8_t *)mem)[1] <<  8) |  ((uint8_t *)mem)[0])
+#else
+#define READ_4BYTES_UNALIGNED(mem, value) \
+    (uint32_t)((((uint8_t *)mem)[3] << 24) | (((uint8_t *)mem)[2] << 16) | \
+               (((uint8_t *)mem)[1] <<  8) |  ((uint8_t *)mem)[0])
+#endif
+#endif
+
+#endif /* __CPR_LINUX_ALIGN_H__ */
diff --git a/libs/sipcc/cpr/linux/cpr_linux_assert.h b/libs/sipcc/cpr/linux/cpr_linux_assert.h
new file mode 100644 (file)
index 0000000..aba13d0
--- /dev/null
@@ -0,0 +1,85 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_ASSERT_H_
+#define _CPR_LINUX_ASSERT_H_
+
+#include "assert.h"
+
+/*--------------------------------------
+ *
+ * Macros
+ *
+ */
+
+/**
+ * CPR assert macro which calls cpr_assert_msg instead of abort
+ *
+ * The macro is dependent on the setting of FILE_ID which is used
+ * to override the __FILE__ setting.  For certain compilers, i.e.
+ * read 'Diab 4.4b', the __FILE__ is set to a Windows type path
+ * name which can contain backslashes that can cause odd output.
+ */
+#ifdef FILE_ID
+#define cpr_assert(expr) \
+    ((expr) ? (void)0 : cpr_assert_msg(FILE_ID, __LINE__, #expr))
+#else
+#define cpr_assert(expr) \
+    ((expr) ? (void)0 : cpr_assert_msg(__FILE__, __LINE__, #expr))
+#endif
+
+#define cpr_assert_debug(expr)
+
+/*
+ * A side note if somehow concerned about performance.
+ *
+ * This method will pre-render the string via the compiler,
+ * but will use more space due to larger strings.  Basically,
+ * good for speed and bad for memory.
+ *
+ * This is coded mostly as an example so if performance was an issue
+ * that the asserts could be low impact.
+ *
+ * #define cpr_assert_debug(expr) \
+ *   ((expr) ? (void)0 : cpr_assert_msg( \
+ *             __FILE__ ": line " __LINE__ ": assertion failed: " #expr))
+ *
+ * Note that this is not allowed when using __STRING_ANSI__
+ */
+
+#define cpr_assert_debug_rtn(expr)
+
+/*--------------------------------------
+ *
+ * Structures
+ *
+ */
+
+/**
+ * CPR assert modes of operation
+ */
+typedef enum {
+    CPR_ASSERT_MODE_NONE,            /**< Off, no message ouput          */
+    CPR_ASSERT_MODE_WARNING_LIMITED, /**< Warnings to syslog are limited */
+    CPR_ASSERT_MODE_WARNING_ALL,     /**< All warnings sent to syslog    */
+    CPR_ASSERT_MODE_ABORT            /**< Assert failure will call abort */
+} cpr_assert_mode_e;
+
+
+/*--------------------------------------
+ *
+ * Globals
+ *
+ */
+extern uint32_t cpr_assert_count;
+
+/*--------------------------------------
+ *
+ * Prototypes
+ *
+ */
+void
+cpr_assert_msg(const char *file, const int line, const char *expression);
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_errno.c b/libs/sipcc/cpr/linux/cpr_linux_errno.c
new file mode 100644 (file)
index 0000000..3a593de
--- /dev/null
@@ -0,0 +1,184 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_errno.h"
+#include <errno.h>
+
+/**
+ * @addtogroup OSAPIs The CPR OS Abstractions
+ * @brief Misc OS API Abstractions in CPR
+ *
+ * @{
+ */
+static int8_t errno_table[] =
+{
+    CPR_EPERM,
+    CPR_ENOENT,
+    CPR_ESRCH,
+    CPR_EINTR,
+    CPR_EIO,
+    CPR_ENXIO,
+    CPR_E2BIG,
+    CPR_ENOEXEC,
+    CPR_EBADF,
+    CPR_ECHILD,
+    CPR_EAGAIN, /*10*/
+    CPR_ENOMEM,
+    CPR_EACCES,
+    CPR_EFAULT,
+    CPR_ENOTBLK,
+    CPR_EBUSY,
+    CPR_EEXIST,
+    CPR_EXDEV,
+    CPR_ENODEV,
+    CPR_ENOTDIR,
+    CPR_EISDIR,/*20*/
+    CPR_EINVAL,
+    CPR_ENFILE,
+    CPR_EMFILE,
+    CPR_ENOTTY,
+    CPR_ETXTBSY,
+    CPR_EFBIG,
+    CPR_ENOSPC,
+    CPR_ESPIPE,
+    CPR_EROFS,
+    CPR_EMLINK,/*30*/
+    CPR_EPIPE,
+    CPR_EDOM,
+    CPR_ERANGE,
+    CPR_ENOMSG,
+    CPR_EIDRM,
+    CPR_ECHRNG,
+    CPR_EL2NSYNC,
+    CPR_EL3HLT,
+    CPR_EL3RST,
+    CPR_ELNRNG,/*40*/
+    CPR_EUNATCH,
+    CPR_ENOCSI,
+    CPR_EL2HLT,
+    CPR_EDEADLK,
+    CPR_ENOLCK,
+    CPR_ECANCELED,
+    CPR_ENOTSUP,
+    CPR_EDQUOT,
+    CPR_EBADE,
+    CPR_EBADR,
+    CPR_EXFULL,
+    CPR_ENOANO,
+    CPR_EBADRQC,
+    CPR_EBADSLT,
+    CPR_EDEADLOCK,
+    CPR_EBFONT,
+    CPR_UNKNOWN_ERR,            /* empty 58 */
+    CPR_UNKNOWN_ERR,            /* empty 59 */
+    CPR_ENOSTR,
+    CPR_ENODATA,
+    CPR_ETIME,
+    CPR_ENOSR,
+    CPR_ENONET,
+    CPR_ENOPKG,
+    CPR_EREMOTE,
+    CPR_ENOLINK,
+    CPR_EADV,
+    CPR_ESRMNT,
+    CPR_ECOMM,
+    CPR_EPROTO,
+    CPR_UNKNOWN_ERR,            /* empty 72 */
+    CPR_UNKNOWN_ERR,            /* empty 73 */
+    CPR_EMULTIHOP,
+    CPR_UNKNOWN_ERR,            /* empty 75 */
+    CPR_UNKNOWN_ERR,            /* empty 76 */
+    CPR_EBADMSG,
+    CPR_ENAMETOOLONG,
+    CPR_EOVERFLOW,
+    CPR_ENOTUNIQ,
+    CPR_EBADFD,
+    CPR_EREMCHG,
+    CPR_ELIBACC,
+    CPR_ELIBBAD,
+    CPR_ELIBSCN,
+    CPR_ELIBMAX,
+    CPR_ELIBEXEC,
+    CPR_EILSEQ,
+    CPR_ENOSYS,
+    CPR_ELOOP,
+    CPR_ERESTART,
+    CPR_ESTRPIPE,
+    CPR_ENOTEMPTY,
+    CPR_EUSERS,
+    CPR_ENOTSOCK,
+    CPR_EDESTADDRREQ,
+    CPR_EMSGSIZE,
+    CPR_EPROTOTYPE,
+    CPR_ENOPROTOOPT,
+    /* errno index goes from 99 to 120 */
+    CPR_EPROTONOSUPPORT,
+    CPR_ESOCKTNOSUPPORT,
+    CPR_EOPNOTSUPP,
+    CPR_EPFNOSUPPORT,
+    CPR_EAFNOSUPPORT,
+    CPR_EADDRINUSE,
+    CPR_EADDRNOTAVAIL,
+    CPR_ENETDOWN,
+    CPR_ENETUNREACH,
+    CPR_ENETRESET,
+    CPR_ECONNABORTED,
+    CPR_ECONNRESET,
+    CPR_ENOBUFS,
+    CPR_EISCONN,
+    CPR_ENOTCONN,
+    CPR_ECLOSED,
+    CPR_UNKNOWN_ERR,            /* empty 136 */
+    CPR_UNKNOWN_ERR,            /* empty 137 */
+    CPR_UNKNOWN_ERR,            /* empty 138 */
+    CPR_UNKNOWN_ERR,            /* empty 139 */
+    CPR_UNKNOWN_ERR,            /* empty 140 */
+    CPR_UNKNOWN_ERR,            /* empty 141 */
+    CPR_UNKNOWN_ERR,            /* empty 142 */
+    CPR_ESHUTDOWN,
+    CPR_ETOOMANYREFS,
+    CPR_ETIMEDOUT,
+    CPR_ECONNREFUSED,
+    CPR_EHOSTDOWN,
+    CPR_EHOSTUNREACH,
+    CPR_EALREADY,
+    CPR_EINPROGRESS,
+    CPR_ESTALE
+};
+
+/**
+ *
+ * @brief Translates to "cpr_errno" Macro
+ *
+ * pSIPCC uses the cpr_errno macro to print the errno
+ * for error conditions. This function is used to map the standard
+ * errno to standard CPR errors
+ *
+ * @return The CPR error number
+ *
+ */
+int16_t
+cprTranslateErrno (void)
+{
+    int16_t e = (int16_t) errno;
+
+    /*
+     * Verify against MIN and MAX errno numbers
+     */
+    if ((e < 1) || (e > 151)) {
+        return CPR_UNKNOWN_ERR;
+    } else if (e >= 120) {
+        e = e - 20;
+    } else if (e >= 100) {
+        /*
+         * In the gap from 100 to 119
+         */
+        return CPR_UNKNOWN_ERR;
+    }
+    return (int16_t) errno_table[e - 1];
+}
+
+/**
+  * @}
+  */
diff --git a/libs/sipcc/cpr/linux/cpr_linux_errno.h b/libs/sipcc/cpr/linux/cpr_linux_errno.h
new file mode 100644 (file)
index 0000000..213cb02
--- /dev/null
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_ERRNO_H_
+#define _CPR_LINUX_ERRNO_H_
+
+#include <errno.h>
+
+/*
+ * Maintain re-entrant nature by wrapping 'errno'
+ */
+/** @def cpr_errno is used by pSIPCC. MUST be defined by the CPR layer.
+  */
+#define cpr_errno cprTranslateErrno()
+
+int16_t cprTranslateErrno(void);
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_in.h b/libs/sipcc/cpr/linux/cpr_linux_in.h
new file mode 100644 (file)
index 0000000..4bcd21f
--- /dev/null
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_IN_H_
+#define _CPR_LINUX_IN_H_
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_init.c b/libs/sipcc/cpr/linux/cpr_linux_init.c
new file mode 100644 (file)
index 0000000..00b1a32
--- /dev/null
@@ -0,0 +1,214 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *  @section intro_sec Introduction
+ *  CPR is an OS-abstraction layer which provides the functionality set of an OS
+ *  while hiding all the operational details from the applications. CPR also
+ *  provides additional functionality when certain operating-systems prove
+ *  deficient providing a consistent set of functionality for the applications
+ *  to use. The pSipCC library uses the CPR routines to achieve portablity. It
+ *  is the responsibility of any vendor requiring to port pSipCC into their
+ *  platforms to have the correct implementation of the CPR layer.
+ *
+ *  The CPR delivery includes two "archives/binary" for memory and string
+ *  related operations. These binaries contain functions that the pSIPCC uses
+ *  for it's functionality. The header files that contain the external functions
+ *  (APIs) from these binaries are also distributed.
+ *
+ *  @section sub_sec Functionality
+ *  CPR consists of a number of functional subsystems to achieve OS abstraction. The main
+ *  functionality provided by CPR to the pSIPCC library are
+ *  @li Memory Management - Provided as a binary that needs to be linked into CPR
+ *  @li Timers
+ *  @li Inter Process Communication - Sockets, Message Queues, Mutex, Semaphores
+ *  @li String Handling - Provided as a binary that needs to be linked into CPR
+ *  @li Thread Abstraction
+ *  @li Other Platform/OS Abstractions
+ *  @li Debug/Logging Abstraction
+ *
+ *  @section file_list EXTERNAL APIS
+ *   The External APIs that need to be exposed by CPR to the pSIPCC application are
+ *   defined in the following header files. The documentation within
+ *   each header file lists the functions/macros that @b NEED to be defined for
+ *   pSIPCC to work correctly. Example functions (and an implementation for
+ *   Linux) is available for most functions defined in these headers. Look under
+ *   the "Modules" tab to find the various subsystems and associated APIs and
+ *   helper functions.
+ *   @li cpr_debug.h
+ *   @li cpr_errno.h
+ *   @li cpr_in.h
+ *   @li cpr_locks.h
+ *   @li cpr_rand.h
+ *   @li cpr_socket.h
+ *   @li cpr_stdio.h
+ *   @li cpr_threads.h
+ *   @li cpr_timers.h
+ *   @li cpr.h
+ *   @li cpr_ipc.h
+ *   @li cpr_stddef.h
+ *   @li cpr_time.h
+ *   @li cpr_types.h
+ *
+ *   The function prototypes in these header files are implemented in the
+ *   binaries related to memory and string functionality. The prototypes are
+ *   given so that vendors can use these functions for implementation of other
+ *   CPR parts.
+ *   @li cpr_memory.h
+ *   @li cpr_stdlib.h
+ *   @li cpr_string.h
+ *   @li cpr_strings.h
+ *
+ *  @section standard_headers Standard Header Files
+ *  The pSIPCC component expects some standard header files and associated
+ *  functionality to be present in the native operating system of the vendors
+ *  porting it. These header files contain some basic functionality that pSIPCC
+ *  uses for proper operation. A list of the standard headers (from a standard
+ *  Linux distribution) are -
+ *   @li <assert.h>
+ *   @li <errno.h>
+ *   @li <arpa/inet.h>
+ *   @li <netinet/in.h>
+ *   @li <netinet/tcp.h>
+ *   @li <pthread.h>
+ *   @li <sys/socket.h>
+ *   @li <sys/select.h>
+ *   @li <sys/un.h>
+ *   @li <unistd.h>
+ *   @li <stdarg.h>
+ *   @li <stdio.h>
+ *   @li <stdlib.h>
+ *   @li <string.h>
+ *   @li <ctype.h>
+ *   @li <strings.h>
+ *   @li <time.h>
+ *
+ *
+ *  @section Comp Compiling CPR
+ *  A sample makefile is also provided with the Linux distribution. Running
+ *  "make" generates a CPR executable called "cprtest". This sample makefile can
+ *  be modified to generate a shared library if required. Modify the CC, STDINC,
+ *  STDLIB paths to point to the correct compiler and header files in the
+ *  environment where this is being built.
+ *  @note The README file shows the exact commands required to build/test the CPR
+ *  functionality.
+ *
+ *  @file cpr_linux_init.c
+ *  @brief This file contains CPR initialization routines
+ *
+ *  DESCRIPTION
+ *     Initialization routine for the Cisco Portable Runtime layer
+ *     running in the Linux operating System.
+ */
+
+/**
+  * @addtogroup Initialization The initialization module
+  * @ingroup CPR
+  * @brief The intialization module consists of APIs used to initialize or destroy the CPR layer by pSipCC
+  *
+  * @{
+  */
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_timers.h"
+#include "cpr_linux_locks.h"
+#include "cpr_linux_timers.h"
+#include "plat_api.h"
+#include <errno.h>
+#include <sys/msg.h>
+#include <sys/ipc.h>
+#include "plat_debug.h"
+
+/**
+  * Mutex to manage message queue list.
+  */
+extern pthread_mutex_t msgQueueListMutex;
+
+/**
+  * Boolean to check that cprPreInit been called
+  */
+static boolean pre_init_called = FALSE;
+
+/**
+ * cprInfo prints out informational messages that are
+ * not necessarily errors, but maybe of interest to
+ * someone debugging a problem. Examples of this are
+ * requesting a msg from a msg queue that is empty,
+ * stopping a timer that is not running, etc...
+ */
+int32_t cprInfo = TRUE;
+
+
+/**
+ * cprPreInit
+ *
+ * @brief The cprPreInit function IS called from pSIPCC @b before any components are initialized.
+ *
+ * This function @b SHOULD initialize those portions of the CPR that
+ * are needed before applications can start using it. The memory subsystem
+ * (sandbox) is initialized from this routine.
+ *
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ * @note pSIPCC will NOT continue and stop initialization if the return value is CPR_FAILURE.
+ */
+cprRC_t
+cprPreInit (void)
+{
+    static const char fname[] = "cprPreInit";
+    int32_t returnCode;
+
+    /*
+     * Make function reentreant
+     */
+    if (pre_init_called == TRUE) {
+        return CPR_SUCCESS;
+    }
+    pre_init_called = TRUE;
+    /*
+     * Create message queue list mutex
+     */
+    returnCode = pthread_mutex_init(&msgQueueListMutex, NULL);
+    if (returnCode != 0) {
+        CPR_ERROR("%s: MsgQueue Mutex init failure %d\n", fname, returnCode);
+        return CPR_FAILURE;
+    }
+
+    returnCode = cpr_timer_pre_init();
+    if (returnCode != 0) {
+        CPR_ERROR("%s: timer pre init failed %d\n", fname, returnCode);
+        return CPR_FAILURE;
+    }
+
+
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * cprPostInit
+ *
+ * @brief The cprPostInit function IS called from pSIPCC @b after all the components are initialized.
+ *
+ * This function @b SHOULD complete any CPR activities before the phone is
+ * operational. In other words a call to this function will be the last line of
+ * the phone initializtion routine from pSIPCC. This function implementation
+ * ties in a couple of debug commands to get more information on the CPR from
+ * the shell.
+ *
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ * @note pSIPCC will NOT continue and stop initialization if the return value is CPR_FAILURE.
+ */
+cprRC_t
+cprPostInit (void)
+{
+/*    if (cpr_memory_mgmt_post_init() not_eq TRUE) {
+        return CPR_FAILURE;
+    }
+*/
+    return CPR_SUCCESS;
+}
+
diff --git a/libs/sipcc/cpr/linux/cpr_linux_ipc.c b/libs/sipcc/cpr/linux/cpr_linux_ipc.c
new file mode 100644 (file)
index 0000000..e02207a
--- /dev/null
@@ -0,0 +1,948 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *  @brief CPR layer for interprocess communication
+ *
+ * The name of this file may be overly broad, rather this file deals
+ * with IPC via message queues.  A user may create, destroy and
+ * associate a thread with a message queue.  Once established, messages
+ * can be delivered and retrieved.
+ *
+ * The send/get APIs attempt to reliably deliver messages even when
+ * under stress.  Two mechanisms have been added to deal with a full
+ * message queue.  First, the message queue size may be extended to
+ * allow more messages to be handled than supported by an OS.
+ * Second, if the queue is indeed full a sleep-and-retry
+ * method is used to force a context-switch to allow for other threads
+ * to run in hope of clearing some messages off of the queue.  The
+ * latter method is always-on by default.  The former method must be
+ * enabled by extending the message queue by some size greater than
+ * zero (0).
+ *
+ * @defgroup IPC The Inter Process Communication module
+ * @ingroup CPR
+ * @brief The module related to IPC abstraction for the pSIPCC
+ * @addtogroup MsgQIPCAPIs The Message Queue IPC APIs
+ * @ingroup IPC
+ * @brief APIs expected by pSIPCC for using message queues
+ *
+ * @{
+ *
+ *
+ */
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include <cpr_stdio.h>
+#include <errno.h>
+#include <sys/msg.h>
+#include <sys/ipc.h>
+#include "plat_api.h"
+#include "CSFLog.h"
+
+static const char *logTag = "cpr_linux_ipc";
+
+#define STATIC static
+
+/* @def The Message Queue depth */
+#define OS_MSGTQL 31
+
+/*
+ * Internal CPR API
+ */
+extern pthread_t cprGetThreadId(cprThread_t thread);
+
+/**
+ * @struct cpr_msgq_node_s
+ * Extended internal message queue node
+ *
+ * A double-linked list holding the necessary message information
+ */
+typedef struct cpr_msgq_node_s
+{
+    struct cpr_msgq_node_s *next;
+    struct cpr_msgq_node_s *prev;
+    void *msg;
+    void *pUserData;
+} cpr_msgq_node_t;
+
+/**
+ * @struct cpr_msg_queue_s
+ * Msg queue information needed to hide OS differences in implementation.
+ * To use msg queues, the application code may pass in a name to the
+ * create function for msg queues. CPR does not use this field, it is
+ * solely for the convenience of the application and to aid in debugging.
+ *
+ * Note: Statistics are not protected by a mutex; therefore, there exists
+ * the possibility that the results may not be accurate.
+ *
+ * Note:if the depth supplied by OS is insufficient,a message queue owner may
+ * increase the message queue depth via cprCreateMessageQueue's depth
+ * parameter where the value can range from MSGTQL to CPR_MAX_MSG_Q_DEPTH.
+ */
+typedef struct cpr_msg_queue_s
+{
+    struct cpr_msg_queue_s *next;
+    const char *name;
+    pthread_t thread;
+    int32_t queueId;
+    uint16_t maxCount;
+    uint16_t currentCount;
+    uint32_t totalCount;
+    uint32_t sendErrors;
+    uint32_t reTries;
+    uint32_t highAttempts;
+    uint32_t selfQErrors;
+    uint16_t extendedQDepth;
+    uint16_t maxExtendedQDepth;
+    pthread_mutex_t mutex;       /* lock for managing extended queue     */
+    cpr_msgq_node_t *head;       /* extended queue head (newest element) */
+    cpr_msgq_node_t *tail;       /* extended queue tail (oldest element) */
+} cpr_msg_queue_t;
+
+/**
+ * @enum cpr_msgq_post_result_e
+ * A enumeration used to report the result of posting a message to
+ * a message queue
+ */
+typedef enum
+{
+    CPR_MSGQ_POST_SUCCESS,
+    CPR_MSGQ_POST_FAILED,
+    CPR_MSGQ_POST_PENDING
+} cpr_msgq_post_result_e;
+
+
+/*
+ * Head of list of message queues
+ */
+static cpr_msg_queue_t *msgQueueList = NULL;
+
+/*
+ * Mutex to manage message queue list
+ */
+pthread_mutex_t msgQueueListMutex;
+
+/*
+ * String to represent message queue name when it is not provided
+ */
+static const char unnamed_string[] = "unnamed";
+
+
+/*
+ * CPR_MAX_MSG_Q_DEPTH
+ *
+ * The maximum queue depth supported by the CPR layer.  This value
+ * is arbitrary though the purpose is to limit the memory usage
+ * by CPR and avoid (nearly) unbounded situations.
+ *
+ * Note: This value should be greater than MSGTQL which is currently
+ *       defined as 31
+ */
+#define CPR_MAX_MSG_Q_DEPTH 256
+
+/*
+ * CPR_SND_TIMEOUT_WAIT_INTERVAL
+ *
+ * The interval of time to wait in milliseconds between attempts to
+ * send a message to the message queue
+ *
+ * Note: 20 ms. to avoid less than a tick wake up since on most
+ *       OSes 10ms is one 1 tick
+ *       this should really be OS_TICK_MS * 2 or OS_TICK_MS + X
+ */
+#define CPR_SND_TIMEOUT_WAIT_INTERVAL 20
+
+/*
+ * CPR_ATTEMPTS_TO_SEND
+ *
+ * The number of attempts made to send a message when the message
+ * would otherwise be blocked.  Note in this condition the thread
+ * will sleep the timeout interval to allow the msg queue to be
+ * drained.
+ *
+ * Note: 25 attempts for upto .5 seconds at the interval of
+ *       CPR_SND_TIMEOUT_WAIT_INTERVAL worst case.
+ */
+#define CPR_ATTEMPTS_TO_SEND 25
+
+/*
+ * Also, important to note that the total timeout interval must be
+ * greater than the SIP's select call timeout value which is 25msec.
+ * This is necessary to cover the case where the SIP message queue
+ * is full and the select timeout occurs.
+ *
+ * Total timeout interval = CPR_SND_TIMEOUT_WAIT_INTERVAL *
+ *                          CPR_ATTEMPTS_TO_SEND;
+ */
+
+
+/*
+ * Prototype declarations
+ */
+static cpr_msgq_post_result_e
+cprPostMessage(cpr_msg_queue_t *msgq, void *msg, void **ppUserData);
+static void
+cprPegSendMessageStats(cpr_msg_queue_t *msgq, uint16_t numAttempts);
+static cpr_msgq_post_result_e
+cprPostExtendedQMsg(cpr_msg_queue_t *msgq, void *msg, void **ppUserData);
+static void
+cprMoveMsgToQueue(cpr_msg_queue_t *msgq);
+
+/*
+ * Functions
+ */
+
+/**
+ * Creates a message queue
+ *
+ * @brief The cprCreateMessageQueue function is called to allow the OS to
+ * perform whatever work is needed to create a message queue.
+
+ * If the name is present, CPR should assign this name to the message queue to assist in
+ * debugging. The message queue depth is the second input parameter and is for
+ * setting the desired queue depth. This parameter may not be supported by all OS.
+ * Its primary intention is to set queue depth beyond the default queue depth
+ * limitation.
+ * On any OS where there is no limit on the message queue depth or
+ * its queue depth is sufficiently large then this parameter is ignored on that
+ * OS.
+ *
+ * @param[in] name  - name of the message queue (optional)
+ * @param[in] depth - the message queue depth, optional field which should
+ *                default if set to zero(0)
+ *
+ * @return Msg queue handle or NULL if init failed, errno should be provided
+ *
+ * @note the actual message queue depth will be bounded by the
+ *       standard system message queue depth and CPR_MAX_MSG_Q_DEPTH.
+ *       If 'depth' is outside of the bounds, the value will be
+ *       reset automatically.
+ */
+cprMsgQueue_t
+cprCreateMessageQueue (const char *name, uint16_t depth)
+{
+    static const char fname[] = "cprCreateMessageQueue";
+    cpr_msg_queue_t *msgq;
+    key_t key;
+    static int key_id = 100; /* arbitrary starting number */
+    struct msqid_ds buf;
+
+    msgq =(cpr_msg_queue_t *)cpr_calloc(1, sizeof(cpr_msg_queue_t));
+    if (msgq == NULL) {
+        CPR_ERROR("%s: Malloc failed: %s\n", fname,
+                  name ? name : unnamed_string);
+        errno = ENOMEM;
+        return NULL;
+    }
+
+    msgq->name = name ? name : unnamed_string;
+
+    /*
+     * Find a unique key
+     */
+    key = ftok("/proc/self", key_id++);
+    CSFLogDebug(logTag, "key = %x\n", key);
+
+    if (key == -1) {
+        CPR_ERROR("%s: Key generation failed: %d\n", fname, errno);
+        cpr_free(msgq);
+        return NULL;
+    }
+
+    /*
+     * Set creation flag so that OS will create the message queue
+     */
+    msgq->queueId = msgget(key, (IPC_EXCL | IPC_CREAT | 0666));
+    if (msgq->queueId == -1) {
+        if (errno == EEXIST) {
+            CSFLogDebug(logTag, "Q exists so first remove it and then create again\n");
+                /* Remove message queue */
+            msgq->queueId = msgget(key, (IPC_CREAT | 0666));
+            if (msgctl(msgq->queueId, IPC_RMID, &buf) == -1) {
+
+                CPR_ERROR("%s: Destruction failed: %s: %d\n", fname,
+                          msgq->name, errno);
+
+                return NULL;
+            }
+            msgq->queueId = msgget(key, (IPC_CREAT | 0666));
+        }
+    } else {
+        CSFLogDebug(logTag, "there was no preexisting q..\n");
+
+    }
+
+
+
+    if (msgq->queueId == -1) {
+        CPR_ERROR("%s: Creation failed: %s: %d\n", fname, name, errno);
+        if (errno == EEXIST) {
+
+        }
+
+        cpr_free(msgq);
+        return NULL;
+    }
+    CSFLogDebug(logTag, "create message q with id=%x\n", msgq->queueId);
+
+    /* flush the q before ?? */
+
+    /*
+     * Create mutex for extended (overflow) queue
+     */
+    if (pthread_mutex_init(&msgq->mutex, NULL) != 0) {
+        CPR_ERROR("%s: Failed to create msg queue (%s) mutex: %d\n",
+                  fname, name, errno);
+        (void) msgctl(msgq->queueId, IPC_RMID, &buf);
+        cpr_free(msgq);
+        return NULL;
+    }
+
+    /*
+     * Set the extended message queue depth (within bounds)
+     */
+    if (depth > CPR_MAX_MSG_Q_DEPTH) {
+        CPR_INFO("%s: Depth too large (%d) reset to %d\n", fname, depth,
+                 CPR_MAX_MSG_Q_DEPTH);
+        depth = CPR_MAX_MSG_Q_DEPTH;
+    }
+
+    if (depth < OS_MSGTQL) {
+        if (depth) {
+            CPR_INFO("%s: Depth too small (%d) reset to %d\n", fname, depth, OS_MSGTQL);
+        }
+        depth = OS_MSGTQL;
+    }
+    msgq->maxExtendedQDepth = depth - OS_MSGTQL;
+
+    /*
+     * Add message queue to list for statistics reporting
+     */
+    pthread_mutex_lock(&msgQueueListMutex);
+    msgq->next = msgQueueList;
+    msgQueueList = msgq;
+    pthread_mutex_unlock(&msgQueueListMutex);
+
+    return msgq;
+}
+
+
+/**
+  * cprDestroyMessageQueue
+ * @brief Removes all messages from the queue and then destroy the message queue
+ *
+ * The cprDestroyMessageQueue function is called to destroy a message queue. The
+ * function drains any messages from the queue and the frees the
+ * message queue. Any messages on the queue are to be deleted, and not sent to the intended
+ * recipient. It is the application's responsibility to ensure that no threads are
+ * blocked on a message queue when it is destroyed.
+ *
+ * @param[in] msgQueue - message queue to destroy
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE, errno should be provided in this case
+ */
+cprRC_t
+cprDestroyMessageQueue (cprMsgQueue_t msgQueue)
+{
+    static const char fname[] = "cprDestroyMessageQueue";
+    cpr_msg_queue_t *msgq;
+    void *msg;
+    struct msqid_ds buf;
+    CSFLogDebug(logTag, "Destroy message Q called..\n");
+
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+    if (msgq == NULL) {
+        /* Bad application! */
+        CPR_ERROR("%s: Invalid input\n", fname);
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    /* Drain message queue */
+    msg = cprGetMessage(msgQueue, FALSE, NULL);
+    while (msg != NULL) {
+        cpr_free(msg);
+        msg = cprGetMessage(msgQueue, FALSE, NULL);
+    }
+
+    /* Remove message queue from list */
+    pthread_mutex_lock(&msgQueueListMutex);
+    if (msgq == msgQueueList) {
+        msgQueueList = msgq->next;
+    } else {
+        cpr_msg_queue_t *msgql = msgQueueList;
+
+        while ((msgql->next != NULL) && (msgql->next != msgq)) {
+            msgql = msgql->next;
+        }
+        if (msgql->next == msgq) {
+            msgql->next = msgq->next;
+        }
+    }
+    pthread_mutex_unlock(&msgQueueListMutex);
+
+    /* Remove message queue */
+    if (msgctl(msgq->queueId, IPC_RMID, &buf) == -1) {
+        CPR_ERROR("%s: Destruction failed: %s: %d\n", fname,
+                  msgq->name, errno);
+        return CPR_FAILURE;
+    }
+
+    /* Remove message queue mutex */
+    if (pthread_mutex_destroy(&msgq->mutex) != 0) {
+        CPR_ERROR("%s: Failed to destroy msg queue (%s) mutex: %d\n",
+                  fname, msgq->name, errno);
+    }
+
+    cpr_free(msgq);
+    return CPR_SUCCESS;
+}
+
+
+/**
+  * cprSetMessageQueueThread
+ * @brief Associate a thread with the message queue
+ *
+ * This method is used by pSIPCC to associate a thread and a message queue.
+ * @param[in] msgQueue  - msg queue to set
+ * @param[in] thread    - CPR thread to associate with queue
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ *
+ * @note Nothing is done to prevent overwriting the thread ID
+ *       when the value has already been set.
+ */
+cprRC_t
+cprSetMessageQueueThread (cprMsgQueue_t msgQueue, cprThread_t thread)
+{
+    static const char fname[] = "cprSetMessageQueueThread";
+    cpr_msg_queue_t *msgq;
+
+    if ((!msgQueue) || (!thread)) {
+        CPR_ERROR("%s: Invalid input\n", fname);
+        return CPR_FAILURE;
+    }
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+    if (msgq->thread != 0) {
+        CPR_ERROR("%s: over-writing previously msgq thread name for %s",
+                  fname, msgq->name);
+    }
+
+    msgq->thread = cprGetThreadId(thread);
+    return CPR_SUCCESS;
+}
+
+/**
+  * cprGetMessage
+ * @brief Retrieve a message from a particular message queue
+ *
+ * The cprGetMessage function retrieves the first message from the message queue
+ * specified and returns a void pointer to that message.
+ *
+ * @param[in]  msgQueue    - msg queue from which to retrieve the message. This
+ * is the handle returned from cprCreateMessageQueue.
+ * @param[in]  waitForever - boolean to either wait forever (TRUE) or not
+ *                           wait at all (FALSE) if the msg queue is empty.
+ * @param[out] ppUserData  - pointer to a pointer to user defined data. This
+ * will be NULL if no user data was present.
+ *
+ * @return Retrieved message buffer or NULL if failure occurred or
+ *         the waitForever flag was set to false and no messages were
+ *         on the queue.
+ *
+ * @note   If ppUserData is defined, the value will be initialized to NULL
+ */
+void *
+cprGetMessage (cprMsgQueue_t msgQueue, boolean waitForever, void **ppUserData)
+{
+    static const char fname[] = "cprGetMessage";
+    struct msgbuffer rcvBuffer = { 0 };
+    struct msgbuffer *rcvMsg = &rcvBuffer;
+    void *buffer;
+    int msgrcvflags;
+    cpr_msg_queue_t *msgq;
+
+    /* Initialize ppUserData */
+    if (ppUserData) {
+        *ppUserData = NULL;
+    }
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+    if (msgq == NULL) {
+        /* Bad application! */
+        CPR_ERROR("%s: Invalid input\n", fname);
+        errno = EINVAL;
+        return NULL;
+    }
+
+    /*
+     * If waitForever is set, block on the message queue
+     * until a message is received.
+     */
+    if (waitForever) {
+        msgrcvflags = 0;
+    } else {
+        msgrcvflags = IPC_NOWAIT;
+    }
+
+    if (msgrcv(msgq->queueId, rcvMsg,
+        sizeof(struct msgbuffer) - offsetof(struct msgbuffer, msgPtr),
+        0, msgrcvflags) == -1) {
+       if (!waitForever && errno == ENOMSG) {
+               CPR_INFO("%s: no message on queue %s (non-blocking receive "
+                         " operation), returning\n", fname, msgq->name);
+       } else {
+               CPR_ERROR("%s: msgrcv for queue %s failed: %d\n",
+                              fname, msgq->name, errno);
+        }
+        return NULL;
+    }
+    CPR_INFO("%s: msgrcv success for queue %s \n",fname, msgq->name);
+
+    (void) pthread_mutex_lock(&msgq->mutex);
+    /* Update statistics */
+    msgq->currentCount--;
+    (void) pthread_mutex_unlock(&msgq->mutex);
+
+    /*
+     * Pull out the data
+     */
+    if (ppUserData) {
+        *ppUserData = rcvMsg->usrPtr;
+    }
+    buffer = rcvMsg->msgPtr;
+
+    /*
+     * If there are messages on the extended queue, attempt to
+     * push a message back onto the real system queue
+     */
+    if (msgq->extendedQDepth) {
+        cprMoveMsgToQueue(msgq);
+    }
+
+    return buffer;
+}
+
+
+/**
+  * cprSendMessage
+ * @brief Place a message on a particular queue.  Note that caller may
+ * block (see comments below)
+ *
+ * @param[in] msgQueue   - msg queue on which to place the message
+ * @param[in] msg        - pointer to the msg to place on the queue
+ * @param[in] ppUserData - pointer to a pointer to user defined data
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE, errno should be provided
+ *
+ * @note 1. Messages queues are set to be non-blocking, those cases
+ *       where the system call fails with a would-block error code
+ *       (EAGAIN) the function will attempt other mechanisms described
+ *       below.
+ * @note 2. If enabled with an extended message queue, either via a
+ *       call to cprCreateMessageQueue with depth value or a call to
+ *       cprSetExtendMessageQueueDepth() (when unit testing), the message
+ *       will be added to the extended message queue and the call will
+ *       return successfully.  When room becomes available on the
+ *       system's message queue, those messages will be added.
+ * @note 3. If the message queue becomes full and no space is availabe
+ *       on the extended message queue, then the function will attempt
+ *       to resend the message up to CPR_ATTEMPTS_TO_SEND and the
+ *       calling thread will *BLOCK* CPR_SND_TIMEOUT_WAIT_INTERVAL
+ *       milliseconds after each failed attempt.  If unsuccessful
+ *       after all attempts then EGAIN error code is returned.
+ * @note 4. This applies to all CPR threads, including the timer thread.
+ *       So it is possible that the timer thread would be forced to
+ *       sleep which would have the effect of delaying all active
+ *       timers.  The work to fix this rare situation is not considered
+ *       worth the effort to fix....so just leaving as is.
+ */
+cprRC_t
+cprSendMessage (cprMsgQueue_t msgQueue, void *msg, void **ppUserData)
+{
+    static const char fname[] = "cprSendMessage";
+    static const char error_str[] = "%s: Msg not sent to %s queue: %s\n";
+    cpr_msgq_post_result_e rc;
+    cpr_msg_queue_t *msgq;
+    int16_t attemptsToSend = CPR_ATTEMPTS_TO_SEND;
+    uint16_t numAttempts   = 0;
+
+    /* Bad application? */
+    if (msgQueue == NULL) {
+        CPR_ERROR(error_str, fname, "undefined", "invalid input");
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    msgq = (cpr_msg_queue_t *) msgQueue;
+
+    /*
+     * Attempt to send message
+     */
+    do {
+        (void) pthread_mutex_lock(&msgq->mutex);
+
+        /*
+         * If in a queue overflow condition, post message to the
+         * extended queue; otherwise, post to normal message queue
+         */
+        if (msgq->extendedQDepth) {
+            /*
+             * Check if extended queue is full, if not then
+             * attempt to add the message.
+             */
+            if (msgq->extendedQDepth < msgq->maxExtendedQDepth) {
+                rc = cprPostExtendedQMsg(msgq, msg, ppUserData);
+                // do under lock to avoid races
+                if (rc == CPR_MSGQ_POST_SUCCESS) {
+                    cprPegSendMessageStats(msgq, numAttempts);
+                } else {
+                    msgq->sendErrors++;
+                }
+                (void) pthread_mutex_unlock(&msgq->mutex);
+
+                if (rc == CPR_MSGQ_POST_SUCCESS) {
+                    return CPR_SUCCESS;
+                }
+                else
+                {
+                    CPR_ERROR(error_str, fname, msgq->name, "no memory");
+                    return CPR_FAILURE;
+                }
+            }
+
+            /*
+             * Even the extended message queue is full, so
+             * release the message queue mutex and use the
+             * re-try procedure.
+             */
+            (void) pthread_mutex_unlock(&msgq->mutex);
+
+            /*
+             * If attempting to post to the calling thread's
+             * own message queue, the re-try procedure will
+             * not work.  No options left...fail with an error.
+             */
+            if (pthread_self() == msgq->thread) {
+                msgq->selfQErrors++;
+                msgq->sendErrors++;
+                CPR_ERROR(error_str, fname, msgq->name, "FULL");
+                return CPR_FAILURE;
+            }
+        } else {
+            /*
+             * Normal posting of message
+             */
+            rc = cprPostMessage(msgq, msg, ppUserData);
+
+            /*
+             * Before releasing the mutex, check if the
+             * return code is 'pending' which means the
+             * system message queue is full
+             */
+            if (rc == CPR_MSGQ_POST_PENDING) {
+                /*
+                 * If the message queue has enabled the extended queue
+                 * support, then attempt to add to the extended queue.
+                 */
+                if (msgq->maxExtendedQDepth) {
+                    rc = cprPostExtendedQMsg(msgq, msg, ppUserData);
+                }
+            }
+
+            (void) pthread_mutex_unlock(&msgq->mutex);
+
+            if (rc == CPR_MSGQ_POST_SUCCESS) {
+                cprPegSendMessageStats(msgq, numAttempts);
+                return CPR_SUCCESS;
+            } else if (rc == CPR_MSGQ_POST_FAILED) {
+                CPR_ERROR("%s: Msg not sent to %s queue: %d\n",
+                          fname, msgq->name, errno);
+                msgq->sendErrors++;
+                /*
+                 * If posting to calling thread's own queue,
+                 * then peg the self queue error.
+                 */
+                if (pthread_self() == msgq->thread) {
+                    msgq->selfQErrors++;
+                }
+
+                return CPR_FAILURE;
+            }
+            /*
+             * Else pending due to a full message queue
+             * and the extended queue has not been enabled,
+             * so just use the re-try attempts.
+             */
+        }
+
+        /*
+         * Did not succeed in sending the message, so continue
+         * to attempt up to the CPR_ATTEMPTS_TO_SEND.
+         */
+        attemptsToSend--;
+        if (attemptsToSend > 0) {
+            /*
+             * Force a context-switch of the thread attempting to
+             * send the message, in order to help the case where
+             * the msg queue is full and the owning thread may get
+             * a a chance be scheduled so it can drain it (Note:
+             * no guarantees, more of a "last-ditch effort" to
+             * recover...especially when temporarily over-whelmed).
+             */
+            cprSleep(CPR_SND_TIMEOUT_WAIT_INTERVAL);
+            msgq->reTries++;
+            numAttempts++;
+        }
+    } while (attemptsToSend > 0);
+
+    CPR_ERROR(error_str, fname, msgq->name, "FULL");
+    msgq->sendErrors++;
+    return CPR_FAILURE;
+}
+
+/**
+ * @}
+ * @addtogroup MsgQIPCHelper Internal Helper functions for MsgQ
+ * @ingroup IPC
+ * @brief Helper functions used by CPR to implement the Message Queue IPC APIs
+ * @{
+ */
+
+/**
+ * cprPegSendMessageStats
+ * @brief Peg the statistics for successfully posting a message
+ *
+ * @param[in] msgq        - message queue
+ * @param[in] numAttempts - number of attempts to post message to message queue
+ *
+ * @return none
+ *
+ * @pre (msgq != NULL)
+ */
+static void
+cprPegSendMessageStats (cpr_msg_queue_t *msgq, uint16_t numAttempts)
+{
+    /*
+     * Collect statistics
+     */
+    msgq->totalCount++;
+    if (msgq->currentCount > msgq->maxCount) {
+        msgq->maxCount = msgq->currentCount;
+    }
+
+    if (numAttempts > msgq->highAttempts) {
+        msgq->highAttempts = numAttempts;
+    }
+}
+
+/**
+ * cprPostMessage
+ * @brief Post message to system message queue
+ *
+ * @param[in] msgq       - message queue
+ * @param[in] msg        - message to post
+ * @param[in] ppUserData - ptr to ptr to option user data
+ *
+ * @return the post result which is CPR_MSGQ_POST_SUCCESS,
+ *         CPR_MSGQ_POST_FAILURE or CPR_MSGQ_POST_PENDING
+ *
+ * @pre (msgq != NULL)
+ * @pre (msg != NULL)
+ */
+static cpr_msgq_post_result_e
+cprPostMessage (cpr_msg_queue_t *msgq, void *msg, void **ppUserData)
+{
+    struct msgbuffer mbuf;
+
+    /*
+     * Put msg user wants to send into a CNU msg buffer
+     * Copy the address of the msg buffer into the mtext
+     * portion of the message.
+     */
+    mbuf.mtype = CPR_IPC_MSG;
+    mbuf.msgPtr = msg;
+
+    if (ppUserData != NULL) {
+        mbuf.usrPtr = *ppUserData;
+    } else {
+        mbuf.usrPtr = NULL;
+    }
+
+    /*
+     * Send message buffer
+     */
+    if (msgsnd(msgq->queueId, &mbuf,
+                sizeof(struct msgbuffer) - offsetof(struct msgbuffer, msgPtr),
+               IPC_NOWAIT) != -1) {
+        msgq->currentCount++;
+        return CPR_MSGQ_POST_SUCCESS;
+    }
+
+    /*
+     * If msgsnd system call would block, handle separately;
+     * otherwise a real system error.
+     */
+    if (errno == EAGAIN) {
+        return CPR_MSGQ_POST_PENDING;
+    }
+
+    return CPR_MSGQ_POST_FAILED;
+}
+
+/**
+ * cprPostExtendedQMsg
+ * @brief Post message to internal extended message queue
+ *
+ * @param[in] msgq       - message queue
+ * @param[in] msg        - message to post
+ * @param[in] ppUserData - ptr to ptr to option user data
+ *
+ * @return the post result which is CPR_MSGQ_POST_SUCCESS or
+ *         CPR_MSGQ_POST_FAILURE if no memory available
+ *
+ * @pre (msgq != NULL)
+ * @pre (msg != NULL)
+ * @pre (msgq->mutex has been locked)
+ * @pre (msgq->extendedQDepth < msgq->maxExtendedQDepth)
+ *
+ * @todo Could use cpr_chunk_malloc to pre-allocate all of the nodes
+ *       but that does have the consequence of allocating memory that
+ *       may not be necessary
+ */
+static cpr_msgq_post_result_e
+cprPostExtendedQMsg (cpr_msg_queue_t *msgq, void *msg, void **ppUserData)
+{
+    cpr_msgq_node_t *node;
+
+    /*
+     * Allocate new message queue node
+     */
+    node = cpr_malloc(sizeof(*node));
+    if (!node) {
+        errno = ENOMEM;
+        return CPR_MSGQ_POST_FAILED;
+    }
+
+    /*
+     * Fill in data
+     */
+    node->msg = msg;
+    if (ppUserData != NULL) {
+        node->pUserData = *ppUserData;
+    } else {
+        node->pUserData = NULL;
+    }
+
+    /*
+     * Push onto list
+     */
+    node->prev = NULL;
+    node->next = msgq->head;
+    msgq->head = node;
+
+    if (node->next) {
+        node->next->prev = node;
+    }
+
+    if (msgq->tail == NULL) {
+        msgq->tail = node;
+    }
+    msgq->extendedQDepth++;
+    msgq->currentCount++;
+
+    return CPR_MSGQ_POST_SUCCESS;
+}
+
+
+/**
+ * cprMoveMsgToQueue
+ * @brief Move message from extended internal queue to system message queue
+ *
+ * @param[in] msgq - the message queue
+ *
+ * @return none
+ *
+ * @pre (msgq != NULL)
+ * @pre (msgq->extendedQDepth > 0)
+ */
+static void
+cprMoveMsgToQueue (cpr_msg_queue_t *msgq)
+{
+    static const char *fname = "cprMoveMsgToQueue";
+    cpr_msgq_post_result_e rc;
+    cpr_msgq_node_t *node;
+
+    (void) pthread_mutex_lock(&msgq->mutex);
+
+    if (!msgq->tail) {
+        /* the linked list is bad...ignore it */
+        CPR_ERROR("%s: MsgQ (%s) list is corrupt", fname, msgq->name);
+        (void) pthread_mutex_unlock(&msgq->mutex);
+        return;
+    }
+
+    node = msgq->tail;
+
+    rc = cprPostMessage(msgq, node->msg, &node->pUserData);
+    if (rc == CPR_MSGQ_POST_SUCCESS) {
+        /*
+         * Remove node from extended list
+         */
+        msgq->tail = node->prev;
+        if (msgq->tail) {
+            msgq->tail->next = NULL;
+        }
+        if (msgq->head == node) {
+            msgq->head = NULL;
+        }
+        msgq->extendedQDepth--;
+        /*
+         * Fix increase in the current count which was incremented
+         * in cprPostMessage but not really an addition.
+         */
+        msgq->currentCount--;
+    }
+
+    (void) pthread_mutex_unlock(&msgq->mutex);
+
+    if (rc == CPR_MSGQ_POST_SUCCESS) {
+        cpr_free(node);
+    } else {
+        CPR_ERROR("%s: Failed to repost msg on %s queue: %d\n",
+                  fname, msgq->name, errno);
+    }
+}
+
+/**
+  * @}
+  *
+  * @addtogroup MsgQIPCAPIs The Message Queue IPC APIs
+  * @{
+  */
+
+/**
+ * cprGetDepth
+ *
+ * @brief get depth of a message queue
+ *
+ * The pSIPCC uses this API to look at the depth of a message queue for internal
+ * routing and throttling decision
+ *
+ * @param[in] msgQueue - message queue
+ *
+ * @return depth of msgQueue
+ *
+ * @pre (msgQueue != NULL)
+ */
+uint16_t cprGetDepth (cprMsgQueue_t msgQueue)
+{
+        cpr_msg_queue_t *msgq;
+        msgq = (cpr_msg_queue_t *) msgQueue;
+        return msgq->currentCount;
+}
+
diff --git a/libs/sipcc/cpr/linux/cpr_linux_ipc.h b/libs/sipcc/cpr/linux/cpr_linux_ipc.h
new file mode 100644 (file)
index 0000000..a735f07
--- /dev/null
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_CNU_IPC_H_
+#define _CPR_CNU_IPC_H_
+
+#include "cpr_threads.h"
+#include <pthread.h>
+
+/* Enable support for cprSetMessageQueueThread API */
+#define CPR_USE_SET_MESSAGE_QUEUE_THREAD
+
+/* Maximum message size allowed by CNU */
+#define CPR_MAX_MSG_SIZE  8192
+
+/* Our CNU msgtype */
+#define CPR_IPC_MSG 1
+
+
+/* Message buffer layout */
+struct msgbuffer {
+    long    mtype;    /* Message type */
+    void   *msgPtr;   /* Ptr to msg */
+    void   *usrPtr;   /* Ptr to user data */
+};
+
+/* For gathering statistics regarding message queues */
+typedef struct {
+    char name[16];
+    uint16_t maxCount;
+    uint16_t currentCount;
+    uint32_t totalCount;
+    uint32_t rcvTimeouts;
+    uint32_t sendErrors;
+    uint32_t reTries;
+    uint32_t highAttempts;
+    uint32_t selfQErrors;
+    uint16_t extendedDepth;
+} cprMsgQueueStats_t;
+
+
+/*
+ * Mutex for updating the message queue list
+ */
+extern pthread_mutex_t msgQueueListMutex;
+
+
+/**
+ * cprGetDepth
+ *
+ * Get depth of a message queue
+ */
+uint16_t cprGetDepth(cprMsgQueue_t msgQueue);
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_locks.c b/libs/sipcc/cpr/linux/cpr_linux_locks.c
new file mode 100644 (file)
index 0000000..b945d1c
--- /dev/null
@@ -0,0 +1,209 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include <errno.h>
+#include <pthread.h>
+#include <sys/time.h>
+
+/**
+  * @defgroup MutexIPCAPIs The Mutex/Semaphore IPC APIs
+  * @ingroup IPC
+  * @brief The module related to Mutex/Sempahore abstraction for the pSIPCC
+  * @{
+  */
+
+
+/**
+ * cprCreateMutex
+ *
+ * @brief Creates a mutual exclusion block
+ *
+ * The cprCreateMutex function is called to allow the OS to perform whatever
+ * work is needed to create a mutex.
+ *
+ * @param[in] name  - name of the mutex. If present, CPR assigns this name to
+ * the mutex to assist in debugging.
+ *
+ * @return Mutex handle or NULL if creation failed. If NULL, set errno
+ */
+cprMutex_t
+cprCreateMutex (const char *name)
+{
+    static const char fname[] = "cprCreateMutex";
+    static uint16_t id = 0;
+    int32_t returnCode;
+    cpr_mutex_t *cprMutexPtr;
+    pthread_mutex_t *pthreadMutexPtr;
+
+    /*
+     * Malloc memory for a new mutex. CPR has its' own
+     * set of mutexes so malloc one for the generic
+     * CPR view and one for the CNU specific version.
+     */
+    cprMutexPtr = (cpr_mutex_t *) cpr_malloc(sizeof(cpr_mutex_t));
+    pthreadMutexPtr = (pthread_mutex_t *) cpr_malloc(sizeof(pthread_mutex_t));
+    if ((cprMutexPtr != NULL) && (pthreadMutexPtr != NULL)) {
+        /* Assign name */
+        cprMutexPtr->name = name;
+
+        /*
+         * Use default mutex attributes. TBD: if we do not
+         * need cnuMutexAttributes global get rid of it
+         */
+        returnCode = pthread_mutex_init(pthreadMutexPtr, NULL);
+        if (returnCode != 0) {
+            CPR_ERROR("%s - Failure trying to init Mutex %s: %d\n",
+                      fname, name, returnCode);
+            cpr_free(pthreadMutexPtr);
+            cpr_free(cprMutexPtr);
+            return (cprMutex_t)NULL;
+        }
+
+        /*
+         * TODO - It would be nice for CPR to keep a linked
+         * list of active mutexes for debugging purposes
+         * such as a show command or walking the list to ensure
+         * that an application does not attempt to create
+         * the same mutex twice.
+         */
+        cprMutexPtr->u.handlePtr = pthreadMutexPtr;
+        cprMutexPtr->lockId = ++id;
+        return (cprMutex_t)cprMutexPtr;
+    }
+
+    /*
+     * Since the code malloced two pointers ensure both
+     * are freed since one malloc call could have worked
+     * and the other failed.
+     */
+    if (pthreadMutexPtr != NULL) {
+        cpr_free(pthreadMutexPtr);
+    } else if (cprMutexPtr != NULL) {
+        cpr_free(cprMutexPtr);
+    }
+
+    /* Malloc failed */
+    CPR_ERROR("%s - Malloc for mutex %s failed.\n", fname, name);
+    errno = ENOMEM;
+    return (cprMutex_t)NULL;
+}
+
+
+/**
+ * cprDestroyMutex
+ *
+ * @brief Destroys the mutex passed in.
+ *
+ * The cprDestroyMutex function is called to destroy a mutex. It is the
+ * application's responsibility to ensure that the mutex is unlocked when
+ * destroyed. Unpredictiable behavior will occur if an application
+ * destroys a locked mutex.
+ *
+ * @param[in] mutex - mutex to destroy
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE. errno should be set for CPR_FAILURE.
+ */
+cprRC_t
+cprDestroyMutex (cprMutex_t mutex)
+{
+    static const char fname[] = "cprDestroyMutex";
+    cpr_mutex_t *cprMutexPtr;
+    int32_t rc;
+
+    cprMutexPtr = (cpr_mutex_t *) mutex;
+    if (cprMutexPtr != NULL) {
+        rc = pthread_mutex_destroy(cprMutexPtr->u.handlePtr);
+        if (rc != 0) {
+            CPR_ERROR("%s - Failure destroying Mutex %s: %d\n",
+                      fname, cprMutexPtr->name, rc);
+            return CPR_FAILURE;
+        }
+        cprMutexPtr->lockId = 0;
+        cpr_free(cprMutexPtr->u.handlePtr);
+        cpr_free(cprMutexPtr);
+        return CPR_SUCCESS;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprGetMutex
+ *
+ * @brief Acquire ownership of a mutex
+ *
+ * This function locks the mutex referenced by the mutex parameter. If the mutex
+ * is locked by another thread, the calling thread will block until the mutex is
+ * released.
+ *
+ * @param[in] mutex - Which mutex to acquire
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprGetMutex (cprMutex_t mutex)
+{
+    static const char fname[] = "cprGetMutex";
+    cpr_mutex_t *cprMutexPtr;
+    int32_t rc;
+
+    cprMutexPtr = (cpr_mutex_t *) mutex;
+    if (cprMutexPtr != NULL) {
+        rc = pthread_mutex_lock((pthread_mutex_t *) cprMutexPtr->u.handlePtr);
+        if (rc != 0) {
+            CPR_ERROR("%s - Error acquiring mutex %s: %d\n",
+                      fname, cprMutexPtr->name, rc);
+            return CPR_FAILURE;
+        }
+        return CPR_SUCCESS;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprReleaseMutex
+ *
+ * @brief Release ownership of a mutex
+ *
+ * This function unlocks the mutex referenced by the mutex parameter.
+ * @param[in] mutex - Which mutex to release
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprReleaseMutex (cprMutex_t mutex)
+{
+    static const char fname[] = "cprReleaseMutex";
+    cpr_mutex_t *cprMutexPtr;
+    int32_t rc;
+
+    cprMutexPtr = (cpr_mutex_t *) mutex;
+    if (cprMutexPtr != NULL) {
+        rc = pthread_mutex_unlock((pthread_mutex_t *) cprMutexPtr->u.handlePtr);
+        if (rc != 0) {
+            CPR_ERROR("%s - Error releasing mutex %s: %d\n",
+                      fname, cprMutexPtr->name, rc);
+            return CPR_FAILURE;
+        }
+        return CPR_SUCCESS;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
diff --git a/libs/sipcc/cpr/linux/cpr_linux_locks.h b/libs/sipcc/cpr/linux/cpr_linux_locks.h
new file mode 100644 (file)
index 0000000..ef6e2a9
--- /dev/null
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_LOCKS_H_
+#define _CPR_LINUX_LOCKS_H_
+
+/*
+ * System Mutexes.
+ */
+extern pthread_mutex_t linuxThreadMutex;
+#endif
+
diff --git a/libs/sipcc/cpr/linux/cpr_linux_private.h b/libs/sipcc/cpr/linux/cpr_linux_private.h
new file mode 100644 (file)
index 0000000..985cf04
--- /dev/null
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_PRIVATE_H_
+#define _CPR_LINUX_PRIVATE_H_
+
+extern pthread_mutexattr_t cprMutexAttributes;
+
+cpr_status_e cprLockInit(void);
+cpr_status_e cprTimerInit(void);
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_rand.h b/libs/sipcc/cpr/linux/cpr_linux_rand.h
new file mode 100644 (file)
index 0000000..8fa8b07
--- /dev/null
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_RAND_H_
+#define _CPR_LINUX_RAND_H_
+
+/* @def Defines to generate a random number */
+#define cpr_srand(seed) srand(seed)
+#define cpr_rand()      rand()
+
+
+
+#endif
+
diff --git a/libs/sipcc/cpr/linux/cpr_linux_socket.c b/libs/sipcc/cpr/linux/cpr_linux_socket.c
new file mode 100644 (file)
index 0000000..0259fa2
--- /dev/null
@@ -0,0 +1,965 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdio.h"
+#include "cpr_assert.h"
+#include "cpr_socket.h"
+#include "cpr_debug.h"
+#include "cpr_rand.h"
+#include "cpr_timers.h"
+#include "cpr_errno.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include <sys/syslog.h>
+#include <sys/fcntl.h>
+#include <ctype.h>
+
+
+//const cpr_in6_addr_t in6addr_any = IN6ADDR_ANY_INIT;
+const cpr_ip_addr_t ip_addr_invalid = {0};
+
+#define IN6ADDRSZ   16
+#define INT16SZ     2
+#define        INADDRSZ        4
+
+#define MAX_RETRY_FOR_EAGAIN 10
+
+/* Forward declarations of internal (helper) functions */
+static int cpr_inet_pton4(const char *src, uint8_t *dst, int pton);
+static int cpr_inet_pton6(const char *src, uint8_t *dst);
+
+/**
+ * cprBind
+ *
+ * @brief The cprBind function is the CPR wrapper for the "Bind" socket call.
+ *
+ * The cprBind() function shall assign a local socket address address to a
+ * socket identified by descriptor socket that has no local socket address
+ * assigned. Sockets created with the cprSocket() function are initially
+ * unnamed; they are identified only by their address family.
+ *
+ * @param[in] soc  - The socket previously created using cprAccept that is to be
+ *                   bound
+ * @param[out] addr - The address of the socket that is to be bound
+ * @param[in] addr_len Points to a cpr_socklen_t structure which on specifies the length
+ *                   of the supplied cpr_sockaddr_t structure.
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ *         @li [CPR_ENOTSOCK]   The descriptor references a file, not a socket
+ *
+ */
+cpr_status_e
+cprBind (cpr_socket_t soc,
+         CONST cpr_sockaddr_t * RESTRICT addr,
+         cpr_socklen_t addr_len)
+{
+    cprAssert(addr != NULL, CPR_FAILURE);
+
+    return ((bind(soc, (struct sockaddr *)addr, addr_len) != 0) ?
+            CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprCloseSocket
+ *
+ * @brief The cprCloseSocket function shall destroy the socket
+ *
+ * The cprCloseSocket() function shall destroy the socket descriptor indicated
+ * by socket.  The descriptor may be made available for return by subsequent
+ * calls to cprSocket().  If the linger option is set with a non-zero timeout
+ * and the socket has untransmitted data, then cprCloseSocket() shall block for
+ * up to the current linger interval until all data is transmitted.
+ *
+ * @param[in] soc  - The socket that needs to be destroyed
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+cprCloseSocket (cpr_socket_t soc)
+{
+    return ((close(soc) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprConnect
+ *
+ * @brief The cprConnect function is the wrapper for the "connect" socket API
+ *
+ * The cprConnect() function shall attempt to make a connection on a socket.
+ * If the connection cannot be established immediately and non-blocking is set for
+ * the socket, cprConnect() shall fail and set cpr_errno to [CPR_EINPROGRESS], but
+ * the connection request shall not be aborted, and the connection shall be
+ * established asynchronously. When the connection has been established
+ * asynchronously, cprSelect() shall indicate that the file descriptor for the
+ * socket is ready for writing.
+ * If the initiating socket is connection-mode, then cprConnect() shall attempt to
+ * establish a connection to the address specified by the address argument.If the
+ * connection cannot be established immediately and non-blocking is not set for
+ * the socket, cprConnect() shall block for up to an unspecified timeout interval
+ * until the connection is established. If the timeout interval expires before the
+ * connection is established, cprConnect() shall fail and the connection attempt
+ * shall be aborted.
+ * If the initiating socket is connectionless (i.e. SOCK_DGRAM), then cprConnect()
+ * shall set the socket's peer address, and no connection is made. The peeraddress
+ * identifies where all datagrams are sent on subsequent cprSend() functions, and
+ * limits the remote sender for subsequent cprRecv() functions. If address is a
+ * null address for the protocol, the socket's peer address shall be reset.
+ *
+ * @param[in] soc  - Specifies the socket created with cprSocket() to connect
+ * @param[in] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
+ * @param[in] addr_len  - Points to a cpr_socklen_t structure which specifies
+ *                        the length of the supplied cpr_sockaddr_t structure.
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+cprConnect (cpr_socket_t soc,
+            SUPPORT_CONNECT_CONST cpr_sockaddr_t * RESTRICT addr,
+            cpr_socklen_t addr_len)
+{
+    int retry = 0, retval;
+
+    cprAssert(addr != NULL, CPR_FAILURE);
+
+    retval = connect(soc, (struct sockaddr *)addr, addr_len);
+
+    while( retval == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      retval = connect(soc, (struct sockaddr *)addr, addr_len);
+    }
+
+    return ((retval!=0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprGetSockName
+ *
+ * @brief The cprGetSockName retrieves the locally-bound name of the socket
+ *
+ * The cprGetSockName() function shall retrieve the locally-bound name of the
+ * specified socket, store this address in the cpr_sockaddr_t struct
+ * structure pointed to by the "addr" argument, and store the length of this address in
+ * the object pointed to by the "addr_len" argument.  If the actual length
+ * of the address is greater than the length of the supplied cpr_sockaddr_t
+ * structure, the stored address shall be truncated.  If the socket has not been
+ * bound to a local name, the value stored in the object pointed to by address is
+ * unspecified.
+ *
+ * @param[in] soc  - Specifies the socket to get the peer address from
+ * @param[out] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
+ * @param[out] addr_len  - Points to a cpr_socklen_t structure which specifies
+ *                        the length of the supplied cpr_sockaddr_t structure.
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ *  @note If successful, the address argument shall point to the address of the socket
+ *  @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
+ *        @li [CPR_ENOTCONN]  The socket is not connected
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_OPNOTSUPP] The operation is not supported for the socket
+ */
+cpr_status_e
+cprGetSockName (cpr_socket_t soc,
+                cpr_sockaddr_t * RESTRICT addr,
+                cpr_socklen_t * RESTRICT addr_len)
+{
+    cprAssert(addr != NULL, CPR_FAILURE);
+    cprAssert(addr_len != NULL, CPR_FAILURE);
+
+    return ((getsockname(soc, (struct sockaddr *)addr, addr_len) != 0) ?
+            CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprListen
+ *
+ * @brief The cprListen is the CPR wrapper for the "listen" socket API.
+ *
+ * The cprListen() function shall mark a connection-mode socket, specified by
+ * the "soc" argument, as accepting connections.  The "backlog" argument
+ * provides a hint to the implementation which the implementation shall use to
+ * limit the number of outstanding connections in the socket's listen queue.
+ * Implementations may impose a limit on backlog and silently reduce the
+ * specified value. Implementations shall support values of backlog up to
+ * SOMAXCONN.
+ * If listen() is called with a backlog argument value that is less than zero
+ * (0), the function behaves as if it had been called with a backlog argument
+ * value of 0.  A backlog argument of zero (0) may allow the socket to accept
+ * connections, in which case the length of the listen queue may be set to an
+ * implementation-defined minimum value.
+ *
+ * @param[in] soc  - Specifies the socket to get the peer address
+ * @param[in] backlog  - The limit on the number of outstanding connections
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *  @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EDESTADDRREQ]  The socket is not bound to a local address
+ */
+cpr_status_e
+cprListen (cpr_socket_t soc,
+           uint16_t backlog)
+{
+    return ((listen(soc, backlog) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprRecv
+ *
+ * @brief The cprRecv() function shall receive a message from a socket.
+ *
+ * This function is normally used with connected sockets because it does not permit
+ * the application to retrieve the source address of received data.  The cprRecv()
+ * function shall return the length of the message written to the buffer pointed
+ * to by the "buf" argument.
+ * For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
+ * a single operation.  If the message is too long to fit in the supplied buffer
+ * and the "flags" argument does not have MSG_PEEK set, the excess bytes are
+ * discarded.  If the MSG_WAITALL flag is not set, data shall be returned onlyup
+ * to the end of the first message.
+ * For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
+ * data is returned as it becomes available; therefore, no data is discarded.
+ * If no messages are available at the socket and non-blocking is not set on the
+ * socket's file descriptor, cprRecv() shall block until a message arrives. If no
+ * messages are available at the socket and non-blocking is set on the socket's
+ * file descriptor, cprRecv() shall fail and set cpr_errno to [CPR_EAGAIN]or
+ * [CPR_EWOULDBLOCK].
+ * The cprSelect() function can be used to determine when data is available to be
+ * received.  The cprRecv() function is the same as cprRecvFrom() with a zero
+ * address_len argument.
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ * @param[in] flags  - The options used for the recv.
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+cprRecv (cpr_socket_t soc,
+         void * RESTRICT buf,
+         size_t len,
+         int32_t flags)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(buf != NULL, CPR_FAILURE);
+
+    rc = recv(soc, buf, len, flags);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = recv(soc, buf, len, flags);
+    }
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprRecvFrom
+ *
+ * @brief The cprRecvFrom() function shall receive a message from a specific socket.
+ *
+ * The cprRecvFrom() function shall receive a message from a socket and is
+ * normally used with connectionless-mode sockets because it permits the
+ * application to retrieve the source address of received data.  This function
+ * shall return the length of the message written to the buffer pointed to bythe
+ * buffer argument.
+ * For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
+ * a single operation.  If the message is too long to fit in the supplied buffer
+ * and the flags argument does not have MSG_PEEK set, the excess bytes are
+ * discarded.  If the MSG_WAITALL flag is not set, data shall be returned onlyup
+ * to the end of the first message.
+ * For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
+ * data is returned as it becomes available; therefore, no data is discarded.
+ * If no messages are available at the socket and non-blocking is not set on the
+ * socket's file descriptor, cprRecvFrom() shall block until a message arrives. If no
+ * messages are available at the socket and non-blocking is set on the socket's
+ * file descriptor, cprRecvFrom() shall fail and set cpr_errno to [CPR_EAGAIN]or
+ * [CPR_EWOULDBLOCK].
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ * @param[in] flags  - The options used for the recvFrom
+ * @param[out] from - A null pointer or pointer to a cpr_sockaddr_t structure in
+ *                   which the sending address is to be stored.
+ * @param[out] fromlen - The length of the cpr_sockaddr_t structure pointed to by
+ *                    the "from" argument.
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+cprRecvFrom (cpr_socket_t soc,
+             void * RESTRICT buf,
+             size_t len,
+             int32_t flags,
+             cpr_sockaddr_t * RESTRICT from,
+             cpr_socklen_t * RESTRICT fromlen)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(buf != NULL, CPR_FAILURE);
+    cprAssert(from != NULL, CPR_FAILURE);
+    cprAssert(fromlen != NULL, CPR_FAILURE);
+
+    rc = recvfrom(soc, buf, len, flags, (struct sockaddr *)from, fromlen);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = recvfrom(soc, buf, len, flags, (struct sockaddr *)from, fromlen);
+    }
+
+    if (rc == -1) {
+        CPR_INFO("error in recvfrom buf=%x fromlen=%d\n", buf, *fromlen);
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSelect
+ *
+ * @brief The cprSelect() function is the CPR wrapper for the "select" socket API.
+ *
+ * The cprSelect() function returns which of the specified file descriptors is ready for
+ * reading, ready for writing, or has an exception pending.  The function will
+ * block up to the specified timeout interval for one of the conditions to be
+ * true or until interrupted by a signal.
+ *
+ * File descriptor masks of type fd_set can be initialized and tested with
+ * FD_CLR(), FD_ISSET(), FD_SET(), and FD_ZERO().  The OS-implementation may
+ * implement these calls either as a macro definition or an actual function.
+ * void FD_CLR(cpr_socket_t, fd_set *) shall remove the file descriptor fd from the
+ * set. If fd is not a member of this set, there shall be no effect on theset.
+ * int FD_ISSET(cpr_socket_t, fd_set *) shall evaluate to non-zero if the file
+ * descriptor fd is a member of the set, and shall evaluate to zero otherwise.
+ * void FD_SET(cpr_socket_t, fd_set *) shall add the file descriptor fd to the set.
+ * If the file descriptor fd is already in this set, there shall be no effect on
+ * the set.
+ * void FD_ZERO(fd_set *) shall initialize the descriptor set to the null set.
+ *
+ * @param[in] nfds    Specifies the argument range of file descriptors to be tested. The
+ *            descriptors from zero through nfds-1 in the descriptor sets shall be examined.
+ * @param[in] read_fds    If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for being ready to read.
+ *    @li On output this specifies the file descriptors that are ready to read.
+ * @param[in] write_fds   If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for being ready to write.
+ *    @li On output this specifies the file descriptors that are ready to write.
+ * @param[in] except_fds   If not a null pointer, this is a pointer to an fd_set object.
+ *    @li On input this specifies the file descriptors to be checked for errors/exceptions pending.
+ *    @li On output this specifies the file descriptors that have errors/exceptions pending.
+ * @param[in] timeout If not a null pointer, this  points to an object of type struct cpr_timeval
+ *       that specifies the maximum time interval to wait for the selection to complete.
+ *       If timeout expires, the function shall return.  If the parameter is a null pointer, the function
+ *       will block indefinitely until at least one file descriptor meets the criteria.
+ *
+ * @note While this function supports multiple file descriptor types, only file descriptors referring to a
+ *      socket are guaranteed to be supported.
+ * @note Note that the "nfds" parameter is not used in Windows.
+ *
+ * @return Upon successful completion, cprSelect() shall return the number of file descriptors ready.
+ *       Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to indicate the error where read_fds,
+ *       write_fds and error_fds are not modified.
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_INTR]   The function was interrupted before an event or
+ *                         timeout occurred
+ *        @li [CPR_INVAL]  An invalid timeout was specified or nfds is less
+ *                        than 0 or greater than FD_SETSIZE
+ *
+ */
+int16_t
+cprSelect (uint32_t nfds,
+           fd_set * RESTRICT read_fds,
+           fd_set * RESTRICT write_fds,
+           fd_set * RESTRICT except_fds,
+           struct cpr_timeval * RESTRICT timeout)
+{
+    int16_t rc;
+    struct timeval t, *t_p;
+
+    if (timeout != NULL) {
+        t.tv_sec  = timeout->tv_sec;
+        t.tv_usec = timeout->tv_usec;
+        t_p       = &t;
+    } else {
+        t_p       = NULL;
+    }
+
+    rc = (int16_t) select(nfds, read_fds, write_fds, except_fds, t_p);
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSend
+ *
+ * @brief The cprSend() function is the CPR wrapper for the "send" socket API.
+ *
+ * The cprSend() function shall transmit a message from the specified socket to
+ * its peer. The cprSend() function shall send a message only when the socket is
+ * connected. The length of the message to be sent is specified by the length
+ * argument. If the message is too long to pass through the underlying protocol,
+ * cprSend() shall fail and no data is transmitted.  Delivery of the message is
+ * not guaranteed.
+ * If space is not available at the sending socket to hold the message to be
+ * transmitted, and the socket does not have non-blocking set, cprSend() shall
+ * block until space is available; otherwise, if non-blocking is set, the
+ * cprSend() call shall fail.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags   The socket options
+ *
+ * @return Upon successful completion, cprSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+cprSend (cpr_socket_t soc,
+         CONST void *buf,
+         size_t len,
+         int32_t flags)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(buf != NULL, CPR_FAILURE);
+
+    rc = send(soc, buf, len, flags);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = send(soc, buf, len, flags);
+    }
+
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSendTo
+ *
+ * @brief The cprSendTo() function is the CPR wrapper for the "send" socket API.
+ *
+ * The cprSendTo() function shall send a message through a socket. If the socket
+ * is connectionless-mode, the message shall be sent to the address specified by
+ * address. If the socket is connection-mode, address shall be ignored.
+ * Delivery of the message is not guaranteed.
+ * If space is not available at the sending socket to hold the message to be
+ * transmitted, and the socket does not have non-blocking set, cprSendTo() shall
+ * block until space is available; otherwise, if non-blocking is set, the
+ * cprSendTo() call shall fail.
+ * The cprSelect() function can be used to determine when it is possible to send
+ * more data.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] msg  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ * @param[in] flags   The socket options
+ * @param[in] dest_addr Points to a cpr_sockaddr_t structure containing the destination
+ *                    address.
+ * @param[in] dest_len Specifies the length of the cpr_sockaddr_t structure pointed to by
+ *                     the "dest_addr" argument.
+ *
+ * @return Upon successful completion, cprSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+cprSendTo (cpr_socket_t soc,
+           CONST void *msg,
+           size_t len,
+           int32_t flags,
+           CONST cpr_sockaddr_t *dest_addr,
+           cpr_socklen_t dest_len)
+{
+    ssize_t rc;
+    int retry = 0;
+
+    cprAssert(msg != NULL, CPR_FAILURE);
+    cprAssert(dest_addr != NULL, CPR_FAILURE);
+
+    rc = sendto(soc, msg, len, flags, (struct sockaddr *)dest_addr, dest_len);
+    while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
+      cprSleep(100);
+      retry++;
+      rc = sendto(soc, msg, len, flags, (struct sockaddr *)dest_addr, dest_len);
+    }
+
+    if (rc == -1) {
+        return SOCKET_ERROR;
+    }
+    return rc;
+}
+
+/**
+ * cprSetSockOpt
+ *
+ * @brief The cprSetSockOpt() function is used to set the socket options
+ *
+ * The cprSetSockOpt() function shall set the option specified by the
+ * option_name argument, at the protocol level specified by the "level" argument,
+ * to the value pointed to by the "opt_val" argument for the socket specified
+ * by the "soc" argument.
+ * The level argument specifies the protocol level at which the option resides. To
+ * set options at the socket level, specify the level argument as SOL_SOCKET. To
+ * set options at other levels, supply the appropriate level identifier for the
+ * protocol controlling the option. For example, to indicate that an option is
+ * interpreted by the TCP (Transport Control Protocol), set level to IPPROTO_TCP
+ * as defined in the <cpr_in.h> header.
+ * The opt_name argument specifies a single option to set. The option_name
+ * argument and any specified options are passed uninterpreted to the appropriate
+ * protocol module. The <cpr_socket.h> header defines the socket-level options.
+ *
+ * @param[in] soc The socket on which the options need to be set
+ * @param[in] level The protocol level at which the option resides
+ * @param[in] opt_name This specifies the single option that is being set
+ * @param[in] opt_val The values for the option
+ * @param[in] opt_len The length field for the option values
+ *
+ * @return Upon successful completion, CPR_SUCCESS shall be returned;
+ * otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
+ * error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EINVAL]    The specified option is invalid or the socket is
+ *                          shut down
+ *        @li [CPR_EISCONN]   The specified socket is already connected and can
+ *                          not be changed
+ *        @li [CPR_ENOPROTOOPT]   The option is not supported by the protocol
+ *
+ */
+cpr_status_e
+cprSetSockOpt (cpr_socket_t soc,
+               uint32_t level,
+               uint32_t opt_name,
+               CONST void *opt_val,
+               cpr_socklen_t opt_len)
+{
+    cprAssert(opt_val != NULL, CPR_FAILURE);
+
+    return ((setsockopt(soc, (int)level, (int)opt_name, opt_val, opt_len) != 0)
+            ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+/**
+ * cprSetSockNonBlock
+ *
+ * @brief The cprSetSockNonBlock() function is used to set the socket options
+ *
+ * The cprSetSockNonBlock() function shall set a socket to be non blocking. It
+ * uses the fcntl function on the socket desriptor to achieve this. If the fcntl
+ * operation fails, a CPR_FAILURE is returned and errno is set by the OS
+ * implementation.
+ *
+ * @param[in] soc The socket that needs to be set to non-blocking
+ *
+ * @return Upon successful completion, CPR_SUCCESS shall be returned;
+ * otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
+ * error.
+ */
+cpr_status_e
+cprSetSockNonBlock (cpr_socket_t soc)
+{
+
+    return ((fcntl(soc, F_SETFL, O_NONBLOCK) != 0) ? CPR_FAILURE : CPR_SUCCESS);
+}
+
+
+/**
+ * cprSocket
+ *
+ * @brief The cprSocket() is the CPR wrapper for the "socket" API
+ *
+ * The cprSocket() function shall create an unbound socket in a
+ * communications domain, and return a file descriptor that can be used
+ * in later function calls that operate on sockets.
+ *
+ * @param[in] domain  The communications domain, i.e. address family, in which a socket is to
+ *                   be created
+ * @param[in] type    The type of socket to be created. The following types must
+ *                   be supported:
+ *             @li SOCK_STREAM Provides sequenced, reliable, bidirectional, connection-mode
+ *                               byte streams, i.e. TCP
+ *             @li SOCK_DGRAM  Provides connectionless-mode, unreliable
+ *                           datagrams of fixed maximum length, i.e. UDP
+ *             @li SOCK_SEQPACKET   Provides sequenced, reliable, bidirectional, connection-mode
+ *                          transmission paths for records.  A single operation never transfers part of
+ *                          more than one record.  Record boundaries are visible to the receiver via the
+ *                          MSG_EOR flag.
+ * @param[in] protocol    The protocol to be used with the socket.
+ *
+ * @return Upon successful completion, a socket handle defined by
+ *      cpr_socket_t shall be returned; otherwise, INVALID_SOCKET shall be returned and
+ *       cpr_errno set to indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EINVAL]   The specified option is invalid or the socket is
+ *                           shut down
+ *        @li [CPR_EMFILE]   No more socket descriptors available for the
+ *                           process
+ *        @li [CPR_ENFILE]   No more socket descriptors available for the
+ *                           system
+ *        @li [CPR_EPROTOTYPE] The socket type is not supported by the
+ *                              protocol
+ *        @li [CPR_EPROTONOSUPPORT]   The protocol is not supported for the
+ *                                    domain
+ *
+ */
+cpr_socket_t
+cprSocket (uint32_t domain,
+           uint32_t type,
+           uint32_t protocol)
+{
+    cpr_socket_t s;
+
+    s = socket((int)domain, (int)type, (int)protocol);
+    if (s == -1) {
+        return INVALID_SOCKET;
+    }
+    return s;
+}
+
+/**
+ * @}
+ */
+
+
+
+/* cpr_inet_pton
+ *     Convert from presentation format (which usually means ASCII printable)
+ *     to network format (which is usually some kind of binary format).
+ * @param[in] af The address family IPv4 or IPv6
+ * @param[in] src The address that needs to be converted
+ * @param[out] dst The address after the conversion
+ * @return
+ *     1 if the address was valid for the specified address family
+ *     0 if the address wasn't valid (`dst' is untouched in this case)
+ *     -1 if some other error occurred (`dst' is untouched in this case, too)
+ */
+int
+cpr_inet_pton (int af, const char *src, void *dst)
+{
+
+       switch (af) {
+       case AF_INET:
+               return (cpr_inet_pton4(src, dst, 1));
+       case AF_INET6:
+               return (cpr_inet_pton6(src, dst));
+       default:
+               return (-1);
+       }
+       /* NOTREACHED */
+}
+
+
+/**
+ *  Utility function that sets up the socket address, using
+ *  the name and the pid to guarantee uniqueness
+ *
+ *  @param[in] addr - socket fd to bind with the IPC address.
+ *  @param[in] name - pointer to the name of socket to bind to.
+ *
+ *
+ *  @pre  (name != NULL)
+ */
+void cpr_set_sockun_addr (cpr_sockaddr_un_t *addr, const char *name, pid_t pid)
+{
+    /* Bind to the local socket */
+    memset(addr, 0, sizeof(cpr_sockaddr_un_t));
+    addr->sun_family = AF_LOCAL;
+    snprintf((char *) addr->sun_path, sizeof(addr->sun_path), "%s_%d", name, pid);
+}
+
+/* int
+ * inet_pton4(src, dst, pton)
+ *     when last arg is 0: inet_aton(). with hexadecimal, octal and shorthand.
+ *     when last arg is 1: inet_pton(). decimal dotted-quad only.
+ * return:
+ *     1 if `src' is a valid input, else 0.
+ * notice:
+ *     does not touch `dst' unless it's returning 1.
+ */
+static int
+cpr_inet_pton4(const char *src, uint8_t *dst, int pton)
+{
+       uint32_t val;
+       uint32_t digit;
+       int base, n;
+       unsigned char c;
+       uint32_t parts[4];
+       uint32_t *pp = parts;
+
+       c = *src;
+       for (;;) {
+               /*
+                * Collect number up to ``.''.
+                * Values are specified as for C:
+                * 0x=hex, 0=octal, isdigit=decimal.
+                */
+               if (!isdigit(c))
+                       return (0);
+               val = 0; base = 10;
+               if (c == '0') {
+                       c = *++src;
+                       if (c == 'x' || c == 'X')
+                               base = 16, c = *++src;
+                       else if (isdigit(c) && c != '9')
+                               base = 8;
+               }
+               /* inet_pton() takes decimal only */
+               if (pton && base != 10)
+                       return (0);
+               for (;;) {
+                       if (isdigit(c)) {
+                               digit = c - '0';
+                               if (digit >= (uint16_t)base)
+                                       break;
+                               val = (val * base) + digit;
+                               c = *++src;
+                       } else if (base == 16 && isxdigit(c)) {
+                               digit = c + 10 - (islower(c) ? 'a' : 'A');
+                               if (digit >= 16)
+                                       break;
+                               val = (val << 4) | digit;
+                               c = *++src;
+                       } else
+                               break;
+               }
+               if (c == '.') {
+                       /*
+                        * Internet format:
+                        *      a.b.c.d
+                        *      a.b.c   (with c treated as 16 bits)
+                        *      a.b     (with b treated as 24 bits)
+                        *      a       (with a treated as 32 bits)
+                        */
+                       if (pp >= parts + 3)
+                               return (0);
+                       *pp++ = val;
+                       c = *++src;
+               } else
+                       break;
+       }
+       /*
+        * Check for trailing characters.
+        */
+       if (c != '\0' && !isspace(c))
+               return (0);
+       /*
+        * Concoct the address according to
+        * the number of parts specified.
+        */
+       n = pp - parts + 1;
+       /* inet_pton() takes dotted-quad only.  it does not take shorthand. */
+       if (pton && n != 4)
+               return (0);
+       switch (n) {
+
+       case 0:
+               return (0);             /* initial nondigit */
+
+       case 1:                         /* a -- 32 bits */
+               break;
+
+       case 2:                         /* a.b -- 8.24 bits */
+               if (parts[0] > 0xff || val > 0xffffff)
+                       return (0);
+               val |= parts[0] << 24;
+               break;
+
+       case 3:                         /* a.b.c -- 8.8.16 bits */
+               if ((parts[0] | parts[1]) > 0xff || val > 0xffff)
+                       return (0);
+               val |= (parts[0] << 24) | (parts[1] << 16);
+               break;
+
+       case 4:                         /* a.b.c.d -- 8.8.8.8 bits */
+               if ((parts[0] | parts[1] | parts[2] | val) > 0xff)
+                       return (0);
+               val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+               break;
+       }
+       if (dst) {
+               val = htonl(val);
+               memcpy(dst, &val, INADDRSZ);
+       }
+       return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ *     convert presentation level address to network order binary form.
+ * return:
+ *     1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ *     (1) does not touch `dst' unless it's returning 1.
+ *     (2) :: in a full address is silently ignored.
+ */
+static int
+cpr_inet_pton6(const char *src, uint8_t *dst)
+{
+       static const char xdigits_l[] = "0123456789abcdef",
+                         xdigits_u[] = "0123456789ABCDEF";
+       uint8_t tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+       const char *xdigits, *curtok;
+       int ch, saw_xdigit;
+       unsigned int val;
+
+       memset((tp = tmp), '\0', IN6ADDRSZ);
+       endp = tp + IN6ADDRSZ;
+       colonp = NULL;
+       /* Leading :: requires some special handling. */
+       if (*src == ':')
+               if (*++src != ':')
+                       return (0);
+       curtok = src;
+       saw_xdigit = 0;
+       val = 0;
+       while ((ch = *src++) != '\0') {
+               const char *pch;
+
+               if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+                       pch = strchr((xdigits = xdigits_u), ch);
+               if (pch != NULL) {
+                       val <<= 4;
+                       val |= (pch - xdigits);
+                       if (val > 0xffff)
+                               return (0);
+                       saw_xdigit = 1;
+                       continue;
+               }
+               if (ch == ':') {
+                       curtok = src;
+                       if (!saw_xdigit) {
+                               if (colonp)
+                                       return (0);
+                               colonp = tp;
+                               continue;
+                       } else if (*src == '\0')
+                               return (0);
+                       if (tp + INT16SZ > endp)
+                               return (0);
+                       *tp++ = (uint8_t) (val >> 8) & 0xff;
+                       *tp++ = (uint8_t) val & 0xff;
+                       saw_xdigit = 0;
+                       val = 0;
+                       continue;
+               }
+               if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+                   cpr_inet_pton4(curtok, tp, 1) > 0) {
+                       tp += INADDRSZ;
+                       saw_xdigit = 0;
+                       break;  /* '\0' was seen by inet_pton4(). */
+               }
+               return (0);
+       }
+       if (saw_xdigit) {
+               if (tp + INT16SZ > endp)
+                       return (0);
+               *tp++ = (uint8_t) (val >> 8) & 0xff;
+               *tp++ = (uint8_t) val & 0xff;
+       }
+       if (colonp != NULL) {
+               /*
+                * Since some memmove()'s erroneously fail to handle
+                * overlapping regions, we'll do the shift by hand.
+                */
+               const int n = tp - colonp;
+               int i;
+
+               if (tp == endp)
+                       return (0);
+               for (i = 1; i <= n; i++) {
+                       endp[- i] = colonp[n - i];
+                       colonp[n - i] = 0;
+               }
+               tp = endp;
+       }
+       if (tp != endp)
+               return (0);
+       memcpy(dst, tmp, IN6ADDRSZ);
+       return (1);
+}
+
diff --git a/libs/sipcc/cpr/linux/cpr_linux_socket.h b/libs/sipcc/cpr/linux/cpr_linux_socket.h
new file mode 100644 (file)
index 0000000..7cc7877
--- /dev/null
@@ -0,0 +1,351 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_SOCKET_H_
+#define _CPR_LINUX_SOCKET_H_
+
+#include "cpr_types.h"
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+/**
+ * Set public CPR header file options
+ */
+#ifdef CPR_USE_SOCKETPAIR
+#undef CPR_USE_SOCKETPAIR
+#endif
+#define SUPPORT_CONNECT_CONST const
+
+/**
+ * Define SOCKET_ERROR
+ */
+#define SOCKET_ERROR   (-1)
+
+/**
+ * Define INVALID_SOCKET
+ */
+#define INVALID_SOCKET (-1)
+
+/**
+ * Define cpr_socket_t
+ */
+typedef int cpr_socket_t;
+
+/**
+ * Define cpr_socklen_t
+ */
+typedef socklen_t cpr_socklen_t;
+
+/**
+ * Address family, defined in sys/socket.h
+ *  AF_UNSPEC
+ *  AF_LOCAL / AF_UNIX
+ *  AF_INET
+ *  AF_INET6
+ *  AF_MAX
+ *
+ *  AF_NETLYR2 (unique to CNU) interface directly to layer 2, bypass IP
+ */
+#ifndef AF_UNIX
+#define AF_UNIX  AF_LOCAL
+#endif
+
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981, based on the BSD file netinet/in.h.
+ * IPv6 additions per RFC 2292.
+ */
+
+
+/*
+ * Define the following socket options as needed
+ *   SO_DEBUG
+ *   SO_ACCEPTCONN
+ *   SO_REUSEADDR / SO_EXCLUSIVEADDRUSE
+ *   SO_KEEPALIVE
+ *   SO_DONTROUTE
+ *   SO_BROADCAST
+ *   SO_USELOOPBACK
+ *   SO_LINGER / SO_DONTLINGER
+ *   SO_OOBINLINE
+ *   SO_SNDBUF
+ *   SO_RCVBUF
+ *   SO_ERROR
+ *   SO_TYPE
+ *
+ * The following options are available for Unix-only variants
+ *   SO_SNDLOWAT
+ *   SO_RCVLOWAT
+ *   SO_SNDTIMEO
+ *   SO_RCVTIMEO
+ *   SO_PROTOTYPE - Not documented as being supported by CNU
+ */
+
+/* defined in netinet/in.h */
+#define SO_DONTLINGER       ((int)(~SO_LINGER))
+#define SO_EXCLUSIVEADDRUSE ((int)(~SO_REUSEADDR))
+
+/*
+ * Protocols (Base),
+ * reference http://www.iana.org/assignments/protocol-numbers.html
+ *   IPPROTO_IP
+ *   IPPROTO_GGP
+ *   IPPROTO_ICMP
+ *   IPPROTO_IGMP
+ *   IPPROTO_IPV4 / IPPROTO_IPIP
+ *   IPPROTO_TCP
+ *   IPPROTO_EGP
+ *   IPPROTO_PUP
+ *   IPPROTO_UDP
+ *   IPPROTO_IDP
+ *   IPPROTO_IPV6
+ *   IPPROTO_ROUTING
+ *   IPPROTO_FRAGMENT
+ *   IPPROTO_ESP
+ *   IPPROTO_AH
+ *   IPPROTO_ICMPV6
+ *   IPPROTO_NONE
+ *   IPPROTO_DSTOPTS
+ *   IPPROTO_ND
+ *   IPPROTO_EON
+ *   IPPROTO_IGRP
+ *   IPPROTO_ENCAP
+ *   IPPROTO_IPCOMP
+ *   IPPROTO_RAW
+ *   IPPROTO_MAX
+ */
+
+/* defined in netinet/in.h */
+#ifndef IPPROTO_IPV4
+#define IPPROTO_IPV4         4
+#endif
+
+#ifndef IPPROTO_IPIP
+#define IPPROTO_IPIP  IPPROTO_IPV4
+#endif
+
+//#define IPPROTO_RSVP        46
+#define IPPROTO_IGRP        88  /* Cisco/GXS IGRP */
+#define IPPROTO_EIGRP       88
+#define IPPROTO_IPCOMP     108  /* IP payload compression */
+
+/*
+ * Protocols (IPv6)
+ *   Assumming if IPV6 is not there, then none of the are
+ */
+#ifndef IPPROTO_IPV6
+#define IPPROTO_HOPOPTS      0  /* IPv6 hop-by-hop options */
+#define IPPROTO_IPV6        41  /* IPv6 */
+#define IPPROTO_ROUTING     43  /* IPv6 routing header */
+#define IPPROTO_FRAGMENT    44  /* IPv6 fragmentation header */
+#define IPPROTO_ICMPV6      58  /* ICMPv6 */
+#define IPPROTO_NONE        59  /* IPv6 no next header */
+#define IPPROTO_DSTOPTS     60  /* IPv6 destination options */
+#endif
+
+/*
+ * Protocols (Local)
+ * reference, RFC 3692
+ *   IPPROTO_UNX       Local sockets Unix protocol
+ *   IPPROTO_CDP       Non-Standard at 254, technially this value
+ *                     is for experimentation and testing
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Port/socket numbers: network standard functions
+ * reference http://www.iana.org/assignments/port-numbers
+ *  IPPORT_ECHO
+ *  IPPORT_DISCARD
+ *  IPPORT_SYSTAT
+ *  IPPORT_DAYTIME
+ *  IPPORT_NETSTAT
+ *  IPPORT_FTP
+ *  IPPORT_SSH
+ *  IPPORT_TELNET
+ *  IPPORT_SMTP
+ *  IPPORT_TIMESERVER
+ *  IPPORT_NAMESERVER
+ *  IPPORT_WHOIS
+ *  IPPORT_MTP
+ *  IPPORT_HTTP
+ *  IPPORT_NTP
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Port/socket numbers: host specific functions
+ *
+ *  IPPORT_TFTP
+ *  IPPORT_RJE
+ *  IPPORT_FINGER
+ *  IPPORT_TTYLINK
+ *  IPPORT_SUPDUP
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * UNIX TCP sockets
+ *
+ *  IPPORT_EXECSERVER
+ *  IPPORT_LOGINSERVER
+ *  IPPORT_CMDSERVER
+ *  IPPORT_EFSSERVER
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * UNIX UDP sockets
+ *
+ *  IPPORT_BIFFUDP
+ *  IPPORT_WHOSERVER
+ *  IPPORT_ROUTESERVER
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * SCCP sockets
+ */
+#define IPPORT_SCCP       2000
+
+/*
+ * tbd: need to finalize placement.
+ * Define range of ephemeral ports used for Cisco IP Phones.
+ */
+#define CIPPORT_EPH_LOW         0xC000
+#define CIPPORT_EPH_HI          0xCFFF
+
+
+/*
+ * Ports < IPPORT_RESERVED are reserved for
+ * privileged processes (e.g. root).
+ *
+ * IPPORT_RESERVED
+ * IPPORT_USERRESERVED
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Define INADDR constants
+ *   INADDR_ANY
+ *   INADDR_LOOPBACK
+ *   INADDR_BROADCAST
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * Define IN_CLASS constants/macros
+ *   IN_CLASS{A|B|C|D}(x)
+ *   IN_CLASS{A|B|C|D}_NET
+ *   IN_CLASS{A|B|C|D}_NSHIFT
+ *   IN_CLASS{A|B|C|D}_HOST
+ *   IN_CLASS{A|B|C|D}_MAX
+ */
+
+
+/*
+ * sockaddr_storage:
+ * Common superset of at least AF_INET, AF_INET6 and AF_LINK sockaddr
+ * structures. Has sufficient size and alignment for those sockaddrs.
+ */
+typedef uint16_t        sa_family_t;
+
+typedef struct
+{
+    sa_family_t sun_family;  /* AF_LOCAL/AF_UNIX */
+    char        sun_path[108];
+} cpr_sockaddr_un_t;
+
+
+/*
+ * Desired maximum size, alignment size and related types.
+ */
+#define _SS_MAXSIZE     256     /* Implementation specific max size */
+
+#define cpr_sun_len(a) sizeof(a)
+void cpr_set_sockun_addr(cpr_sockaddr_un_t *addr, const char *name, pid_t pid);
+
+/*
+ * To represent desired sockaddr max alignment for platform, a
+ * type is chosen which may depend on implementation platform architecture.
+ * Type chosen based on alignment size restrictions from <sys/isa_defs.h>.
+ * We desire to force up to (but no more than) 64-bit (8 byte) alignment,
+ * on platforms where it is possible to do so. (e.g not possible on ia32).
+ * For all currently supported platforms by our implementation
+ * in <sys/isa_defs.h>, (i.e. sparc, sparcv9, ia32, ia64)
+ * type "double" is suitable for that intent.
+ *
+ * Note: Type "double" is chosen over the more obvious integer type int64_t.
+ *   int64_t is not a valid type for strict ANSI/ISO C compilation on ILP32.
+ */
+typedef double          sockaddr_maxalign_t;
+
+#define _SS_ALIGNSIZE   (sizeof (sockaddr_maxalign_t))
+
+/*
+ * Definitions used for sockaddr_storage structure paddings design.
+ */
+#define _SS_PAD1SIZE    (_SS_ALIGNSIZE - sizeof (sa_family_t))
+#define _SS_PAD2SIZE    (_SS_MAXSIZE - (sizeof (sa_family_t)+ \
+                        _SS_PAD1SIZE + _SS_ALIGNSIZE))
+
+#ifndef __cplusplus
+typedef struct cpr_sockaddr_storage sockaddr_storage;
+#endif
+
+/*
+ * IP level options
+ *   IP_HDRINCL
+ *   IP_TOS
+ *   IP_TTL
+ */
+
+/* defined in netinet/in.h */
+
+/*
+ * TCP level options
+ *   TCP_NODELAY
+ *   TCP_MAXSEG
+ *   TCP_KEEPALIVE
+ */
+
+/* defined in netinet/tcp.h */
+
+
+/* TODO: Still to cleanup */
+
+/*
+ * WinSock 2 extension -- new options
+ */
+#define SO_MAX_MSG_SIZE   0x2003    /* maximum message size */
+
+
+#define SO_NBIO                 0x0400  /* Nonblocking socket I/O operation */
+#define SO_ASYNC                0x0800  /* should send asyn notification of
+                                         * I/O events */
+#define SO_VRFTABLEID           0x1000  /* set VRF routing table id */
+#define SO_SRC_SPECIFIED        0x2000  /* Specified Source Address to be used */
+#define SO_STRICT_ADDR_BIND     0x4000  /* Accept only those packets that have
+                                         * been sent to the address that this
+                                         * socket is bound to */
+/*
+ * Used for getting random port for tls connect
+ */
+#define TCP_PORT_RETRY_CNT      5
+#define TCP_PORT_MASK           0xfff
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_stdio.c b/libs/sipcc/cpr/linux/cpr_linux_stdio.c
new file mode 100644 (file)
index 0000000..48b101b
--- /dev/null
@@ -0,0 +1,148 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdio.h"
+#include "cpr_string.h"
+#include "CSFLog.h"
+
+/**
+ * @def LOG_MAX
+ *
+ * Constant represents the maximum allowed length for a message
+ */
+#define LOG_MAX 1024
+
+/**
+ * @addtogroup DebugAPIs The CPR Logging Abstractions
+ * @ingroup CPR
+ * @brief The CPR Debug/Logging APIs
+ *
+ * @{
+ */
+
+/**
+ * Debug message
+ *
+ * @param _format  format string
+ * @param ...      variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+int
+buginf (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    va_list ap;
+    int rc;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+    if (rc <= 0) {
+        return rc;
+    }
+
+  CSFLogDebug("cpr", "%s", fmt_buf);
+
+  return rc;
+}
+
+/**
+ * Debug message that can be larger than #LOG_MAX
+ *
+ * @param str - a fixed constant string
+ *
+ * @return  zero(0)
+ *
+ * @pre (str not_eq NULL)
+ */
+int
+buginf_msg (const char *str)
+{
+    char buf[LOG_MAX + 1];
+    const char *p;
+    int16_t len;
+
+    // terminate buffer
+    buf[LOG_MAX] = NUL;
+
+    len = (int16_t) strlen(str);
+
+    if (len > LOG_MAX) {
+        p = str;
+        do {
+            memcpy(buf, p, LOG_MAX);
+            p += LOG_MAX;
+            len -= LOG_MAX;
+
+            printf("%s",buf);
+        } while (len > LOG_MAX);
+
+        if (len) {
+          CSFLogDebug("cpr", "%s", (char *)p);
+        }
+    } else {
+      CSFLogDebug("cpr", "%s", (char *) str);
+    }
+
+    return 0;
+}
+
+/**
+ * Error message
+ *
+ * @param _format  format string
+ * @param ...     variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+void
+err_msg (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    va_list ap;
+    int rc;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+    if (rc <= 0) {
+        return;
+    }
+
+    CSFLogError("cpr", "%s", fmt_buf);
+}
+
+
+/**
+ * Notice message
+ *
+ * @param _format  format string
+ * @param ...     variable arg list
+ *
+ * @return  Return code from vsnprintf
+ *
+ * @pre (_format not_eq NULL)
+ */
+void
+notice_msg (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    va_list ap;
+    int rc;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+    if (rc <= 0) {
+        return;
+    }
+
+    CSFLogInfo("cpr", "%s", fmt_buf);
+}
+
diff --git a/libs/sipcc/cpr/linux/cpr_linux_stdio.h b/libs/sipcc/cpr/linux/cpr_linux_stdio.h
new file mode 100644 (file)
index 0000000..72cf1e9
--- /dev/null
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_STDIO_H_
+#define _CPR_LINUX_STDIO_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_string.c b/libs/sipcc/cpr/linux/cpr_linux_string.c
new file mode 100644 (file)
index 0000000..099fc73
--- /dev/null
@@ -0,0 +1,173 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_types.h"
+#include "cpr_stdlib.h"
+#include "cpr_string.h"
+#include "cpr_strings.h"
+
+
+/**
+ * cpr_strdup
+ *
+ * @brief The CPR wrapper for strdup
+
+ * The cpr_strdup shall return a pointer to a new string, which is a duplicate
+ * of the string pointed to by "str" argument. A null pointer is returned if the
+ * new string cannot be created.
+ *
+ * @param[in] str  - The string that needs to be duplicated
+ *
+ * @return The duplicated string or NULL in case of no memory
+ *
+ */
+char *
+cpr_strdup (const char *str)
+{
+    char *dup;
+    size_t len;
+
+    if (!str) {
+        return (char *) NULL;
+    }
+
+    len = strlen(str);
+    if (len == 0) {
+        return (char *) NULL;
+    }
+    len++;
+
+    dup = cpr_malloc(len * sizeof(char));
+    if (!dup) {
+        return (char *) NULL;
+    }
+    (void) memcpy(dup, str, len);
+    return dup;
+}
+
+
+/**
+ * cpr_strcasecmp
+ *
+ * @brief The CPR wrapper for strcasecmp
+ *
+ * The cpr_strcasecmp performs case insensitive string comparison of the "s1"
+ * and the "s2" strings.
+ *
+ * @param[in] s1  - The first string
+ * @param[in] s2  - The second string
+ *
+ * @return integer <,=,> 0 depending on whether s1 is <,=,> s2
+ */
+#ifndef CPR_USE_OS_STRCASECMP
+int
+cpr_strcasecmp (const char *s1, const char *s2)
+{
+    const unsigned char *us1 = (const unsigned char *) s1;
+    const unsigned char *us2 = (const unsigned char *) s2;
+
+    /* No match if only one ptr is NULL */
+    if ((!s1 && s2) || (s1 && !s2)) {
+        /*
+         * If one of these is NULL it will be the lesser of the two
+         * values and therefore we'll get the proper sign in the int
+         */
+        return (int) (s1 - s2);
+    }
+
+    /* Match if both ptrs the same (e.g. NULL) */
+    if (s1 == s2)
+        return 0;
+
+    while (*us1 != '\0' && *us2 != '\0' && toupper(*us1) == toupper(*us2)) {
+        us1++;
+        us2++;
+    }
+
+    return (toupper(*us1) - toupper(*us2));
+}
+
+/**
+ * cpr_strncasecmp
+ *
+ * @brief The CPR wrapper for strncasecmp
+ *
+ * The cpr_strncasecmp performs case insensitive string comparison for specific
+ * length "len".
+ *
+ * @param[in] s1  - The first string
+ * @param[in] s2  - The second string
+ * @param[in] len  - The length to be compared
+ *
+ * @return integer <,=,> 0 depending on whether s1 is <,=,> s2
+ */
+int
+cpr_strncasecmp (const char *s1, const char *s2, size_t len)
+{
+    const unsigned char *us1 = (const unsigned char *) s1;
+    const unsigned char *us2 = (const unsigned char *) s2;
+
+    /* No match if only one ptr is NULL */
+    if ((!s1 && s2) || (s1 && !s2))
+        return ((int) (s1 - s2));
+
+    if ((len == 0) || (s1 == s2))
+        return 0;
+
+    while (len-- > 0 && toupper(*us1) == toupper(*us2)) {
+        if (len == 0 || *us1 == '\0' || *us2 == '\0')
+            break;
+        us1++;
+        us2++;
+    }
+
+    return (toupper(*us1) - toupper(*us2));
+}
+#endif
+
+/**
+ * strcasestr
+ *
+ * @brief The same as strstr, but ignores case
+ *
+ * The strcasestr performs the strstr function, but ignores the case.
+ * This function shall locate the first occurrence in the string
+ * pointed to by s1 of the sequence of bytes (excluding the terminating
+ * null byte) in the string pointed to by s2.
+ *
+ * @param[in] s1  - The input string
+ * @param[in] s2  - The pattern to be matched
+ *
+ * @return A pointer to the first occurrence of string s2 found
+ *           in string s1 or NULL if not found.  If s2 is an empty
+ *           string then s1 is returned.
+ */
+char *
+strcasestr (const char *s1, const char *s2)
+{
+    unsigned int i;
+
+    if (!s1)
+        return (char *) NULL;
+
+    if (!s2 || (s1 == s2) || (*s2 == '\0'))
+        return (char *) s1;
+
+    while (*s1) {
+        i = 0;
+        do {
+            if (s2[i] == '\0')
+                return (char *) s1;
+            if (s1[i] == '\0')
+                return (char *) NULL;
+            if (toupper(s1[i]) != toupper(s2[i]))
+                break;
+            i++;
+        } while (1);
+        s1++;
+    }
+
+    return (char *) NULL;
+}
+
diff --git a/libs/sipcc/cpr/linux/cpr_linux_string.h b/libs/sipcc/cpr/linux/cpr_linux_string.h
new file mode 100644 (file)
index 0000000..78f8e9c
--- /dev/null
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_STRING_H_
+#define _CPR_LINUX_STRING_H_
+
+#include <string.h>
+#include <ctype.h>
+
+/**
+ * cpr_strdup
+ *
+ * @brief The CPR wrapper for strdup
+
+ * The cpr_strdup shall return a pointer to a new string, which is a duplicate
+ * of the string pointed to by "str" argument. A null pointer is returned if the
+ * new string cannot be created.
+ *
+ * @param[in] str  - The string that needs to be duplicated
+ *
+ * @return The duplicated string or NULL in case of no memory
+ *
+ */
+char *
+cpr_strdup(const char *str);
+
+
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_strings.h b/libs/sipcc/cpr/linux/cpr_linux_strings.h
new file mode 100644 (file)
index 0000000..69ffc1a
--- /dev/null
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_STRINGS_H_
+#define _CPR_LINUX_STRINGS_H_
+
+#include <strings.h>
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_threads.c b/libs/sipcc/cpr/linux/cpr_linux_threads.c
new file mode 100644 (file)
index 0000000..36d0844
--- /dev/null
@@ -0,0 +1,203 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include <pthread.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#define LINUX_MIN_THREAD_PRIORITY (-20)        /* tbd: check MV linux: current val from Larry port */
+#define LINUX_MAX_THREAD_PRIORITY (+19)        /* tbd: check MV linux. current val from Larry port */
+
+
+/**
+ * cprCreateThread
+ *
+ * @brief Create a thread
+ *
+ *  The cprCreateThread function creates another execution thread within the
+ *  current process. If the input parameter "name" is present, then this is used
+ *  for debugging purposes. The startRoutine is the address of the function where
+ *  the thread execution begins. The start routine prototype is defined as
+ *  follows
+ *  @code
+ *     int32_t (*cprThreadStartRoutine)(void* data)
+ *  @endcode
+ *
+ * @param[in]  name         - name of the thread created (optional)
+ * @param[in]  startRoutine - function where thread execution begins
+ * @param[in]  stackSize    - size of the thread's stack
+ * @param[in]  priority     - thread's execution priority
+ * @param[in]  data         - parameter to pass to startRoutine
+ *
+ * Return Value: Thread handle or NULL if creation failed.
+ */
+cprThread_t
+cprCreateThread (const char *name,
+                 cprThreadStartRoutine startRoutine,
+                 uint16_t stackSize,
+                 uint16_t priority,
+                 void *data)
+{
+    static const char fname[] = "cprCreateThread";
+    static uint16_t id = 0;
+    cpr_thread_t *threadPtr;
+    pthread_t threadId;
+    pthread_attr_t attr;
+
+    CPR_INFO("%s: creating '%s' thread\n", fname, name);
+
+    /* Malloc memory for a new thread */
+    threadPtr = (cpr_thread_t *)cpr_malloc(sizeof(cpr_thread_t));
+    if (threadPtr != NULL) {
+        if (pthread_attr_init(&attr) != 0) {
+
+            CPR_ERROR("%s - Failed to init attribute for thread %s\n",
+                      fname, name);
+            cpr_free(threadPtr);
+            return (cprThread_t)NULL;
+        }
+
+        if (pthread_attr_setstacksize(&attr, stackSize) != 0) {
+            CPR_ERROR("%s - Invalid stacksize %d specified for thread %s\n",
+                      fname, stackSize, name);
+            cpr_free(threadPtr);
+            return (cprThread_t)NULL;
+        }
+
+        if (pthread_create(&threadId, &attr, startRoutine, data) != 0) {
+            CPR_ERROR("%s - Creation of thread %s failed: %d\n",
+                      fname, name, errno);
+            cpr_free(threadPtr);
+            return (cprThread_t)NULL;
+        }
+
+        /* Assign name to CPR if one was passed in */
+        if (name != NULL) {
+            threadPtr->name = name;
+        }
+
+        /*
+         * TODO - It would be nice for CPR to keep a linked
+         * list of running threads for debugging purposes
+         * such as a show command or walking the list to ensure
+         * that an application does not attempt to create
+         * the same thread twice.
+         */
+        threadPtr->u.handleInt = threadId;
+        threadPtr->threadId = ++id;
+        return (cprThread_t)threadPtr;
+    }
+
+    /* Malloc failed */
+    CPR_ERROR("%s - Malloc for thread %s failed.\n", fname, name);
+    errno = ENOMEM;
+    return (cprThread_t)NULL;
+}
+
+
+/**
+ * cprDestroyThread
+ *
+ * @brief Destroys the thread passed in.
+ *
+ * The cprDestroyThread function is called to destroy a thread. The thread
+ * parameter may be any valid thread including the calling thread itself.
+ *
+ * @param[in] thread - thread to destroy.
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE. errno should be set for FAILURE case.
+ *
+ * @note In Linux there will never be a success indication as the
+ *       calling thread will have been terminated.
+ */
+cprRC_t
+cprDestroyThread (cprThread_t thread)
+{
+    static const char fname[] = "cprDestroyThread";
+    cpr_thread_t *cprThreadPtr;
+
+    cprThreadPtr = (cpr_thread_t *) thread;
+    if (cprThreadPtr != NULL) {
+        /*
+         * Make sure thread is trying to destroy itself.
+         */
+        if ((pthread_t) cprThreadPtr->u.handleInt == pthread_self()) {
+            cprThreadPtr->threadId = 0;
+            cpr_free(cprThreadPtr);
+            pthread_exit(NULL);
+            return CPR_SUCCESS;
+        }
+
+        CPR_ERROR("%s: Thread attempted to destroy another thread, not itself.\n",
+                  fname);
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+/**
+ * cprAdjustRelativeThreadPriority
+ *
+ * @brief The function sets the relative thread priority up or down by the given value.
+ *
+ * This function is used pSIPCC to set up the thread priority. The values of the
+ * priority range from -20 (Maximum priority) to +19 (Minimum priority).
+ *
+ * @param[in] relPri - nice value of the thread -20 is MAX and 19 is MIN
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprAdjustRelativeThreadPriority (int relPri)
+{
+    const char *fname = "cprAdjustRelativeThreadPriority";
+
+    if (setpriority(PRIO_PROCESS, 0, relPri) == -1) {
+        CPR_ERROR("%s: could not set the nice..err=%d\n",
+                  fname, errno);
+        return CPR_FAILURE;
+    }
+    return CPR_SUCCESS;
+}
+
+/**
+ * @}
+ * @addtogroup ThreadInternal Helper functions for implementing threads in CPR
+ * @ingroup Threads
+ * @brief Helper functions used by CPR for thread implementation
+ *
+ * @{
+ */
+
+/**
+ * cprGetThreadId
+ *
+ * @brief Return the pthread ID for the given CPR thread.
+ *
+ * @param[in] thread - thread to query
+ *
+ * @return Thread's Id or zero(0)
+ *
+ */
+pthread_t
+cprGetThreadId (cprThread_t thread)
+{
+    if (thread) {
+        return ((cpr_thread_t *)thread)->u.handleInt;
+    }
+    return 0;
+}
+
+/**
+  * @}
+  */
diff --git a/libs/sipcc/cpr/linux/cpr_linux_time.h b/libs/sipcc/cpr/linux/cpr_linux_time.h
new file mode 100644 (file)
index 0000000..182bab6
--- /dev/null
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_TIME_H_
+#define _CPR_LINUX_TIME_H_
+
+#include <time.h>
+
+typedef size_t cpr_time_t;
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_timers.h b/libs/sipcc/cpr/linux/cpr_linux_timers.h
new file mode 100644 (file)
index 0000000..39ebda8
--- /dev/null
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_TIMERS_H_
+#define _CPR_LINUX_TIMERS_H_
+#include <pthread.h>
+
+/*
+ * Linux does not provide native support for non-blocking timers
+ * so CPR provides that functionality for Linux.
+ */
+
+/*
+ * Determine the granularity of the timers in
+ * milliseconds. ie how often does the TickThread
+ * wake up to decrement the timer intervals
+ */
+#define timerGranularity 10
+
+//struct timerBlk_s timerBlk;
+
+typedef struct cpr_timer_s
+{
+  const char *name;
+  uint32_t cprTimerId;
+  cprMsgQueue_t callBackMsgQueue;
+  uint16_t applicationTimerId;
+  uint16_t applicationMsgId;
+  void *data;
+  union {
+    void *handlePtr;
+  }u;
+}cpr_timer_t;
+
+/* Linked List of currently running timers */
+typedef struct timerDef
+{
+    int32_t duration;
+    boolean timerActive;
+    cpr_timer_t *cprTimerPtr;
+    struct timerDef *previous;
+    struct timerDef *next;
+} timerBlk;
+
+/* Timer mutex for updating the timer linked list */
+extern pthread_mutex_t timerMutex;
+
+/* Start routines for the timer threads */
+extern void *linuxTimerTick(void *);
+
+cprRC_t cpr_timer_pre_init(void);
+cprRC_t cpr_timer_de_init(void);
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_timers_using_select.c b/libs/sipcc/cpr/linux/cpr_linux_timers_using_select.c
new file mode 100644 (file)
index 0000000..a9d8f21
--- /dev/null
@@ -0,0 +1,1300 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *  @brief CPR layer for Timers.
+ *
+ *     This file contains the Cisco Portable Runtime layer for non-blocking
+ *     timers. This implementation is for the Linux operating system using
+ *     select with a timeout.
+ *
+ *     Timer Service runs in its own thread and blocks on select
+ *     call with a timeout. The value of timeout is set equal to
+ *     the duration of the earliest expiring timer. The timer list
+ *     is kept sorted by earliest to latest expiration times. Therefore,
+ *     timeout value is simply the duration on the head of the list.
+ *
+ *     For starting or cancelling a timer, the timer library contacts
+ *     the timer service using a local socket based IPC. This is done
+ *     by bringing up a connection between timer service and the client
+ *     during timer init.
+ *     Timer Service thread is the only thread that adds or removes
+ *     timer blocks from the list. When timer library client wants
+ *     to start or cancel a timer, the library sends an IPC message
+ *     to the timer service thread on the socket connection. This
+ *     unblocks the select call. Select also returns the amount of
+ *     time left on the timeout when it gets unblocked. This amount is
+ *     first subtracted from the head timer block duration. Then rest
+ *     of the processing is carried out based on whether the request
+ *     was to add or to remove a timer block and duration
+ *     on all timer blocks is adjusted as required.
+ *     When the select times out it implies the timers at the head
+ *     of the list has expired. The list is scanned for expired timers
+ *     and expiry processing such as posting message to the handler
+ *     etc. is done.
+ *
+ *     When there are no timers in the list, service blocks on select
+ *     forever waiting for at least one timer to be added.
+ *
+ */
+
+/**
+ * @defgroup Timers The Timer implementation module
+ * @ingroup CPR
+ * @brief The module related to Timer abstraction for the pSIPCC
+ * @addtogroup TimerAPIs The Timer APIs
+ * @ingroup Timers
+ * @brief APIs expected by pSIPCC for using Timers
+ *
+ */
+
+#include "cpr.h"
+#include "cpr_socket.h"
+#include "cpr_stdlib.h"
+#include "cpr_stdio.h"
+#include "cpr_threads.h"
+#include "cpr_timers.h"
+#include "cpr_string.h"
+#include "phntask.h"
+#include <errno.h>
+#include <unistd.h>
+#include "cpr_linux_timers.h"
+#include "platform_api.h"
+
+/*--------------------------------------------------------------------------
+ * Local definitions
+ *--------------------------------------------------------------------------
+ */
+
+typedef struct timer_ipc_cmd_s
+{
+    cpr_timer_t *timer_ptr;
+    void        *user_data_ptr;
+    uint32_t    duration;
+} timer_ipc_cmd_t;
+
+
+typedef struct timer_ipc_s
+{
+    uint32_t  msg_type;
+    union
+    {
+        timer_ipc_cmd_t cmd;
+        cprRC_t         result;
+    }u;
+} timer_ipc_t;
+
+#define TMR_CMD_ADD    1
+#define TMR_CMD_REMOVE 2
+#define TMR_RESULT     3
+
+#define API_RETURN(_val) \
+    {                     \
+        pthread_mutex_unlock(&api_mutex); \
+        return (_val); \
+     }\
+
+#define API_ENTER() \
+{\
+    pthread_mutex_lock(&api_mutex);\
+}\
+
+/* for AF_LOCAL not all implementations return client addr in recvfrom so
+ * using explicit path for client too.
+ */
+#define SERVER_PATH "/tmp/CprTmrServer"
+#define CLIENT_PATH "/tmp/CprTmrClient"
+
+
+/*--------------------------------------------------------------------------
+ * Global data
+ *--------------------------------------------------------------------------
+ */
+
+
+static timerBlk *timerListHead;
+
+static pthread_t timerThreadId;
+
+
+static pthread_mutex_t api_mutex;
+
+
+/* local socket used by timer library */
+static int client_sock = INVALID_SOCKET;
+
+
+/* local socket used by the timer  service */
+static int serv_sock = INVALID_SOCKET;
+
+static struct sockaddr_un tmr_serv_addr;
+static struct sockaddr_un tmr_client_addr;
+
+static fd_set socks; /* descriptor set */
+
+
+/*--------------------------------------------------------------------------
+ * External data references
+ *--------------------------------------------------------------------------
+ */
+
+
+/*--------------------------------------------------------------------------
+ * External function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+/*
+ * Internal CPR function to fill in data in the sysheader.
+ * This is to prevent knowledge of the evil syshdr structure
+ * from spreading to cpr_linux_timers.c This thing is
+ * like kudzu...
+ */
+extern void fillInSysHeader(void *buffer, uint16_t cmd, uint16_t len,
+                            void *timerMsg);
+
+
+
+/*--------------------------------------------------------------------------
+ * Local scope function prototypes
+ *--------------------------------------------------------------------------
+ */
+
+static void     *timerThread(void *data);
+static cprRC_t  start_timer_service_loop();
+static void     process_expired_timers();
+static void     send_api_result(cprRC_t result, struct sockaddr_un *addr, socklen_t len);
+
+
+/**
+ * @addtogroup TimerAPIs The Timer APIs
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * cprSleep
+ *
+ * @brief Suspend the calling thread
+ * The cprSleep function blocks the calling thread for the indicated number of
+ * milliseconds.
+ *
+ * @param[in] duration - Number of milliseconds the thread should sleep
+ *
+ * @return -  none
+ */
+void
+cprSleep (uint32_t duration)
+{
+    /*
+     * usleep() can only support up to one second, so split
+     * between sleep and usleep if one second or more
+     */
+    if (duration >= 1000) {
+        (void) sleep(duration / 1000);
+        (void) usleep((duration % 1000) * 1000);
+    } else {
+        (void) usleep(duration * 1000);
+    }
+}
+
+/**
+  * @}
+  */
+
+/**
+ * @defgroup TimerInternal The Timer internal functions
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * addTimerToList
+ * Send message to timer service to add the timer pointed by cprTimerPtr
+ * to the list. This routine is just sending IPC message to timer service
+ * but the actual addition is done by timer service using the addTimer function.
+ * This function is only called by CPR functions and is not visible to external
+ * applications.
+ * @param[in] cprTimerPtr  - timer pointer
+ * @param[in] duration     - timer duration in msec.
+ * @param[in] data         - opaque data
+ * @return  - CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t addTimerToList (cpr_timer_t *cprTimerPtr, uint32_t duration, void *data)
+{
+
+    static const char fname[] = "addTimerToList";
+    timer_ipc_t tmr_cmd = {0};
+    timer_ipc_t tmr_rsp={0};
+
+    API_ENTER();
+
+    //CPR_INFO("%s: cprTimerptr=0x%x dur=%d user_data=%x\n",
+    //       fname, cprTimerPtr, duration, data);
+    tmr_cmd.msg_type = TMR_CMD_ADD;
+    tmr_cmd.u.cmd.timer_ptr = cprTimerPtr;
+    tmr_cmd.u.cmd.user_data_ptr = data;
+    tmr_cmd.u.cmd.duration = duration;
+
+//CPR_INFO("%s:sending messge of type=%d\n", fname, tmr_cmd.msg_type);
+    /* simply post a request here to the timer service.*/
+    if (client_sock != -1) {
+        if (sendto(client_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
+                   (struct sockaddr *)&tmr_serv_addr, sizeof(tmr_serv_addr)) < 0) {
+            CPR_ERROR("Failed to tx IPC msg to timer service, errno = %s %s\n",
+                   strerror(errno), fname);
+            API_RETURN(CPR_FAILURE);
+        }
+
+    } else {
+        CPR_ERROR("can not make IPC connection, client_sock is invalid %s\n", fname);
+        API_RETURN(CPR_FAILURE);
+    }
+
+    /*
+     * wait for the timer service to excute the request
+     * so that we get result of operation
+     */
+
+    if (recvfrom(client_sock, &tmr_rsp, sizeof(timer_ipc_t),0, NULL, NULL) < 0) {
+        //CPR_INFO("error in recving the result error=%s\n", strerror(errno));
+        API_RETURN(CPR_FAILURE);
+    } else {
+        //CPR_INFO("received response from the timer result=%d\n", tmr_rsp.u.result);
+        API_RETURN(tmr_rsp.u.result);
+    }
+}
+
+
+/**
+ * addTimer
+ *
+ * Add a timer to the timer linked list.
+ * This function is only called by CPR functions and is not visible to external
+ * applications.
+ *
+ * @param[in] cprTimerPtr - pointer to the CPR timer structure
+ * @param[in] duration    - how long before timer expires in milliseconds
+ * @param[in] data        - information to be passed to callback function
+ *
+ * @return  - CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t addTimer (cpr_timer_t *cprTimerPtr, uint32_t duration, void *data)
+{
+    static const char fname[] = "addTimer";
+    timerBlk *timerList;
+    timerBlk *newTimerPtr;
+
+    CPR_INFO("%s:adding timer=0x%x timerblk=%x\n", fname,
+           cprTimerPtr, cprTimerPtr->u.handlePtr);
+
+
+    /* Verify the timer has been initialized */
+    newTimerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+    if (newTimerPtr == NULL) {
+        CPR_ERROR("%s - Timer %s has not been initialized.\n",
+                  fname, cprTimerPtr->name);
+        errno = EINVAL;
+
+        return(CPR_FAILURE);
+    }
+
+    /* Ensure this timer is not already running */
+    if (newTimerPtr->timerActive) {
+        CPR_ERROR("%s - Timer %s is already active.\n", fname, cprTimerPtr->name);
+        errno = EAGAIN;
+        return(CPR_FAILURE);
+
+    }
+
+    /* Sanity tests passed, store the data the application passed in */
+    newTimerPtr->duration = duration;
+    cprTimerPtr->data = data;
+
+    /*
+     * Insert timer into the linked list. The timer code only
+     * decrements the first timer in the list to be efficient.
+     * Therefore, the timer at the top of the list is the timer
+     * that will expire first. Timers are added to the list
+     * in ascending order of time left before expiration and
+     * ticksLeft is calculated to be the difference between
+     * when the timer before them expires and when the newly
+     * inserted timer expires.
+     */
+
+    /* Check for insertion into an empty list */
+    if (timerListHead == NULL) {
+        //CPR_INFO("no timer in the list case..\n");
+        timerListHead = newTimerPtr;
+    } else {
+
+       /* Insert timer into list */
+        timerList = timerListHead;
+        while (timerList != NULL) {
+
+            /*
+             * If the duration on this new timer are less than the
+             * timer in the list, insert this new timer before
+             * it in the list as it will expire first. In doing so
+             * the code must subtract the new timer's duration from
+             * the duration of the timer in the in the list to keep the deltas correct.
+             */
+            if (newTimerPtr->duration < timerList->duration) {
+                //CPR_INFO("less than case..\n");
+                timerList->duration -= newTimerPtr->duration;
+                newTimerPtr->next = timerList;
+                newTimerPtr->previous = timerList->previous;
+                if (newTimerPtr->previous) {
+                    newTimerPtr->previous->next = newTimerPtr;
+                }
+                timerList->previous = newTimerPtr;
+
+                /* Check for insertion at the head of list */
+                if (timerListHead == timerList) {
+                    //CPR_INFO("insert at the head case..\n");
+                    timerListHead = newTimerPtr;
+                }
+                break;
+            } else {
+                /*
+                 * Else this new timer expires after the timer in
+                 * the list. Therefore subtract the timer's duration
+                 * from the new timer's duration. Since only the
+                 * first timer is decremented all other timers added
+                 * expiration must be calculated as a delta from the
+                 * timer in front of them in the list.
+                 */
+                //CPR_INFO("greater than case..\n");
+                newTimerPtr->duration -= timerList->duration;
+
+                /* Check for insertion at the end of list */
+                if (timerList->next == NULL) {
+                    //CPR_INFO("insert at the end sub case..\n");
+                    newTimerPtr->previous = timerList;
+                    timerList->next = newTimerPtr;
+                    newTimerPtr->next = NULL;
+                    break;
+                }
+                timerList = timerList->next;
+            }
+        }
+    }
+
+    newTimerPtr->timerActive = TRUE;
+    return(CPR_SUCCESS);
+}
+
+
+/**
+ * removeTimerFromList
+ * Send message to timer service to remove the timer pointed by cprTimerPtr
+ * from the list. This routine is just sending IPC message to timer service
+ * and the actual removal is done by timer service using the removeTimer function..
+ * This function is only called by CPR functions and is not visible to external
+ * applications.
+ *
+ * @param[in] cprTimerPtr - pointer to the timer to be removed from the list
+ * @return - CPR_SUCCESS or CPR_FAILURE
+ *
+ */
+static cprRC_t
+removeTimerFromList (cpr_timer_t *cprTimerPtr)
+{
+
+    static const char fname[] = "removeTimerFromList";
+    timer_ipc_t tmr_cmd = {0};
+    timer_ipc_t tmr_rsp = {0};
+
+
+    API_ENTER();
+
+    //CPR_INFO("%s:remove timer from list=0x%x\n",fname, cprTimerPtr);
+    tmr_cmd.msg_type = TMR_CMD_REMOVE;
+    tmr_cmd.u.cmd.timer_ptr = cprTimerPtr;
+
+    //CPR_INFO("sending messge of type=%d\n", tmr_cmd.msg_type);
+
+    /* simply post a request here to the timer service.. */
+    if (client_sock != -1) {
+        if (sendto(client_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
+                   (struct sockaddr *)&tmr_serv_addr, sizeof(tmr_serv_addr)) < 0) {
+            CPR_ERROR("%s:failed to tx IPC Msg to timer service, errno = %s\n",
+                      fname, strerror(errno));
+            API_RETURN(CPR_FAILURE);
+        }
+    } else {
+        CPR_ERROR("%s:client_sock invalid, no IPC connection \n", fname);
+        API_RETURN(CPR_FAILURE);
+    }
+
+    /*
+     * wait for the timer service to excute the request
+     * so that we get result of operation
+     */
+
+    if (recvfrom(client_sock, &tmr_rsp, sizeof(timer_ipc_t),0, NULL, NULL) < 0) {
+        //CPR_INFO("error in recving the result error=%s\n", strerror(errno));
+        API_RETURN(CPR_FAILURE);
+    } else {
+        //CPR_INFO("received response from the timer result=%d\n", tmr_rsp.u.result);
+        API_RETURN(tmr_rsp.u.result);
+    }
+}
+
+
+/**
+ * removeTimer
+ *
+ * Remove a timer from the timer linked list. This function is only
+ * called by CPR functions and is not visible to applications.
+ *
+ * @param[in] cprTimerPtr - which timer to cancel
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t
+removeTimer (cpr_timer_t *cprTimerPtr)
+{
+    static const char fname[] = "removeTimer";
+    timerBlk *timerList;
+    timerBlk *previousTimer;
+    timerBlk *nextTimer;
+    timerBlk *timerPtr;
+
+    //CPR_INFO("removing timer..0x%x\n", cprTimerPtr);
+
+    /*
+     * No need to sanitize the cprTimerPtr data as only
+     * internal CPR functions call us and they have already
+     * sanitized that data. In addition those functions
+     * have already grabbed the timer list mutex so no need
+     * to do that here.
+     */
+    timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+    //CPR_INFO("%s: timer ptr=%x\n", fname, timerPtr);
+
+    if (timerPtr != NULL) {
+        /* Walk the list looking for this timer to cancel. */
+        timerList = timerListHead;
+        while (timerList != NULL) {
+            if (timerList->cprTimerPtr->cprTimerId == cprTimerPtr->cprTimerId) {
+                /* Removing only element in the list */
+                if ((timerList->previous == NULL) &&
+                    (timerList->next == NULL)) {
+                    timerListHead = NULL;
+
+                /* Removing head of the list */
+                } else if (timerList->previous == NULL) {
+                    nextTimer = timerList->next;
+                    nextTimer->previous = NULL;
+                    timerListHead = nextTimer;
+
+                /* Removing tail of the list */
+                } else if (timerList->next == NULL) {
+                    previousTimer = timerList->previous;
+                    previousTimer->next = NULL;
+
+                /* Removing from middle of the list */
+                } else {
+                    nextTimer = timerList->next;
+                    previousTimer = timerList->previous;
+                    previousTimer->next = nextTimer;
+                    nextTimer->previous = previousTimer;
+                }
+
+                /* Add time back to next timer in the list */
+                if (timerList->next) {
+                    timerList->next->duration += timerList->duration;
+                }
+
+                /*
+                 * Reset timer values
+                 */
+                timerList->next = NULL;
+                timerList->previous = NULL;
+                timerList->duration = -1;
+                timerList->timerActive = FALSE;
+                cprTimerPtr->data = NULL;
+
+                return(CPR_SUCCESS);
+
+            }
+
+            /* Walk the list */
+            timerList = timerList->next;
+        }
+
+        /*
+         * Either the timer was not active or it was marked active, but not
+         * found on the timer list. If the timer was inactive then that is OK,
+         * but let user know about an active timer not found in the timer list.
+         */
+        if ((timerPtr->next != NULL) || (timerPtr->previous != NULL)) {
+            CPR_ERROR("%s - Timer %s marked as active, "
+                      "but was not found on the timer list.\n",
+                      fname, cprTimerPtr->name);
+            timerPtr->next = NULL;
+            timerPtr->previous = NULL;
+        }
+        timerPtr->duration = -1;
+        timerPtr->cprTimerPtr->data = NULL;
+        timerPtr->timerActive = FALSE;
+
+        return(CPR_SUCCESS);
+
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - Timer not initialized.\n", fname);
+    errno = EINVAL;
+    return(CPR_FAILURE);
+
+}
+
+/**
+ * @}
+ * @addtogroup TimerAPIs The Timer APIs
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * cprCreateTimer
+ *
+ * @brief Initialize a timer
+ *
+ * The cprCreateTimer function is called to allow the OS to perform whatever
+ * work is needed to create a timer. The input name parameter is optional. If present, CPR assigns
+ * this name to the timer to assist in debugging. The callbackMsgQueue is the
+ * address of a message queue created with cprCreateMsgQueue. This is the
+ * queue where the timer expire message will be sent.
+ * So, when this timer expires a msg of type "applicationMsgId" will be sent to the msg queue
+ * "callbackMsgQueue" indicating that timer applicationTimerId has expired.
+ *
+ * @param[in]   name               -  name of the timer
+ * @param[in]   applicationTimerId - ID for this timer from the application's
+ *                                  perspective
+ * @param[in]   applicationMsgId   - ID for syshdr->cmd when timer expire msg
+ *                                  is sent
+ * @param[in]   callBackMsgQueue   - where to send a msg when this timer expires
+ *
+ * @return  Timer handle or NULL if creation failed.
+ */
+cprTimer_t
+cprCreateTimer (const char *name,
+                uint16_t applicationTimerId,
+                uint16_t applicationMsgId,
+                cprMsgQueue_t callBackMsgQueue)
+{
+    static const char fname[] = "cprCreateTimer";
+    static uint32_t cprTimerId = 0;
+    cpr_timer_t *cprTimerPtr;
+    timerBlk *timerPtr;
+
+    /*
+     * Malloc memory for a new timer. Need to
+     * malloc memory for the generic CPR view and
+     * one for the CNU specific version.
+     */
+    cprTimerPtr = (cpr_timer_t *) cpr_malloc(sizeof(cpr_timer_t));
+    timerPtr = (timerBlk *) cpr_malloc(sizeof(timerBlk));
+    if ((cprTimerPtr != NULL) && (timerPtr != NULL)) {
+        /* Assign name (Optional) */
+        cprTimerPtr->name = name;
+
+        /* Set timer ids, msg id and callback msg queue (Mandatory) */
+        cprTimerPtr->applicationTimerId = applicationTimerId;
+        cprTimerPtr->applicationMsgId = applicationMsgId;
+        cprTimerPtr->cprTimerId = cprTimerId++;
+        if (callBackMsgQueue == NULL) {
+            CPR_ERROR("%s - Callback msg queue for timer %s is NULL.\n",
+                      fname, name);
+            cpr_free(timerPtr);
+            cpr_free(cprTimerPtr);
+            return NULL;
+        }
+        cprTimerPtr->callBackMsgQueue = callBackMsgQueue;
+
+        /*
+         * Set remaining values in both structures to defaults
+         */
+        timerPtr->next = NULL;
+        timerPtr->previous = NULL;
+        timerPtr->duration = -1;
+        timerPtr->timerActive = FALSE;
+        cprTimerPtr->data = NULL;
+
+        /*
+         * TODO - It would be nice for CPR to keep a linked
+         * list of active timers for debugging purposes
+         * such as a show command or walking the list to ensure
+         * that an application does not attempt to create
+         * the same timer twice.
+         *
+         * TODO - It would be nice to initialize of pool of
+         * timers at init time and have this function just
+         * return a timer from the pool. Then when the
+         * timer expired or cancel the code would not free
+         * it, but just return it to the pool.
+         */
+        timerPtr->cprTimerPtr = cprTimerPtr;
+        cprTimerPtr->u.handlePtr = timerPtr;
+        //CPR_INFO("cprTimerCreate: timer_t=%x blk=%x\n",cprTimerPtr, timerPtr);
+
+        return cprTimerPtr;
+    }
+
+    /*
+     * If we get here there has been a malloc failure.
+     */
+    if (timerPtr) {
+        cpr_free(timerPtr);
+    }
+    if (cprTimerPtr) {
+        cpr_free(cprTimerPtr);
+    }
+
+    /* Malloc failed */
+    CPR_ERROR("%s - Malloc for timer %s failed.\n", fname, name);
+    errno = ENOMEM;
+    return NULL;
+}
+
+
+/**
+ * cprStartTimer
+ *
+ * @brief Start a system timer
+ *
+ * The cprStartTimer function starts a previously created timer referenced by
+ * the parameter timer. CPR timer granularity is 10ms. The "timer" input
+ * parameter is the handle returned from a previous successful call to
+ * cprCreateTimer.
+ *
+ * @param[in]  timer    - which timer to start
+ * @param[in]  duration - how long before timer expires in milliseconds
+ * @param[in]  data     - information to be passed to callback function
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprStartTimer (cprTimer_t timer,
+               uint32_t duration,
+               void *data)
+{
+    static const char fname[] = "cprStartTimer";
+    cpr_timer_t *cprTimerPtr;
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        /* add timer to the list */
+        return addTimerToList(cprTimerPtr, duration, data);
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprIsTimerRunning
+ *
+ * @brief Determine if a timer is active
+ *
+ * This function determines whether the passed in timer is currently active. The
+ * "timer" parameter is the handle returned from a previous successful call to
+ *  cprCreateTimer.
+ *
+ * @param[in] timer - which timer to check
+ *
+ * @return True is timer is active, False otherwise
+ */
+boolean
+cprIsTimerRunning (cprTimer_t timer)
+{
+    static const char fname[] = "cprIsTimerRunning";
+    cpr_timer_t *cprTimerPtr;
+    timerBlk *timerPtr;
+
+    //CPR_INFO("istimerrunning(): timer=0x%x\n", timer);
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+        if (timerPtr == NULL) {
+            CPR_ERROR("%s - Timer %s has not been initialized.\n",
+                      fname, cprTimerPtr->name);
+            errno = EINVAL;
+            return FALSE;
+        }
+
+        if (timerPtr->timerActive) {
+            return TRUE;
+        }
+    } else {
+        /* Bad application! */
+        CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+        errno = EINVAL;
+    }
+
+    return FALSE;
+}
+
+
+/**
+ * cprCancelTimer
+ *
+ * @brief Cancels a running timer
+ *
+ * The cprCancelTimer function cancels a previously started timer referenced by
+ * the parameter timer.
+ *
+ * @param[in] timer - which timer to cancel
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprCancelTimer (cprTimer_t timer)
+{
+    static const char fname[] = "cprCancelTimer";
+    timerBlk *timerPtr;
+    cpr_timer_t *cprTimerPtr;
+    cprRC_t rc = CPR_SUCCESS;
+
+    //CPR_INFO("cprCancelTimer: timer ptr=%x\n", timer);
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
+        if (timerPtr == NULL) {
+            CPR_ERROR("%s - Timer %s has not been initialized.\n",
+                      fname, cprTimerPtr->name);
+            errno = EINVAL;
+            return CPR_FAILURE;
+        }
+
+        /*
+         * Ensure timer is active before trying to remove it.
+         * If already inactive then just return SUCCESS.
+         */
+        if (timerPtr->timerActive) {
+            //CPR_INFO("removing timer from the list=%x\n", timerPtr);
+            rc = removeTimerFromList(timer);
+        }
+        return rc;
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprUpdateTimer
+ *
+ * @brief Updates the expiration time for a running timer
+ *
+ * The cprUpdateTimer function cancels a previously started timer referenced by
+ * the parameter timer and then restarts the same timer with the duration passed
+ * in.
+ *
+ * @param[in]   timer    - which timer to update
+ * @param[in]   duration - how long before timer expires in milliseconds
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprUpdateTimer (cprTimer_t timer, uint32_t duration)
+{
+    static const char fname[] = "cprUpdateTimer";
+    cpr_timer_t *cprTimerPtr;
+    void *timerData;
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        /* Grab data before cancelling timer */
+        timerData = cprTimerPtr->data;
+    } else {
+        CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+        errno = EINVAL;
+        return CPR_FAILURE;
+    }
+
+    if (cprCancelTimer(timer) == CPR_SUCCESS) {
+        if (cprStartTimer(timer, duration, timerData) == CPR_SUCCESS) {
+            return CPR_SUCCESS;
+        } else {
+            CPR_ERROR("%s - Failed to start timer %s\n",
+                      fname, cprTimerPtr->name);
+            return CPR_FAILURE;
+        }
+    }
+
+    CPR_ERROR("%s - Failed to cancel timer %s\n", fname, cprTimerPtr->name);
+    return CPR_FAILURE;
+}
+
+
+/**
+ * cprDestroyTimer
+ *
+ * @brief Destroys a timer.
+ *
+ * This function will cancel the timer and then destroy it. It sets
+ * all links to NULL and then frees the timer block.
+ *
+ * @param[in] timer - which timer to destroy
+ *
+ * @return  CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t
+cprDestroyTimer (cprTimer_t timer)
+{
+    static const char fname[] = "cprDestroyTimer";
+    cpr_timer_t *cprTimerPtr;
+    cprRC_t rc;
+
+    //CPR_INFO("cprDestroyTimer:destroying timer=%x\n", timer);
+
+    cprTimerPtr = (cpr_timer_t *) timer;
+    if (cprTimerPtr != NULL) {
+        rc = cprCancelTimer(timer);
+        if (rc == CPR_SUCCESS) {
+            cprTimerPtr->cprTimerId = 0;
+            cpr_free(cprTimerPtr->u.handlePtr);
+            cpr_free(cprTimerPtr);
+            return CPR_SUCCESS;
+        } else {
+            CPR_ERROR("%s - Cancel of Timer %s failed.\n",
+                      fname, cprTimerPtr->name);
+            return CPR_FAILURE;
+        }
+    }
+
+    /* Bad application! */
+    CPR_ERROR("%s - NULL pointer passed in.\n", fname);
+    errno = EINVAL;
+    return CPR_FAILURE;
+}
+
+/**
+ * @}
+ * @addtogroup TimerInternal The Timer internal functions
+ * @ingroup Timers
+ * @{
+ */
+
+/**
+ * cpr_timer_pre_init
+ *
+ * @brief Initalize timer service and client IPC
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cpr_timer_pre_init (void)
+{
+    static const char fname[] = "cpr_timer_pre_init";
+    int32_t returnCode;
+
+    /* start the timer service first */
+    returnCode = (int32_t)pthread_create(&timerThreadId, NULL, timerThread, NULL);
+    if (returnCode == -1) {
+        CPR_ERROR("%s: Failed to create Timer Thread : %s\n", fname, strerror(errno));
+        return CPR_FAILURE;
+    }
+
+    /*
+     * wait some time so that timer service thread is up
+     * TBD:we should really implement wait on timerthread using condvar.
+     */
+    cprSleep(1000);
+
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * cpr_timer_de_init
+ *
+ * @brief De-Initalize timer service and client IPC
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t cpr_timer_de_init(void)
+{
+    // close all sockets..
+    close(client_sock);
+    close(serv_sock);
+
+
+    // destroy api mutex
+    pthread_mutex_destroy(&api_mutex);
+
+    return CPR_SUCCESS;
+}
+
+
+/**
+ * Timer service thread
+ *
+ */
+/**
+ * timerThread
+ *
+ * @brief Timer service thread
+ *
+ * This is the start function for the timer server thread.
+ *
+ * @param[in] data - The data passed in (UNUSED)
+ *
+ * @return  This function eventually starts an infinite loop on a "select".
+ */
+void *timerThread (void *data)
+{
+    static const char fname[] = "timerThread";
+
+    //CPR_INFO("timerThread:started..\n");
+#ifndef HOST
+#ifndef PTHREAD_SET_NAME
+#define PTHREAD_SET_NAME(s)     do { } while (0)
+#endif
+    PTHREAD_SET_NAME("CPR Timertask");
+#endif
+
+    /*
+     * Increase the timer thread priority from default priority.
+     * This is required to make sure timers fire with reasonable precision.
+     *
+     * NOTE: always make sure the priority is higher than sip/gsm threads;
+     * otherwise, we must use mutex around the following while loop.
+     */
+    (void) cprAdjustRelativeThreadPriority(TIMER_THREAD_RELATIVE_PRIORITY);
+
+    /* get ready to listen for timer commands and service them */
+    if (start_timer_service_loop() == CPR_FAILURE) {
+        CPR_ERROR("%s: timer service loop failed\n", fname);
+    }
+
+    return NULL;
+}
+
+
+/**
+ * local_bind
+ * Function used to do a bind on the local socket.
+ *
+ * @param[in]  sock  - socket descriptor to bind
+ * @param[in]  name  - name to use for binding local socket
+ * @return 0 if success, -1 on error (errno will be set)
+ *
+ */
+static int local_bind (int sock, char *name)
+{
+    struct sockaddr_un addr;
+
+    /* construct the address structure */
+    addr.sun_family = AF_LOCAL;
+    sstrncpy(addr.sun_path, name, sizeof(addr.sun_path));
+    /* make sure file doesn't already exist */
+    unlink(addr.sun_path);
+
+    return bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+}
+
+/**
+ * select_sockets
+ *
+ * Set the socket descriptors to be used for reading.
+ * Only server side uses select.
+ *
+ * @return The server socket number
+ */
+static int select_sockets (void)
+{
+    FD_ZERO(&socks);
+
+    FD_SET(serv_sock, &socks);
+
+    return (serv_sock);
+}
+
+
+/**
+ * read_timer_cmd
+ * read message received on the IPC from the client
+ * the only messages are timer commands {add, remove}
+ *
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+static cprRC_t read_timer_cmd ()
+{
+    static const char fname[] = "read_timer_cmd";
+    int  rcvlen;
+    timer_ipc_t tmr_cmd ={0};
+    cprRC_t ret = CPR_FAILURE;
+
+
+
+    rcvlen =recvfrom(serv_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
+                     NULL, NULL);
+
+    if (rcvlen > 0) {
+        //CPR_INFO("got message type=%d\n", tmr_cmd.msg_type);
+        switch(tmr_cmd.msg_type) {
+       case TMR_CMD_ADD:
+            //CPR_INFO("request to add timer ptr=%x duration=%d datptr=%x\n",
+            //       tmr_cmd.u.cmd.timer_ptr, tmr_cmd.u.cmd.duration, tmr_cmd.u.cmd.user_data_ptr);
+
+            ret = addTimer(tmr_cmd.u.cmd.timer_ptr,tmr_cmd.u.cmd.duration,
+                     (void *)tmr_cmd.u.cmd.user_data_ptr);
+
+            break;
+
+       case TMR_CMD_REMOVE:
+            //CPR_INFO("request to remove timer ptr=%x\n", tmr_cmd.u.cmd.timer_ptr);
+            ret = removeTimer(tmr_cmd.u.cmd.timer_ptr);
+            break;
+
+        default:
+            CPR_ERROR("%s:invalid ipc command = %d\n", tmr_cmd.msg_type);
+            ret = CPR_FAILURE;
+            break;
+        }
+    } else {
+        CPR_ERROR("%s:while reading serv_sock err =%s: Closing Socket..Timers not operational !!! \n",
+                  fname, strerror(errno));
+        (void) close(serv_sock);
+        serv_sock = INVALID_SOCKET;
+        ret = CPR_FAILURE;
+    }
+
+    /* send the result back */
+    send_api_result(ret, &tmr_client_addr, sizeof(tmr_client_addr));
+
+    return (ret);
+
+}
+
+/**
+ * send_api_result back to client via a socket sendto operation
+ * @param[in] retVal - value of result
+ * @param[in] addr   - address to send the result to
+ * @param[in] len    - length of addr
+ */
+void send_api_result(cprRC_t retVal, struct sockaddr_un *addr, socklen_t len)
+{
+    static const char fname[] = "send_api_result";
+    timer_ipc_t tmr_rsp = {0};
+
+    tmr_rsp.msg_type = TMR_RESULT;
+    tmr_rsp.u.result = retVal;
+    if (sendto(serv_sock, &tmr_rsp, sizeof(timer_ipc_t),0, (struct sockaddr *)addr, len) < 0) {
+        CPR_ERROR("%s: error in sending on serv_sock err=%s\n", fname, strerror(errno));
+    }
+}
+
+/**
+ * Start the timer service loop.
+ * Service loop waits for timer commands from timer clients processes them.
+ * Waiting is done by issuing a select call with a timeout. This is the main
+ * function that implements the timer functionality using the select call.
+ * timeout value = duration on the head of the timer list.
+ * @return CPR_SUCCESS or CPR_FAILURE
+ */
+cprRC_t start_timer_service_loop (void)
+{
+    static const char fname[] = "start_timer_service_loop";
+    int lsock = -1;
+    struct timeval tv;
+    int ret;
+    boolean use_timeout;
+
+
+    /* initialize server and client addresses used for sending.*/
+    cpr_set_sockun_addr((cpr_sockaddr_un_t *) &tmr_serv_addr,   SERVER_PATH, getpid());
+    cpr_set_sockun_addr((cpr_sockaddr_un_t *) &tmr_client_addr, CLIENT_PATH, getpid());
+
+    /*
+     * init mutex and cond var.
+     * these are used for making API synchronous etc..
+     */
+    if (pthread_mutex_init(&api_mutex, NULL) != 0) {
+        CPR_ERROR("%s: failed to initialize api_mutex err=%s\n", fname,
+                  strerror(errno));
+        return CPR_FAILURE;
+    }
+
+
+    /* open a unix datagram socket for client library */
+    client_sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
+    if (client_sock == INVALID_SOCKET) {
+        CPR_ERROR("%s:could not create client socket error=%s\n", fname, strerror(errno));
+        return CPR_FAILURE;
+    }
+
+    /* bind service name to the socket */
+    if (local_bind(client_sock,tmr_client_addr.sun_path) < 0) {
+        CPR_ERROR("%s:could not bind local socket:error=%s\n", fname, strerror(errno));
+        (void) close(client_sock);
+        client_sock = INVALID_SOCKET;
+        return CPR_FAILURE;
+    }
+
+    /* open another unix datagram socket for timer service */
+    serv_sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
+    if (serv_sock == INVALID_SOCKET) {
+        CPR_ERROR("%s:could not create server socket error=%s\n", fname, strerror(errno));
+        serv_sock = INVALID_SOCKET;
+        close(client_sock);
+        client_sock = INVALID_SOCKET;
+        return CPR_FAILURE;
+    }
+
+    if (local_bind(serv_sock, tmr_serv_addr.sun_path) < 0) {
+        CPR_ERROR("%s:could not bind serv socket:error=%s\n", fname, strerror(errno));
+        (void) close(serv_sock);
+        (void) close(client_sock);
+        client_sock = serv_sock = INVALID_SOCKET;
+        return CPR_FAILURE;
+    }
+
+
+    while (1) {
+
+        lsock = select_sockets();
+
+       /* set the timer equal to duration on head */
+       if (timerListHead != NULL) {
+            tv.tv_sec = (timerListHead->duration)/1000;
+            tv.tv_usec = (timerListHead->duration%1000)*1000;
+            //CPR_INFO("%s:time duration on head =%d sec:%d usec (or %d msec)\n",
+            //       fname, tv.tv_sec, tv.tv_usec,
+            //       timerListHead->duration);
+            use_timeout = TRUE;
+       } else {
+            //CPR_INFO("%s:no timer in the list.. will block until there is one\n",
+            //       fname);
+            use_timeout = FALSE;
+       }
+
+        ret = select(lsock + 1, &socks, NULL, NULL, (use_timeout == TRUE) ? &tv:NULL);
+
+        if (ret == -1) {
+            CPR_ERROR("%s:error in select err=%s\n", fname,
+                      strerror(errno));
+            return(CPR_FAILURE);
+        } else if (ret == 0) {
+            /*
+             * this means the head timer has expired..there could be others
+             */
+            timerListHead->duration = 0;
+            process_expired_timers();
+        } else {
+
+            if (FD_ISSET(serv_sock, &socks)) {
+                //CPR_INFO("Got something on serv_sock..\n");
+                /* first reduce the duration of the head by current run time */
+                if (timerListHead != NULL) {
+                    //CPR_INFO("set head duration to =%d prev was= %d\n",
+                    //       tv.tv_sec * 1000 + (tv.tv_usec/1000),
+                    //       timerListHead->duration);
+                    /* set the head with the remaining duration(tv) as indicated by select */
+                    timerListHead->duration = tv.tv_sec * 1000 + (tv.tv_usec/1000);
+                }
+                /* read the ipc message to remove or add a timer */
+                (void) read_timer_cmd();
+            }
+       }
+    }
+
+}
+
+/**
+ *
+ * Process the timers expired. Generally this is called when head timer
+ * has expired.
+ * @note we need to process the list as there could be
+ * other timers too in the list which have expired.
+ *
+ */
+void process_expired_timers() {
+    static const char fname[] = "process_expired_timer";
+    cprCallBackTimerMsg_t *timerMsg;
+    void *syshdr;
+    boolean processingTimers;
+
+    /* nothing to do if no timers running */
+    if (timerListHead == NULL) {
+        return;
+    }
+
+    /* nothing to do if head has not expired */
+    if (timerListHead->duration > 0) {
+        return;
+    }
+
+
+    /* There are one or more expired timers on the list */
+    processingTimers = TRUE;
+    while (processingTimers) {
+        if (timerListHead != NULL) {
+            /*
+             * Send msg to queue to indicate this timer has expired
+             */
+            if (timerListHead->duration <= 0) {
+                timerMsg = (cprCallBackTimerMsg_t *)
+                    cpr_malloc(sizeof(cprCallBackTimerMsg_t));
+                if (timerMsg) {
+                    timerMsg->expiredTimerName =
+                        timerListHead->cprTimerPtr->name;
+                    //CPR_INFO("%s: timer %s expired..\n",fname,
+                    //       timerMsg->expiredTimerName);
+
+                    timerMsg->expiredTimerId =
+                        timerListHead->cprTimerPtr->applicationTimerId;
+                    timerMsg->usrData =
+                        timerListHead->cprTimerPtr->data;
+                    syshdr = cprGetSysHeader(timerMsg);
+                    if (syshdr) {
+                        fillInSysHeader(syshdr,
+                                        timerListHead->cprTimerPtr->applicationMsgId,
+                                        sizeof(cprCallBackTimerMsg_t), timerMsg);
+                        if (cprSendMessage(timerListHead->cprTimerPtr->callBackMsgQueue,
+                                           timerMsg, (void **) &syshdr) == CPR_FAILURE) {
+                            cprReleaseSysHeader(syshdr);
+                            cpr_free(timerMsg);
+                            CPR_ERROR("%s - Call to cprSendMessage failed\n", fname);
+                            CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
+                                      fname, timerListHead->cprTimerPtr->name);
+                        }
+                    } else {
+                        cpr_free(timerMsg);
+                        CPR_ERROR("%s - Call to cprGetSysHeader failed\n", fname);
+                        CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
+                                  fname, timerListHead->cprTimerPtr->name);
+                    }
+                } else {
+                    CPR_ERROR("%s - Call to cpr_malloc failed\n", fname);
+                    CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
+                              fname, timerListHead->cprTimerPtr->name);
+                }
+                (void) removeTimer(timerListHead->cprTimerPtr);
+            } else {
+                /* The rest of the timers on the list have not yet expired */
+                processingTimers = FALSE;
+            }
+        } else {
+            /* The timer list is now empty */
+            processingTimers = FALSE;
+        }
+    } /* still more to process */
+}
+
+/**
+  * @}
+  */
diff --git a/libs/sipcc/cpr/linux/cpr_linux_tst.c b/libs/sipcc/cpr/linux/cpr_linux_tst.c
new file mode 100644 (file)
index 0000000..5fb7787
--- /dev/null
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr.h"
+#include "cpr_stdlib.h"
+#include "cpr_linux_tst.h"
+#include "cpr_errno.h"
+#include <stdarg.h>
+#include "plat_api.h"
+
+void
+err_exit (void)
+{
+    *(volatile int *) 0xdeadbeef = 0x12345678;
+}
+
+
+/* main process entry function */
+int
+main (int argc, char **argv, char **env)
+{
+    int ret;
+    char *q;
+
+    buginf("CPR test...\n");
+    //err_exit();
+
+    cprTestCmd(argc, argv);
+
+    return 0;
+}
+
+
+long
+strlib_mem_used (void)
+{
+    return (0);
+}
+
+#define LOG_MAX 255
+
+int
+debugif_printf (const char *_format, ...)
+{
+    char fmt_buf[LOG_MAX + 1];
+    int rc;
+    va_list ap;
+
+    va_start(ap, _format);
+    rc = vsnprintf(fmt_buf, LOG_MAX, _format, ap);
+    va_end(ap);
+
+    if (rc <= 0) {
+        return rc;
+    }
+    printf("%s", fmt_buf);
+
+    return rc;
+}
+
diff --git a/libs/sipcc/cpr/linux/cpr_linux_tst.h b/libs/sipcc/cpr/linux/cpr_linux_tst.h
new file mode 100644 (file)
index 0000000..8ce2ca4
--- /dev/null
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_TST_H_
+#define _CPR_LINUX_TST_H_
+
+#define APP_NAME "SIPCC-"
+#define DEB_F_PREFIX APP_NAME"%s: %s: "
+#define CPR "CPR"
+#define DEB_F_PREFIX_ARGS(msg_name, func_name) msg_name, func_name
+
+#endif
diff --git a/libs/sipcc/cpr/linux/cpr_linux_types.h b/libs/sipcc/cpr/linux/cpr_linux_types.h
new file mode 100644 (file)
index 0000000..6eca4a1
--- /dev/null
@@ -0,0 +1,157 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CPR_LINUX_TYPES_H_
+#define _CPR_LINUX_TYPES_H_
+
+#include "sys/types.h"
+#include "stddef.h"
+#include "inttypes.h"
+
+/**
+ * @typedef boolean
+ *
+ * Define boolean as an unsigned byte
+ *
+ * @note There are differences within TNP header files
+ *    @li curses.h:   bool => char
+ *    @li types.h:    boolean_t => enum
+ *    @li dki_lock.h: bool_t => int
+ */
+typedef uint8_t boolean;
+
+/*
+ * Define size_t
+ *    defined in numerous header files
+ */
+/* DONE (sys/types.h => unsigned int) */
+
+/*
+ * Define ssize_t
+ */
+/* DONE (sys/types.h => int) */
+
+/*
+ * Define MIN/MAX
+ *    defined in param.h
+ *
+ * The GNU versions of the MAX and MIN macros do two things better than
+ * the old versions:
+ * 1. they are more optimal as they only evaluate a & b once by creating a
+ *    a variable of each type on the local stack.
+ * 2. they fix potential errors due to side-effects where a and b were
+ *    evaluated twice, i.e. MIN(i++,j++)
+ *
+ * @note b could be cast to a's type, to help with usage where the code
+ *       compares signed and unsigned types.
+ */
+#ifndef MIN
+#ifdef __GNUC__
+#define MIN(a,b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; })
+#else
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#endif
+
+#ifndef MAX
+#ifdef __GNUC__
+#define MAX(a,b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; })
+#else
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#endif
+
+
+/*
+ * Define NULL
+ *    defined in numerous header files
+ */
+/* DONE (stddef.h) */
+
+/**
+ * @def NUL
+ *
+ * Define NUL for string termination
+ */
+#ifndef NUL
+#define NUL '\0'
+#endif
+
+/**
+ * @def RESTRICT
+ *
+ * If suppoprting the ISO/IEC 9899:1999 standard,
+ * use the '__restrict' keyword
+ */
+#if defined(_POSIX_C_SOURCE) && defined(__GNUC__)
+#define RESTRICT __restrict
+#else
+#define RESTRICT
+#endif
+
+/**
+ * @def CONST
+ *
+ * Define CONST as @c const, if supported
+ */
+#define CONST const
+
+/**
+ * @def INLINE
+ *
+ * Define the appropriate setting for inlining functions
+ */
+#ifdef __STRICT_ANSI__
+#define INLINE
+#else
+#define INLINE __inline__
+#endif
+
+/**
+ * __BEGIN_DECLS and __END_DECLS
+ *
+ * Define macros for compilation by C++ compiler
+ */
+#ifndef __BEGIN_DECLS
+#ifdef __cplusplus
+#define __BEGIN_DECLS extern "C" {
+#else
+#define __BEGIN_DECLS
+#endif
+#endif
+
+#ifndef __END_DECLS
+#ifdef __cplusplus
+#define __END_DECLS   }
+#else
+#define __END_DECLS
+#endif
+#endif
+
+/**
+ * Define TRUE/FALSE
+ *     defined in several header files
+ */
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/*
+ * Define offsetof
+ */
+/* DONE (stddef.h) */
+
+/**
+ * @def FIELDOFFSET(struct name, field name)
+ *
+ * Macro to generate offset from a given field in a structure
+ */
+#define FIELDOFFSET(struct_name, field_name) (size_t)(&(((struct_name *)0)->field_name))
+
+
+#endif
diff --git a/libs/sipcc/include/cc_blf.h b/libs/sipcc/include/cc_blf.h
new file mode 100644 (file)
index 0000000..106b283
--- /dev/null
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_BLF_H_
+#define _CC_BLF_H_
+#include "cc_constants.h"
+
+/**
+ * Initialize the BLF stack
+ * @return
+ */
+int CC_BLF_init();
+/**
+ * Start BLF subscription
+ * @param request_id the request id
+ * @param duration the subscription duration
+ * @param watcher the DN of subscription watcher
+ * @param presentity the DN that is watched/monitored.
+ * @param app_id the application id for the BLF
+ * @param feature_mask please refer to cc_blf_feature_mask_t
+ * @return void
+ */
+void CC_BLF_subscribe(int request_id,
+               int duration,
+               const char *watcher,
+        const char *presentity,
+        int app_id,
+        cc_blf_feature_mask_t feature_mask);
+/**
+ * Unsubscribe the BLF subscription
+ * @param request_id the request id
+ * @return void
+ */
+void CC_BLF_unsubscribe(int request_id);
+
+/**
+ * Unsubscribe all BLF subscription
+ * @return void
+ */
+void CC_BLF_unsubscribe_All();
+
+#endif /* _CC_BLF_H_ */
+
diff --git a/libs/sipcc/include/cc_blf_listener.h b/libs/sipcc/include/cc_blf_listener.h
new file mode 100644 (file)
index 0000000..af03530
--- /dev/null
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_BLF_LISTENER_H_
+#define _CC_BLF_LISTENER_H_
+
+#include "cc_constants.h"
+
+/**
+ * Update BLF notification
+ * @param request_id
+ * @param blf_state the BLF state, it depends on app_id (0 for call list) or line features (speeddial blf
+ * @param app_id
+ * @return void
+ */
+void CC_BLF_notification(int request_id, cc_blf_state_t blf_state, int app_id);
+
+#endif /* _CC_BLF_LISTENER_H_ */
+
diff --git a/libs/sipcc/include/cc_call_feature.h b/libs/sipcc/include/cc_call_feature.h
new file mode 100644 (file)
index 0000000..e1fbb26
--- /dev/null
@@ -0,0 +1,340 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *  @mainpage Portable SIP Stack API
+ *
+ *  @section intro_sec Introduction
+ *  The portable SIP stack is used in multiple SIP endpoints. This document
+ *  describes the API's provided by the portable SIP stack that third party
+ *  vendors must implement to use the stack.
+ *
+ *
+ *  @section sub_sec Functionality
+ *  The SIP stack can be viewed as composed of different components each
+ *  dealing with a specific functionality.
+ *  functional modules in SIP stack
+ *
+ *  @subsection Management  Management
+ *    This section covers the API that deals with managing the sip stack
+ *    initialization, shutdown and state management.
+ *    @li cc_types.h @par
+ *      Type definitions used by SIP stack
+ *    @li cc_constants.h @par
+ *      Constant definitions used by call control
+ *    @li cc_config.h @par
+ *      This section covers API to set config data for the SIP stack
+ *    @li cc_service.h @par
+ *      Commands to initialize, restart, shutdown sipstack
+ *    @li cc_service_listener.h @par
+ *      Callbacks from SIP stack for SIP stack and registration status updates
+ *
+ *  @subsection cf Call Features
+ *    Call Control features and calls related APIs
+ *    @li cc_call_feature.h @par
+ *      APIs to create/terminate calls and invoke call features on them
+ *    @li cc_call_listener.h @par
+ *      Callbacks for call states and call related data updates
+ *    @li cc_info.h @par
+ *      API to send a SIP INFO info Message in the context of a call
+ *    @li cc_info_listener.h @par
+ *      Callback on receipt of SIP INFO Message in the context of a call
+ *
+ *  @subsection df Device Features
+ *    Device based features related APIs
+ *    @li cc_device_feature.h @par
+ *      APIs to invoke device specific features
+ *    @li cc_device_listener.h @par
+ *      Callbacks for device specific feature/data updates
+ *
+ *  @subsection blf BLF APIs
+ *    This covers Busy Lamp Field subscription and state notifications
+ *    @li cc_blf.h @par
+ *      BLF subscription and unsubscription APIs
+ *    @li cc_blf_listener.h @par
+ *      BLF state change notification call backs
+ *
+ *  @subsection vcm  Media APIs
+ *    Media related APIs
+ *    @li vcm.h @par
+ *      This file contains API that interface to the media layer on the platform.
+ *      The following APIs need to be implemented to have the sip stack interact
+ *      and issue commands to the media layer.
+ *    @li ccsdp.h @par
+ *      Contains helper methods to peek and set video SDP attributes as the SDP negotiation for m lines needs
+ *      to be confirmed by the application after examining the SDP attributes. See vcmCheckAttribs and
+ *      vcmPopulateAttribs methods in vcm.h
+ *
+ *  @subsection xml XML
+ *    This section covers the XML Parser API that are invoked by the SIP stack to parse  and encode XML bodies
+ *    @li xml_parser.h @par
+ *      Methods to be implemented by platform for XML enoced and decode operations
+ *    @li xml_parser_defines.h @par
+ *      Type definitons and data structures used by the XML APIs
+ *
+ *  @subsection util Utilities
+ *    Misc utilities
+ *    @li sll_lite.h @par
+ *      Simple linked list implementation bundled with SIP stack to be used in xml_parser apis
+ *    @li dns_util.h @par
+ *      DNS query methods to be implemented by the platform code
+ *    @li plat_api.h  @par
+ *      APIs that must be implemented by the platform for platform specific functionality
+ *
+ */
+
+#ifndef _CC_CALL_FEATURE_H_
+#define _CC_CALL_FEATURE_H_
+
+#include "cc_constants.h"
+
+/***********************************Basic Call Feature Control Methods************************************
+ * This section defines all the call related methods that an upper layer can use to control
+ * a call in progress.
+ */
+
+/**
+ * Used to create any outgoing call regular call. The incoming/reverting/consultation call will be
+ * created by the stack. It creates a call place holder and initialize the memory for a call. An user needs
+ * other methods to start the call, such as the method OriginateCall, etc
+ * @param line line number that is invoked and is assigned
+ * @return call handle wich includes the assigned line and call id
+ */
+cc_call_handle_t CC_createCall(cc_lineid_t line);
+
+/**
+ * Start the call that was created.
+ * @param call_handle the call handle
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ */
+ /*move it up...*/
+cc_return_t CC_CallFeature_originateCall(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref);
+
+/**
+ * Terminate or end a normal call.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_terminateCall(cc_call_handle_t call_handle);
+
+/**
+ * Answer an incoming or reverting call.
+ * @param call_handle call handle
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_answerCall(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref);
+
+/**
+ * Send a keypress to a call, it could be a single digit.
+ * @param call_handle call handle
+ * @param cc_digit digit pressed
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_sendDigit(cc_call_handle_t call_handle, cc_digit_t cc_digit);
+
+/**
+ * Send a backspace action.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_backSpace(cc_call_handle_t call_handle);
+
+/**
+ * Send a dial digit string on an active call, e.g."9191234567".
+ * @param call_handle call handle
+ * @param video_pref the sdp direction
+ * @param numbers dialed string
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_dial(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref, const cc_string_t numbers);
+
+cc_return_t CC_CallFeature_CreateOffer(cc_call_handle_t call_handle, const cc_media_constraints_t *constraints);
+
+cc_return_t CC_CallFeature_CreateAnswer(cc_call_handle_t call_handle, const cc_media_constraints_t *constraints);
+
+cc_return_t CC_CallFeature_SetLocalDescription(cc_call_handle_t call_handle, cc_jsep_action_t action, const char* sdp);
+
+cc_return_t CC_CallFeature_SetRemoteDescription(cc_call_handle_t call_handle, cc_jsep_action_t action, const char* sdp);
+
+cc_return_t CC_CallFeature_SetPeerConnection(cc_call_handle_t call_handle, cc_peerconnection_t pc);
+
+cc_return_t CC_CallFeature_AddStream(cc_call_handle_t call_handle, cc_media_stream_id_t stream_id, cc_media_track_id_t id, cc_media_type_t media_type);
+
+cc_return_t CC_CallFeature_RemoveStream(cc_call_handle_t call_handle, cc_media_stream_id_t stream_id, cc_media_track_id_t id, cc_media_type_t media_type);
+
+cc_return_t CC_CallFeature_AddICECandidate(cc_call_handle_t call_handle, const char* candidate, const char *mid, cc_level_t level);
+
+/**
+ * Initiate a speed dial.
+ * @param call_handle call handle
+ * @param speed_dial_number speed dial number to be dialed.
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_speedDial(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref, const cc_string_t speed_dial_number);
+
+/**
+ * Initiate a BLF call pickup.
+ * @param call_handle call handle
+ * @param speed_dial_number speed dial number configured.
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_blfCallPickup(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref, const cc_string_t speed_dial_number);
+
+/**
+ * Redial the last dial numbers.
+ * @param call_handle call handle
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ * @note: if there is no active dial made, this method should not be called.
+ */
+cc_return_t CC_CallFeature_redial(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref);
+
+/**
+ * Update a video media capability for a call. To be used to escalate deescalate video on a specified call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_updateCallMediaCapability(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref);
+
+/**
+ * Make a call forward all on the line identified by the call_handle.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_callForwardAll(cc_call_handle_t call_handle);
+
+/**
+ * Resume a held call.
+ * @param call_handle call handle
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_resume(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref);
+
+/**
+ * End a consultation call.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_endConsultativeCall(cc_call_handle_t call_handle);
+
+/**
+ * Initiate a conference. Steps to make a conference or transfer:
+ * @li Step1 Create a call handle, e.g. chandle1.
+ * @li Step2 Start the call on this call handle.
+ * @li Step3 When the call is answered, invoke:
+ *        CC_CallFeature_conference(chandle1, FALSE, CC_EMPTY_CALL_HANDLE) to start a conference operation.
+ * @li Step4 Upon receiving the consultative call (cHandle2) created from pSipcc system,
+ *    invoke:
+ *        CC_CallFeature_dial(cHandle2)
+ *       to dial the consultative call.
+ * @li Step5 When the consultative call is in ringout or connected state, invoke:
+ *        CC_CallFeature_conference(cHandle2, FALSE, CC_EMPTY_CALL_HANDLE) to
+ *    finish the conference.
+ * Note: @li in the step 4, a user could kill the consultative call and pickup a hold call (not the parent call that
+ *    initiated the conference). In this scenario, a parent call handle should be supplied.
+ *    For instance,
+ *        CC_CallFeature_conference(cHandle2, FALSE, cHandle1)
+ *       @li If it's a B2bConf, substitute the "FALSE" with "TRUE"
+ *
+ * @param call_handle the call handle for
+ *     @li the original connected call.
+ *     @li the consultative call or a held call besides the parent call initiated the transfer. This is used
+ *        on the second time to finish the transfer.
+ * @param is_local Local conference or not. If it's a local conference, it's a b2bconf.
+ * @param parent_call_handle if supplied, it will be the targeted parent call handle, which initiated the conference.
+ * @param video_pref the sdp direction
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_conference(cc_call_handle_t call_handle, cc_boolean is_local,
+               cc_call_handle_t parent_call_handle, cc_sdp_direction_t video_pref);
+
+/**
+ * Initiate a call transfer. Please refer to Conference feature.
+ * @param call_handle the call handle for
+ *     @li the original connected call.
+ *     @li the consultative call or a held call besides the parent call initiated the transfer. This is used
+ *        on the second time to finish the transfer.
+ * @param parent_call_handle if supplied, it will be the parent call handle, which initiated the transfer.
+ * @param video_pref video preference for the consult call as specified by direction
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_transfer(cc_call_handle_t call_handle, cc_call_handle_t parent_call_handle, cc_sdp_direction_t video_pref);
+
+
+/**
+ * Initiate a direct transfer
+ * @param call_handle the call handle for the call to initialize a transfer
+ * @param target_call_handle the call handle for the target transfer call.
+ * @retrun SUCCESS or FAILURE. If the target call handle is empty, a FAILURE will be returned.
+ */
+cc_return_t CC_CallFeature_directTransfer(cc_call_handle_t call_handle, cc_call_handle_t target_call_handle);
+
+/**
+ * Initiate a join across line
+ * @param call_handle the call handle for the call that initializes a join across line (conference).
+ * @param target_call_handle the call handle for the call will be joined.
+ */
+cc_return_t CC_CallFeature_joinAcrossLine(cc_call_handle_t call_handle, cc_call_handle_t target_call_handle);
+
+/**
+ * Put a connected call on hold.
+ * @param call_handle call handle
+ * @param reason the reason to hold. The following values should be used.
+ *       CC_HOLD_REASON_NONE,
+ *    CC_HOLD_REASON_XFER, //Hold for transfer
+ *    CC_HOLD_REASON_CONF, //Hold for conference
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_holdCall(cc_call_handle_t call_handle, cc_hold_reason_t reason);
+
+/********************************End of basic call feature methods******************************************/
+
+/*************************************Additional call feature methods***************************************
+ *
+ */
+/** @todo if needed
+ * Direct transfer a call.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+//cc_return_t CC_CallFeature_directTransfer(cc_call_handle_t call_handle);
+
+/**
+ * Select or locked a call.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_select(cc_call_handle_t call_handle);
+
+/**
+ * Cancel a call feature, e.g. when the consultative call is connected and the
+ * user wishes not to make the conference, thie method can be invoked.
+ * @param call_handle call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_CallFeature_cancelXfrerCnf(cc_call_handle_t call_handle);
+
+/**
+ * Indicate the mute state on the device
+ * Used to send a mute state across over CAST to the PC
+ */
+void CC_CallFeature_mute(cc_boolean mute);
+
+/**
+ * Indicate the speaker state on the device
+ * Used to send a speaker state across over CAST to the PC
+ */
+void CC_CallFeature_speaker(cc_boolean mute);
+
+/**
+ * Returns the call id of the connected call
+ */
+
+cc_call_handle_t CC_CallFeature_getConnectedCall();
+
+#endif /* _CC_CALL_FEATURE_H_ */
diff --git a/libs/sipcc/include/cc_call_listener.h b/libs/sipcc/include/cc_call_listener.h
new file mode 100644 (file)
index 0000000..7361d03
--- /dev/null
@@ -0,0 +1,201 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_CALL_LISTENER_H_
+#define _CC_CALL_LISTENER_H_
+
+#include "cc_constants.h"
+
+/*****************************************************************************************************
+ * Call Control: provides the methods to process a call or calls.
+ *****************************************************************************************************/
+
+/****************************************Call Listener Methods****************************************
+ * This section defines all call related methods that will invoke an application about a call.
+  * All input parameters please see their definition in cc_constancts.h file.
+ */
+
+/**
+ * Notification for the creation of a new call
+ * @param call_handle call handle that is created.
+ * @param call_state the call state
+ * @param cc_cause cause that the call is created
+ * @param cc_attr attr of the call created
+ * @return void
+ */
+void CC_CallListener_callCreated(cc_call_handle_t call_handle,
+               cc_call_state_t call_state,
+               cc_cause_t cc_cause,
+               cc_call_attr_t cc_attr);
+
+/**
+ * Update a call state, e.g. offhook.
+ * Description: the first CallStateChanged after the CallCreated will have included
+ * call_attr and call_instance. The consective CallStateChanged should ignore the
+ * call_attr and call instance.
+ * @param call_handle call handle
+ * @param call_state call state
+ * @param call_attr call attribute
+ * @param call_cause cause that the call state changed
+ * @param call_instance call instance id
+ * @return void
+ */
+void CC_CallListener_callStateChanged(cc_call_handle_t call_handle,
+               cc_call_state_t call_state,
+               cc_call_attr_t call_attr,
+               cc_cause_t call_cause,
+               cc_call_instance_t call_instance);
+
+/**
+ * Inform the upper layer call control that the call attribute changed.
+ * @param call_handle call handle
+ * @param call_attr the call attr
+ * @return void
+ */
+void CC_CallListener_callAttributeChanged(cc_call_handle_t call_handle,
+               cc_call_attr_t call_attr);
+
+/**
+ * Inform the upper layer call control that a new call comes.
+ * @param call_handle
+ * @param call_security call security attribute
+ * @return void
+ */
+void CC_CallListener_callSecurityChanged(cc_call_handle_t call_handle,
+               cc_call_security_t call_security);
+
+/**
+ * Cancel a conference or Transfer feature. This is usually coming
+ * from the CTI application to cancel the ongoing Xfer or Cnf calls.
+ * @param call_handle call handle
+ * @param other_call_handle the other call id that was cancelled
+ * @return void
+ */
+void CC_CallListener_xferCnfCancelled(cc_call_handle_t call_handle,
+               cc_call_handle_t other_call_handle);
+
+/**
+ * Update a call information about the call names or numbers.
+ * @param call_handle call handle
+ * @param clg_name calling party name
+ * @param clg_number calling party number
+ * @param cld_name called party name
+ * @param cld_number called party number
+ * @param alt_clg_number alternative calling party number
+ * @param orig_name original name
+ * @param orig_number original number
+ * @param redir_name redirect name
+ * @param redir_number redirect number
+ * @param call_security call security
+ * @param call_policy call policy
+ * @param call_instance call instance
+ * @param call_type
+ * @return void
+ */
+void CC_CallListener_callInfoChanged(cc_call_handle_t call_handle,
+               cc_string_t clg_name, cc_string_t clg_number,
+               cc_string_t cld_name, cc_string_t cld_number,
+               cc_string_t alt_clg_number,
+               cc_string_t orig_name, cc_string_t orig_number,
+               cc_string_t redir_name, cc_string_t redir_number,
+               cc_call_security_t call_security,
+               cc_call_policy_t call_policy,
+               cc_call_instance_t call_instance,
+               cc_call_type_t call_type);
+
+/**
+ * Update Global Call Identification for a call. GCID can be used to collapse
+ * multiple call bubbles for the same call to a single call bubble. Please refer
+ * to the collpased call bubble as specified by the roundtable UI Spec.
+ * @param call_handle
+ * @param gcid global call identification
+ * @return void
+ */
+void CC_CallListener_callGCIDChanged(cc_call_handle_t call_handle, char* gcid);
+
+/**
+ * Update the placed call information. This API reports the dialed digits for the
+ * placed call. The number specified by this API should be logged in the placed call information.
+ * @param call_handle call_handle
+ * @param cld_name called party name
+ * @param cld_number called party number
+ * @return void
+ */
+void CC_CallListener_callPlacedInfoChanged(cc_call_handle_t call_handle,
+               cc_string_t cld_name,
+               cc_string_t cld_number);
+
+/**
+ * Send call status to the upper layer for literally display purpose,
+ * e.g., "HOLD" for a call was placed on held state.
+ * @param call_handle call handle
+ * @param status call status display string
+ * @param timeout the time interval in seconds to display the status message on a call bubble if there is one.
+ * @param prioroty the priority of display message.
+ * @return void
+ */
+void CC_CallListener_callStatusChanged(cc_call_handle_t call_handle, cc_string_t status, int timeout, char priority);
+
+/**
+ * Inform the upper layer call control whether it is able to to delete the last dialed digit now.
+ * @param call_handle call handle
+ * @param state true or false whether it can delete last dialed digit.
+ * @return void
+ */
+void CC_CallListener_backSpaceEnabled(cc_call_handle_t call_handle, cc_boolean state);
+
+/**
+ * Imform the upper layer that a call is locked remotely or not.
+ * @param call_handle call handle
+ * @param call_selection if the call is locked or not
+ * @return void
+ */
+void CC_CallListener_callSelected(cc_call_handle_t call_handle,
+               cc_call_selection_t call_selection);
+
+/**
+ * Inform the upper layer the log disposition
+ * @param call_handle call handle
+ * @param log_disp the log disposition
+ * @return void
+ */
+void CC_CallListener_logDispositionUpdated(cc_call_handle_t call_handle,
+               cc_log_disposition_t log_disp);
+
+/**
+ * Inform the upper layer that the last dialed digit was deleted.
+ * @param call_handle call handle
+ * @return void
+ */
+void CC_CallListener_lastDigitDeleted(cc_call_handle_t call_handle);
+
+/**
+ * Inform the upper layer that the call is in preservation state,
+ * which means all features are disabled except to end the call.
+ * e.g., "HOLD" for a call was placed on held state.
+ * @param call_handle call handle
+ * @return void
+ */
+void CC_CallListener_callPreserved(cc_call_handle_t call_handle);
+
+/**
+ * Inform the upper layer whether the video is available or not.
+ * @param call_handle call handle
+ * @param state video direction NONE => no video
+ * @return void
+ */
+void CC_CallListener_videoAvailable(cc_call_handle_t call_handle, cc_int32_t state);
+
+/**
+ * Inform the upper layer that the remote has offered video capability.
+ * @param call_handle call handle
+ * @param direction the media direction
+ * @return void
+ */
+void CC_CallListener_remoteVideoOffered(cc_call_handle_t call_handle, cc_sdp_direction_t direction);
+
+/**************************END of Call Listener Methods***********************************/
+
+
+#endif /* _CC_CALL_LISTENER_H_ */
diff --git a/libs/sipcc/include/cc_config.h b/libs/sipcc/include/cc_config.h
new file mode 100644 (file)
index 0000000..8453088
--- /dev/null
@@ -0,0 +1,289 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_CONFIG_H_
+#define _CC_CONFIG_H_
+
+/* Keep non line specific params here */
+#include "cc_constants.h"
+
+#define CC_MAX_CONFIG_LINES     MAX_CONFIG_LINES
+
+#define CFGID_BEGIN_INDEX                      0
+#define CFGID_MEDIA_PORT_RANGE_START_INT       CFGID_BEGIN_INDEX           //tag:startMediaPort
+#define CFGID_MEDIA_PORT_RANGE_END_INT         CFGID_BEGIN_INDEX + 1       //tag:stopMediaPort
+#define CFGID_CALLERID_BLOCKING_BOOL           CFGID_BEGIN_INDEX + 2       //tag:callIdBlocking
+#define CFGID_ANONYMOUS_CALL_BLOCK_BOOL        CFGID_BEGIN_INDEX + 3       //tag:anonymousCallBlock
+#define CFGID_PREFERRED_CODEC_STRING           CFGID_BEGIN_INDEX + 6       //tag:preferredCodec
+#define CFGID_DTMF_OUTOFBAND_STRING            CFGID_BEGIN_INDEX + 7       //tag:dtmfOutofBand
+#define CFGID_DTMF_AVT_PAYLOAD_INT             CFGID_BEGIN_INDEX + 8       //tag:dtmfAvtPayload
+#define CFGID_DTMF_DB_LEVEL_INT                CFGID_BEGIN_INDEX + 9       //tag:dtmfDbLevel
+
+#define CFGID_SIP_RETX_INT                     CFGID_BEGIN_INDEX + 10      //tag:sipRetx
+#define CFGID_SIP_INVITE_RETX_INT              CFGID_BEGIN_INDEX + 11      //tag:sipInviteRetx
+#define CFGID_TIMER_T1_INT                     CFGID_BEGIN_INDEX + 12      //tag:timerT1
+#define CFGID_TIMER_T2_INT                     CFGID_BEGIN_INDEX + 13      //tag:timerT2
+#define CFGID_TIMER_INVITE_EXPIRES_INT         CFGID_BEGIN_INDEX + 14      //tag:timerInviteExpires
+#define CFGID_TIMER_REGISTER_EXPIRES_INT       CFGID_BEGIN_INDEX + 15      //tag:timerRegisterExpires
+
+#define CFGID_PROXY_REGISTER_BOOL              CFGID_BEGIN_INDEX + 16      //tag:proxy
+#define CFGID_PROXY_BACKUP_STRING              CFGID_BEGIN_INDEX + 17      //tag:backupProxy
+#define CFGID_PROXY_BACKUP_PORT_INT            CFGID_BEGIN_INDEX + 18      //tag:backupProxyPort
+#define CFGID_PROXY_EMERGENCY_STRING           CFGID_BEGIN_INDEX + 19      //tag:emergencyProxy
+#define CFGID_PROXY_EMERGENCY_PORT_INT        CFGID_BEGIN_INDEX + 20       //tag:emergencyProxyPort
+#define CFGID_OUTBOUND_PROXY_STRING            CFGID_BEGIN_INDEX + 21      //tag:outboundProxy
+#define CFGID_OUTBOUND_PROXY_PORT_INT          CFGID_BEGIN_INDEX + 22      //tag:outboundProxyPort
+
+#define CFGID_NAT_RECEIVED_PROCESSING_BOOL     CFGID_BEGIN_INDEX + 23      //tag:natRecievedProcessing
+#define CFGID_REG_USER_INFO_STRING             CFGID_BEGIN_INDEX + 24      //tag:userInfo
+#define CFGID_CNF_JOIN_ENABLE_BOOL             CFGID_BEGIN_INDEX + 25      //tag:cnfJoinEnable
+#define CFGID_REMOTE_PARTY_ID_BOOL             CFGID_BEGIN_INDEX + 26      //tag:remotePartyID
+#define CFGID_SEMI_XFER_BOOL                   CFGID_BEGIN_INDEX + 27      //tag:semiAttendedTransfer
+#define CFGID_CALL_HOLD_RINGBACK_BOOL          CFGID_BEGIN_INDEX + 28      //tag:callHoldRingback
+#define CFGID_STUTTER_MSG_WAITING_BOOL         CFGID_BEGIN_INDEX + 29      //tag:stutterMsgWaiting
+#define CFGID_CFWD_URL_STRING                  CFGID_BEGIN_INDEX + 30      //tag:callForwardURI
+#define CFGID_CALL_STATS_BOOL                  CFGID_BEGIN_INDEX + 31      //tag:callStats
+#define CFGID_AUTO_ANSWER_BOOL                 CFGID_BEGIN_INDEX + 32      //tag:autoAnswerEnabled
+#define CFGID_LOCAL_CFWD_ENABLE_BOOL           CFGID_BEGIN_INDEX + 33      //tag:localCfwdEnable
+#define CFGID_TIMER_REGISTER_DELTA_INT         CFGID_BEGIN_INDEX + 34      //tag:timerRegisterDelta
+#define CFGID_SIP_MAX_FORWARDS_INT             CFGID_BEGIN_INDEX + 35      //tag:maxRedirects
+#define CFGID_2543_HOLD_BOOL                   CFGID_BEGIN_INDEX + 36      //tag:rfc2543Hold
+
+#define CFGID_CCM1_ADDRESS_STRING              CFGID_BEGIN_INDEX + 37      //tag:processNodeName
+#define CFGID_CCM2_ADDRESS_STRING              CFGID_BEGIN_INDEX + 38      //tag:processNodeName
+#define CFGID_CCM3_ADDRESS_STRING              CFGID_BEGIN_INDEX + 39      //tag:processNodeName
+
+#define CFGID_CCM1_IPV6_ADDRESS_STRING         CFGID_BEGIN_INDEX + 40      //tag:ipv6Addr, Not used
+#define CFGID_CCM2_IPV6_ADDRESS_STRING         CFGID_BEGIN_INDEX + 41      //tag:ipv6Addr
+#define CFGID_CCM3_IPV6_ADDRESS_STRING         CFGID_BEGIN_INDEX + 42      //tag:ipv6Addr
+
+#define CFGID_CCM1_SIP_PORT_INT                CFGID_BEGIN_INDEX + 43      //tag:sipPort
+#define CFGID_CCM2_SIP_PORT_INT                CFGID_BEGIN_INDEX + 44      //tag:sipPort
+#define CFGID_CCM3_SIP_PORT_INT                CFGID_BEGIN_INDEX + 45      //tag:sipPort
+
+#define CFGID_CCM1_SEC_LEVEL_INT               CFGID_BEGIN_INDEX + 46      //not from configuration
+#define CFGID_CCM2_SEC_LEVEL_INT               CFGID_BEGIN_INDEX + 47      //not from configuration
+#define CFGID_CCM3_SEC_LEVEL_INT               CFGID_BEGIN_INDEX + 48      //not from configuration
+
+#define CFGID_CCM1_IS_VALID_BOOL               CFGID_BEGIN_INDEX + 49      //not from configuration
+#define CFGID_CCM2_IS_VALID_BOOL               CFGID_BEGIN_INDEX + 50      //not from configuration
+#define CFGID_CCM3_IS_VALID_BOOL               CFGID_BEGIN_INDEX + 51      //not from configuration
+
+#define CFGID_CCM_TFTP_IP_ADDR_STRING          CFGID_BEGIN_INDEX + 52      //not from configuration
+#define CFGID_CCM_TFTP_PORT_INT                CFGID_BEGIN_INDEX + 53      //not from configuration
+#define CFGID_CCM_TFTP_IS_VALID_BOOL           CFGID_BEGIN_INDEX + 54      //not from configuration
+#define CFGID_CCM_TFTP_SEC_LEVEL_INT           CFGID_BEGIN_INDEX + 55      //not from configuration
+
+#define CFGID_CONN_MONITOR_DURATION_INT        CFGID_BEGIN_INDEX + 60      //tag:connectionMonitorDuration
+#define CFGID_CALL_PICKUP_URI_STRING           CFGID_BEGIN_INDEX + 61      //tag:callPickupURI
+#define CFGID_CALL_PICKUP_LIST_URI_STRING      CFGID_BEGIN_INDEX + 62      //tag:callPickupListURI
+#define CFGID_CALL_PICKUP_GROUP_URI_STRING     CFGID_BEGIN_INDEX + 63      //tag:callPickupGroupURI
+#define CFGID_CALL_FORWARD_URI_STRING          CFGID_BEGIN_INDEX + 65      //tag:callForwardURI
+#define CFGID_ABBREVIATED_DIAL_URI_STRING      CFGID_BEGIN_INDEX + 66      //tag:abbreviatedDialURI
+#define CFGID_CALL_LOG_BLF_ENABLED_BOOL        CFGID_BEGIN_INDEX + 67      //tag:callLogBlfEnabled
+#define CFGID_REMOTE_CC_ENABLED_BOOL           CFGID_BEGIN_INDEX + 68      //tag:remoteCcEnable
+#define CFGID_RETAIN_FORWARD_INFORMATION_BOOL  CFGID_BEGIN_INDEX + 69      //tag:retainForwardInformation
+
+#define CFGID_TIMER_KEEPALIVE_EXPIRES_INT      CFGID_BEGIN_INDEX + 70      //tag:timerKeepAliveExpires
+#define CFGID_TIMER_SUBSCRIBE_EXPIRES_INT      CFGID_BEGIN_INDEX + 71      //tag:timerSubscribeExpires
+#define CFGID_TIMER_SUBSCRIBE_DELTA_INT        CFGID_BEGIN_INDEX + 72      //tag:timerSubscribeDelta
+#define CFGID_TRANSPORT_LAYER_PROT_INT         CFGID_BEGIN_INDEX + 73      //tag:transportLayerProtocol
+#define CFGID_KPML_ENABLED_INT                 CFGID_BEGIN_INDEX + 74      //tag:kpml
+
+#define CFGID_NAT_ENABLE_BOOL                  CFGID_BEGIN_INDEX + 75      //tag:natEnabled
+#define CFGID_NAT_ADDRESS_STRING               CFGID_BEGIN_INDEX + 76      //tag:natAddress
+#define CFGID_VOIP_CONTROL_PORT_INT            CFGID_BEGIN_INDEX + 77      //tag:voipControlPort
+#define CFGID_MY_IP_ADDR_STRING                CFGID_BEGIN_INDEX + 78      //not applicable
+#define CFGID_MY_MAC_ADDR_STRING               CFGID_BEGIN_INDEX + 79      //not applicable
+#define CFGID_ENABLE_VAD_BOOL                  CFGID_BEGIN_INDEX + 80      //tag:enableVad
+
+#define CFGID_AUTOANSWER_IDLE_ALTERNATE_BOOL   CFGID_BEGIN_INDEX + 81      //tag:autoAnswerAltBehavior
+#define CFGID_AUTOANSWER_TIMER_INT             CFGID_BEGIN_INDEX + 82      //tag:autoAnswerTimer
+#define CFGID_AUTOANSWER_OVERRIDE_BOOL         CFGID_BEGIN_INDEX + 83      //tag:autoAnswerOverride
+
+#define CFGID_OFFHOOK_TO_FIRST_DIGIT_TIMER_INT CFGID_BEGIN_INDEX + 84      //tag:offhookToFirstDigitTimer
+#define CFGID_CALL_WAITING_SILENT_PERIOD_INT   CFGID_BEGIN_INDEX + 85      //tag:silentPeriodBetweenCallWaitingBursts
+#define CFGID_RING_SETTING_BUSY_POLICY_INT     CFGID_BEGIN_INDEX + 86      //tag:ringSettingBusyStationPolicy
+#define CFGID_DSCP_FOR_CALL_CONTROL_INT        CFGID_BEGIN_INDEX + 87      //tag:dscpForCm2Dvce
+#define CFGID_SPEAKER_ENABLED_BOOL             CFGID_BEGIN_INDEX + 88      //tag:disableSpeaker
+#define CFGID_XFR_ONHOOK_ENABLED_BOOL          CFGID_BEGIN_INDEX + 89      //tag:disableSpeaker
+#define CFGID_ROLLOVER_INT                     CFGID_BEGIN_INDEX + 90      //tag:rollover
+#define CFGID_LOAD_FILE_STRING                 CFGID_BEGIN_INDEX + 91      //not from config file
+
+#define CFGID_BLF_ALERT_TONE_IDLE_INT          CFGID_BEGIN_INDEX + 92      //tag:blfAudibleAlertSettingOfIdleStation
+#define CFGID_BLF_ALERT_TONE_BUSY_INT          CFGID_BEGIN_INDEX + 93      //tag:blfAudibleAlertSettingOfBusyStation
+#define CFGID_AUTO_PICKUP_ENABLED_BOOL         CFGID_BEGIN_INDEX + 94      //tag:autoCallPickupEnable
+
+#define CFGID_JOIN_ACROSS_LINES_INT            CFGID_BEGIN_INDEX + 95      //tag:joinAcrossLines
+
+#if defined(_TNP_)
+#define ROUNDTABLE_INDEX_OFFSET            0
+#else
+#define CFGID_MY_ACTIVE_MAC_ADDR_STRING        CFGID_BEGIN_INDEX + 96      //not from configuration
+#define CFGID_DSCP_AUDIO_INT                   CFGID_BEGIN_INDEX + 97      //tag:dscpForAudio
+#define CFGID_DEVICE_NAME_STRING               CFGID_BEGIN_INDEX + 98      //not applicable
+#define CFGID_USER_AGENT_STRING                CFGID_BEGIN_INDEX + 99      //not from config file
+#define CFGID_MODEL_NUMBER_STRING              CFGID_BEGIN_INDEX + 100     //not from config file
+#define CFGID_DSCP_VIDEO_INT                   CFGID_BEGIN_INDEX + 101     //tag:dscpVideo
+#define ROUNDTABLE_INDEX_OFFSET            6
+#endif
+
+#define CFGID_IP_ADDR_MODE_INT                 CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 96   //Not used
+#define CFGID_INTER_DIGIT_TIMER_INT            CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 97   //tag:t302
+
+#define CFGID_EMCC_MODE_BOOL                   CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 98   // Not from config file
+#define CFGID_VISITING_EM_PORT_INT             CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 99   // Not from config file
+#define CFGID_VISITING_EM_IP_STRING            CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 100  // Not from config file
+
+#define CFGID_JOIN_DXFER_POLICY_STRING         CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 102  //tag:<join-dxfer-policy>used only for RTLite CTI appliation
+#define CFGID_CCM_EXTERNAL_NUMBER_MASK_STRING  CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 103  //tag:<externalNumberMask>
+#define CFGID_MEDIA_IP_ADDR_STRING             CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 104  //tag:<videoCapability>
+#define CFGID_P2PSIP_BOOL                      CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 105
+#define CFGID_VERSION_STRING                   CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 106
+#define CFGID_SDPMODE_BOOL                     CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 107
+#define CFGID_RTCPMUX_BOOL                     CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 108
+#define CFGID_RTPSAVPF_BOOL                    CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 109
+#define CFGID_MAXAVBITRATE_BOOL                CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 110
+#define CFGID_MAXCODEDAUDIOBW_BOOL             CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 111
+#define CFGID_USEDTX_BOOL                      CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 112
+#define CFGID_STEREO_BOOL                      CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 113
+#define CFGID_USEINBANDFEC_BOOL                CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 114
+#define CFGID_CBR_BOOL                         CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 115
+#define CFGID_MAXPTIME_BOOL                    CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 116
+#define CFGID_SCTP_PORT_INT                    CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 117
+#define CFGID_NUM_DATA_STREAMS_INT             CFGID_BEGIN_INDEX + ROUNDTABLE_INDEX_OFFSET + 118
+
+/* All non Line specific params should be added above */
+/* All Line specific params should be added below */
+/*
+   Following configuration settings are coming from the <line> element in the configuration file.
+   The line configuration is block based. Each configuration item has a block, e.g., the  CFGID_LINE_FEATURE_INT
+   has a block with the size equal to the maxinum number of line that a type of phone can be configured with.
+   The following depicts the structure of line feature configuration.
+    config_table[
+        ...
+        cfgid_line_feature_id_line_1,//line feature id block start
+        ...
+        cfgid_line_feature_id_line_max,//line feature id block end
+        cfgid_line_index_id_line_1, //line index block start
+        ...
+        cfgid_line_index_id_line_max,//line index block end
+        ...CFGID_LINE_CFWDALL_STRING
+        cfgid_line_cfwdall_string_line_max //line cfwd all string block end
+        ];
+
+  Based on this, when setting directory name (DN, line name) for the line 3 (line_id = 3),
+  the config id should be CFGID_LINE_NAME_STRING + (line_id -1), or CFGID_LINE_NAME_STRING + 2.
+  Keep in mind the following config ids are defined for the first line.
+ */
+
+#define CFGID_LINE_FEATURE_INT                 CFGID_NUM_DATA_STREAMS_INT + 1                              //tag:featureID
+#define CFGID_LINE_INDEX_INT                   CFGID_LINE_FEATURE_INT + CC_MAX_CONFIG_LINES                //tag:lineIndex
+#define CFGID_LINE_MAXNUMCALLS_INT             CFGID_LINE_INDEX_INT + CC_MAX_CONFIG_LINES                  //tag:maxNumCalls
+#define CFGID_LINE_NAME_STRING                 CFGID_LINE_MAXNUMCALLS_INT + CC_MAX_CONFIG_LINES            //tag:name
+#define CFGID_LINE_AUTHNAME_STRING             CFGID_LINE_NAME_STRING + CC_MAX_CONFIG_LINES                //tag:authName
+#define CFGID_LINE_PASSWORD_STRING             CFGID_LINE_AUTHNAME_STRING + CC_MAX_CONFIG_LINES            //tag:authPassword
+#define CFGID_LINE_DISPLAYNAME_STRING          CFGID_LINE_PASSWORD_STRING + CC_MAX_CONFIG_LINES            //tag:displayName
+#define CFGID_LINE_CONTACT_STRING              CFGID_LINE_DISPLAYNAME_STRING + CC_MAX_CONFIG_LINES         //tag:contact
+#define CFGID_PROXY_ADDRESS_STRING             CFGID_LINE_CONTACT_STRING + CC_MAX_CONFIG_LINES             //tag:proxy
+#define CFGID_PROXY_PORT_INT                   CFGID_PROXY_ADDRESS_STRING + CC_MAX_CONFIG_LINES            //tag:port
+#define CFGID_LINE_AUTOANSWER_ENABLED_BYTE     CFGID_PROXY_PORT_INT + CC_MAX_CONFIG_LINES                  //tag:autoAnswerEnabled
+#define CFGID_LINE_AUTOANSWER_MODE_STRING      CFGID_LINE_AUTOANSWER_ENABLED_BYTE + CC_MAX_CONFIG_LINES    //tag:autoAnswerMode
+#define CFGID_LINE_CALL_WAITING_BYTE           CFGID_LINE_AUTOANSWER_MODE_STRING + CC_MAX_CONFIG_LINES     //tag:callWaiting
+#define CFGID_LINE_SHARED_LINE_BOOL            CFGID_LINE_CALL_WAITING_BYTE + CC_MAX_CONFIG_LINES          //tag:sharedLine
+#define CFGID_LINE_MSG_WAITING_LAMP_BYTE       CFGID_LINE_SHARED_LINE_BOOL + CC_MAX_CONFIG_LINES           //tag:messageWaitingLampPolicy
+#define CFGID_LINE_MESSAGE_WAITING_AMWI_BYTE   CFGID_LINE_MSG_WAITING_LAMP_BYTE + CC_MAX_CONFIG_LINES      //tag:messageWaitingAMWI
+#define CFGID_LINE_RING_SETTING_IDLE_BYTE      CFGID_LINE_MESSAGE_WAITING_AMWI_BYTE + CC_MAX_CONFIG_LINES  //tag:ringSettingIdle
+#define CFGID_LINE_RING_SETTING_ACTIVE_BYTE    CFGID_LINE_RING_SETTING_IDLE_BYTE + CC_MAX_CONFIG_LINES     //tag:ringSettingActive
+#define CFGID_LINE_BUSY_TRIGGER_INT            CFGID_LINE_RING_SETTING_ACTIVE_BYTE + CC_MAX_CONFIG_LINES   //tag:busyTrigger
+#define CFGID_LINE_CFWDALL_STRING              CFGID_LINE_BUSY_TRIGGER_INT + CC_MAX_CONFIG_LINES           //not from configuration
+
+#define CFGID_LINE_SPEEDDIAL_NUMBER_STRING                CFGID_LINE_CFWDALL_STRING + MAX_CONFIG_LINES
+#define CFGID_LINE_RETRIEVAL_PREFIX_STRING                CFGID_LINE_SPEEDDIAL_NUMBER_STRING + MAX_CONFIG_LINES
+#define CFGID_LINE_MESSAGES_NUMBER_STRING                 CFGID_LINE_RETRIEVAL_PREFIX_STRING + MAX_CONFIG_LINES
+#define CFGID_LINE_FWD_CALLER_NAME_DIPLAY_BOOL            CFGID_LINE_MESSAGES_NUMBER_STRING + MAX_CONFIG_LINES
+#define CFGID_LINE_FWD_CALLER_NUMBER_DIPLAY_BOOL          CFGID_LINE_FWD_CALLER_NAME_DIPLAY_BOOL + MAX_CONFIG_LINES
+#define CFGID_LINE_FWD_REDIRECTED_NUMBER_DIPLAY_BOOL      CFGID_LINE_FWD_CALLER_NUMBER_DIPLAY_BOOL + MAX_CONFIG_LINES
+#define CFGID_LINE_FWD_DIALED_NUMBER_DIPLAY_BOOL          CFGID_LINE_FWD_REDIRECTED_NUMBER_DIPLAY_BOOL + MAX_CONFIG_LINES
+#define CFGID_LINE_FEATURE_OPTION_MASK_INT                CFGID_LINE_FWD_DIALED_NUMBER_DIPLAY_BOOL + MAX_CONFIG_LINES
+#define HLAPI_INDEX_OFFSET                                CFGID_LINE_FEATURE_OPTION_MASK_INT
+
+#define CFGID_PROTOCOL_MAX                     HLAPI_INDEX_OFFSET + MAX_CONFIG_LINES             //For notation only.
+
+
+#define CFGID_CONFIG_FILE                      1001
+
+/*********************************************************
+ *
+ *  Value Definitions
+ *
+ *********************************************************/
+// Line feature
+
+/**
+ * Set the maximum line available for registration
+ * @param lines the maximum line could be configured.
+ * @return
+ */
+int CC_Config_SetAvailableLines(cc_lineid_t lines);
+
+/**
+ * Sets the integer value for the config property.
+ * @param [in] cfgid - config property identifier
+ * @param [in] value - integer value to be set.
+ * @return
+ */
+void CC_Config_setIntValue(int cfgid, int value);
+
+/**
+ * Sets the boolean value for the config property.
+ * @param [in] cfgid - config property identifier
+ * @param [in] value - boolean value to be set.
+ * @return
+ */
+void CC_Config_setBooleanValue(int cfgid, cc_boolean value);
+
+/**
+ * Sets the string value for the config property.
+ * @param [in] cfgid - config property identifier
+ * @param [in] value - string value to be set.
+ * @return
+ */
+void CC_Config_setStringValue(int cfgid, const char* value);
+
+/**
+ * Sets the byte value for the config property.
+ * @param [in] cfgid - config property identifier
+ * @param [in] value - byte value to be set.
+ * @return
+ */
+void CC_Config_setByteValue(int cfgid, unsigned char value);
+
+/**
+ * Sets the byte array value for the config property.
+ * @param [in] cfgid - config property identifier
+ * @param [in] byte_array - byte array value to be set.
+ * @param [in] length - lenght of array.
+ * @return
+ */
+void CC_Config_setArrayValue(int cfgid, char *byte_array, int length);
+
+/**
+ * Set the dialplan file
+ * @param dial_plan_string the dial plan content string
+ * @param length the length of dial plan string, the maximum size will be 0x2000.
+ * @return string dial plan version stamp
+ */
+char* CC_Config_setDialPlan(const char *dial_plan_string, int len);
+
+/**
+ * Set the fcp file
+ * @param fcp_plan_)string - path to the fcp plan)
+ * @param len - length, the maximum size will be 0xXXXX (tbd- pfg))
+ * @return string - fcp plan version stamp
+ */
+
+char* CC_Config_setFcp(const char *fcp_plan_string, int len);
+
+#endif /* _CC_CONFIG_H_ */
diff --git a/libs/sipcc/include/cc_constants.h b/libs/sipcc/include/cc_constants.h
new file mode 100644 (file)
index 0000000..8c96cd2
--- /dev/null
@@ -0,0 +1,563 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_CONSTANTS_H_
+#define _CC_CONSTANTS_H_
+#include "cc_types.h"
+
+#define  PC_HANDLE_SIZE     17 /* 8 random bytes in hex plus null */
+
+/**
+ * Max call servers
+ */
+#define MAX_CALL_SERVERS 4
+
+/**
+ * Define notification priority
+ */
+#define CC_DEF_NOTIFY_PRI    20
+#define CC_HR_NOTIFY_PRI     1
+
+#define CC_ALL_LINES          255
+#define SID_LINE_SHIFT        16
+#define CC_NO_CALL_ID         (0)
+#define CC_NO_LINE            (0)
+#define CC_NO_CALL_INSTANCE   (0)
+
+#define CC_MAX_LEN_REQ_SUPP_PARAM_CISCO_SISTAG 64
+
+/**
+ * Attrib bit in video available direction byte to indicate if the video is over CAST
+ */
+#define CC_ATTRIB_CAST 0x8
+
+/**
+ * Define the line id. The value 0 (CC_NO_LINE) means not set, it will be set by pSipcc system.
+ */
+typedef unsigned short cc_lineid_t;
+
+/**
+ * Define the call id. The value 0 (CC_NO_CALL_ID)  means not set, it will be set by pSipcc system.
+ */
+typedef unsigned short cc_callid_t;
+
+typedef unsigned short cc_streamid_t;
+typedef unsigned short cc_mcapid_t;
+typedef unsigned short cc_groupid_t;
+typedef unsigned short cc_level_t;
+
+/**
+ * Define the call instance id
+ */
+
+typedef unsigned short cc_call_instance_t;
+
+/**
+ * Define string char
+ */
+typedef const char *cc_string_t;
+
+/**
+ * Define an empty call handle
+ */
+#define CC_EMPTY_CALL_HANDLE   (0)
+
+/**
+ * This will be returned by pSipcc system, a user/application should use the following two methods to get the selected or
+ * assinged line id and call id.
+ * When a user or an application doesn't select a line (user passes 0 to pSipcc),, the pSipcc will assign it with the first available
+ * line to it based on the maximum call per line that is configured.
+ */
+typedef unsigned int cc_call_handle_t;
+#define CC_SID_TYPE_SHIFT 28
+#define CC_SID_LINE_SHIFT 16
+#define CC_SESSIONTYPE_CALLCONTROL 1
+#define GET_LINE_ID(call_handle) (cc_lineid_t)((call_handle & 0xFFF0000) >> CC_SID_LINE_SHIFT )
+#define GET_CALL_ID(call_handle) (cc_callid_t)(call_handle & 0xFFFF)
+#define CREATE_CALL_HANDLE(line, callid) (cc_call_handle_t)(((line & 0xFFF) << CC_SID_LINE_SHIFT) + (callid & 0xFFFF))
+#define CREATE_CALL_HANDLE_FROM_SESSION_ID(session_id) (session_id & 0xFFFFFFF)
+#define CREATE_SESSION_ID_FROM_CALL_HANDLE(call_handle) ((CC_SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT) + call_handle)
+
+/**
+ * Define return codes
+ */
+typedef enum {
+       CC_FAILURE = -1,
+       CC_SUCCESS
+} cc_return_t;
+
+/**
+ * Define valid number digits for SendDigit method
+ */
+typedef enum {
+       KEY_1 = '1',
+       KEY_2 = '2',
+       KEY_3 = '3',
+       KEY_4 = '4',
+       KEY_5 = '5',
+       KEY_6 = '6',
+       KEY_7 = '7',
+       KEY_8 = '8',
+       KEY_9 = '9',
+       KEY_0 = '0',
+       KEY_STAR = '*',
+       KEY_POUND = '#',
+       KEY_A = 'A',
+       KEY_B = 'B',
+       KEY_C = 'C',
+       KEY_D = 'D',
+        KEY_PLUS = '+'
+} cc_digit_t;
+
+
+/**
+ * Defines cucm mode of the call manager to which device is connected.
+ */
+typedef enum {
+       CC_MODE_INVALID = -1,
+       CC_MODE_CCM,
+       CC_MODE_NONCCM
+} cc_cucm_mode_t;
+
+// Line feature
+typedef enum {
+       CC_LINE_FEATURE_NONE = 0,
+       CC_LINE_FEATURE_REDIAL = 1,
+       CC_LINE_FEATURE_SPEEDDIAL = 2,
+       CC_LINE_FEATURE_DN = 9,
+       CC_LINE_FEATURE_SERVICE = 20,
+       CC_LINE_FEATURE_SPEEDDIALBLF = 21,
+       CC_LINE_FEATURE_MALICIOUSCALLID = 27,
+       CC_LINE_FEATURE_CALLPICKUP = 127,
+       CC_LINE_FEATURE_GROUPCALLPICKUP = 128,
+       CC_LINE_FEATURE_QUALREPORTTOOL = 133,
+       CC_LINE_FEATURE_OTHERPICKUP = 135,
+       CC_LINE_FEATURE_ALLCALLS = 140,
+       CC_LINE_FEATURE_ANSWEROLDEST = 141,
+       CC_LINE_FEATURE_SERVICES = 192,
+       CC_LINE_FEATURE_BLF = 255
+} cc_line_feature_t;
+
+/**
+ * Define feature option mask
+ */
+typedef enum {
+       CC_FEATUREOPTIONMASK_NONE,
+       CC_FEATUREOPTIONMASK_BLF_PICKUP
+} cc_feature_option_mask_t;
+
+/**
+ * Defines cucm secure levels
+ */
+typedef enum {
+    CC_CUCM_NONSECURE,
+    CC_CUCM_AUTHENTICATED,
+    CC_CUCM_ENCRYPTED,
+    CC_CUCM_NOT_IN_CTL
+} cc_cucm_sec_level_t;
+
+/**
+ * Defines cc events causing registration state change
+ */
+typedef enum {
+       CC_CAUSE_NONE,
+       CC_CAUSE_FAILOVER,
+       CC_CAUSE_FALLBACK,
+       CC_CAUSE_REG_ALL_FAILED,
+       CC_CAUSE_SHUTDOWN,
+       CC_CAUSE_UNREG_ALL,
+       CC_CAUSE_LOGOUT_RESET
+} cc_service_cause_t;
+
+/**
+ * Defines cc service state
+ */
+typedef enum {
+    CC_STATE_IDLE = 0,
+    CC_STATE_INS,
+    CC_STATE_OOS,
+    CC_STATE_PRO_BASE
+} cc_service_state_t;
+
+/**
+ * Define cucm connection status.
+ */
+typedef enum {
+    CC_CCM_STATUS_NONE = 0,
+    CC_CCM_STATUS_STANDBY,
+    CC_CCM_STATUS_ACTIVE
+} cc_ccm_status_t;
+
+/**
+ * Define line registration state
+ */
+typedef enum {
+    CC_UNREGISTERED,
+    CC_REGISTERED
+}cc_line_reg_state_t;
+
+/**
+ * Defines pSipcc shutdown reason code
+ */
+typedef enum {
+    CC_SHUTDOWN_NORMAL,
+    CC_SHUTDOWN_UNSPECIFIED,
+    CC_SHUTDOWN_VERMISMATCH
+} cc_shutdown_reason_t;
+
+/**
+ * Defines kpml value
+ */
+typedef enum {
+    CC_KPML_NONE = 0x0,
+    CC_KPML_SIGNAL_ONLY = 0x1,
+    CC_KPML_DTMF_ONLY = 0x2,
+    CC_KPML_BOTH = 0x3
+} cc_kpml_config_t;
+
+/**
+ * Defines whether to upgrade now or later to recently download firmware image
+ */
+typedef enum {
+       CC_UPGRADE_NONE = 0,
+       CC_UPGRADE_NOW,
+       CC_UPGRADE_LATER
+} cc_upgrade_t;
+
+/* Media flow direction */
+typedef enum {
+    CC_SDP_DIRECTION_INACTIVE,
+    CC_SDP_DIRECTION_SENDONLY,
+    CC_SDP_DIRECTION_RECVONLY,
+    CC_SDP_DIRECTION_SENDRECV,
+    CC_SDP_MAX_QOS_DIRECTIONS
+} cc_sdp_direction_t;
+
+/**
+ * Defines BLF state
+ */
+typedef enum {
+       CC_SIP_BLF_UNKNOWN,
+       CC_SIP_BLF_IDLE,
+       CC_SIP_BLF_INUSE,
+       CC_SIP_BLF_EXPIRED,
+       CC_SIP_BLF_REJECTED,
+       CC_SIP_BLF_ALERTING
+} cc_blf_state_t;
+
+/**
+ * Defines BLF feature mask
+ */
+typedef enum {
+    CC_BLF_FEATURE_MASK_NONE = 0,
+    CC_BLF_FEATURE_MASK_PICKUP
+} cc_blf_feature_mask_t;
+
+/**
+ * Defines call state
+ */
+typedef enum {
+       OFFHOOK,
+       ONHOOK,
+       RINGOUT,
+       RINGIN,
+       PROCEED,
+       CONNECTED,
+       HOLD,
+       REMHOLD,
+       RESUME,
+       BUSY,
+       REORDER,
+       CONFERENCE,
+       DIALING,
+       REMINUSE,
+       HOLDREVERT,
+       WHISPER,
+    PRESERVATION,
+       WAITINGFORDIGITS = 21,
+       CREATEOFFER,
+       CREATEANSWER,
+       CREATEOFFERERROR,
+       CREATEANSWERERROR,
+       SETLOCALDESC,
+       SETREMOTEDESC,
+       SETLOCALDESCERROR,
+       SETREMOTEDESCERROR,
+       REMOTESTREAMADD,
+    MAX_CALL_STATES
+} cc_call_state_t;
+
+/**
+ * Defines call attribute
+ */
+typedef enum {
+       CC_ATTR_NOT_DEFINED = -1,
+       CC_ATTR_NORMAL,
+       CC_ATTR_XFR_CONSULT,
+       CC_ATTR_CONF_CONSULT,
+       CC_ATTR_BARGING,
+       CC_ATTR_RIUHELD_LOCKED,
+       CC_ATTR_LOCAL_CONF_CONSULT,
+       CC_ATTR_LOCAL_XFER_CONSULT,
+       CC_ATTR_CFWDALL,
+       CC_ATTR_GRP_CALL_PICKUP,
+       CC_ATTR_CFWD_ALL,
+       CC_ATTR_MAX
+} cc_call_attr_t;
+
+/**
+ * Defines the hold reason
+ */
+typedef enum {
+   CC_HOLD_REASON_NONE,
+   CC_HOLD_REASON_XFER,
+   CC_HOLD_REASON_CONF,
+   CC_HOLD_REASON_SWAP,
+   CC_HOLD_REASON_INTERNAL
+} cc_hold_reason_t;
+
+/**
+ * Defines call type
+ */
+typedef enum {
+       CC_CALL_TYPE_NONE,
+       CC_CALL_TYPE_INCOMING,
+       CC_CALL_TYPE_OUTGOING,
+       CC_CALL_TYPE_FORWARDED
+} cc_call_type_t;
+
+/**
+ * Defines call security
+ */
+typedef enum {
+       CC_SECURITY_NONE,
+       CC_SECURITY_UNKNOWN,
+       CC_SECURITY_AUTHENTICATED,
+       CC_SECURITY_NOT_AUTHENTICATED,
+       CC_SECURITY_ENCRYPTED
+} cc_call_security_t;
+
+/**
+ * Defines call policy
+ */
+typedef enum {
+       /* Call Policy */
+       CC_POLICY_NONE,
+       CC_POLICY_UNKNOWN,
+       CC_POLICY_CHAPERONE
+} cc_call_policy_t;
+
+/**
+ * Defines Log disposition
+ */
+typedef enum {
+       CC_LOGD_MISSED,
+       CC_LOGD_RCVD,
+       CC_LOGD_SENT,
+       CC_LOGD_UNKNWN,
+       CC_LOGD_DELETE
+} cc_log_disposition_t;
+
+/**
+ * Defines call priority
+ */
+typedef enum{
+       CC_PRIORITY_NORMAL,
+       CC_PRIORITY_URGENT
+} cc_call_priority_t;
+
+/**
+ * Defines call selection
+ */
+typedef enum {
+       CC_CALL_SELECT_NONE,
+       CC_CALL_SELECT_LOCKED,
+       CC_CALL_SELECT_UNLOCKED,
+       CC_CALL_SELECT_REMOTE_LOCKED
+} cc_call_selection_t;
+
+/**
+ * Defines service control request
+ */
+typedef enum {
+       CC_DEVICE_RESET = 1,
+       CC_DEVICE_RESTART,
+    CC_DEVICE_ICMP_UNREACHABLE = 5
+} cc_srv_ctrl_req_t;
+
+/**
+ * Defines service control command
+ */
+typedef enum {
+       CC_ACTION_RESET = 1,
+       CC_ACTION_RESTART
+} cc_srv_ctrl_cmd_t;
+
+/**
+ * Defines messaging type
+ */
+typedef enum {
+       CC_VOICE_MESSAGE = 1,
+       CC_TEXT_MESSAGE
+} cc_message_type_t;
+
+/**
+ * Defines handset lamp state
+ */
+typedef enum {
+       CC_LAMP_NONE = 0,
+       CC_LAMP_ON,
+       CC_LAMP_BLINK,
+       CC_LAMP_FRESH
+} cc_lamp_state_t;
+
+/**
+ * defines call cause
+ * Important: when update this enum, please update the cc_cause_name accordingly.
+ */
+typedef enum {
+       CC_CAUSE_MIN = -1,
+       CC_CAUSE_BASE = -1,
+       CC_CAUSE_OK,
+       CC_CAUSE_ERROR,
+       CC_CAUSE_UNASSIGNED_NUM,
+       CC_CAUSE_NO_RESOURCE,
+       CC_CAUSE_NO_ROUTE,
+       CC_CAUSE_NORMAL,
+       CC_CAUSE_BUSY,
+       CC_CAUSE_NO_USER_RESP,
+       CC_CAUSE_NO_USER_ANS,
+       CC_CAUSE_REJECT,
+       CC_CAUSE_INVALID_NUMBER,
+       CC_CAUSE_FACILITY_REJECTED,
+       CC_CAUSE_CALL_ID_IN_USE,
+       CC_CAUSE_XFER_LOCAL,
+       CC_CAUSE_XFER_REMOTE,
+       CC_CAUSE_XFER_BY_REMOTE,
+       CC_CAUSE_XFER_CNF,
+       CC_CAUSE_CONGESTION,
+       CC_CAUSE_ANONYMOUS,
+       CC_CAUSE_REDIRECT,
+       CC_CAUSE_PAYLOAD_MISMATCH,
+       CC_CAUSE_CONF,
+       CC_CAUSE_REPLACE,
+       CC_CAUSE_NO_REPLACE_CALL,
+       CC_CAUSE_NO_RESUME,
+       CC_CAUSE_NO_MEDIA,
+       CC_CAUSE_REQUEST_PENDING,
+       CC_CAUSE_INVALID_PARTICIPANT,
+       CC_CAUSE_NO_CNF_BRIDE,
+       CC_MAXIMUM_PARTICIPANT,
+       CC_KEY_NOT_ACTIVE,
+       CC_TEMP_NOT_AVAILABLE,
+       CC_CAUSE_REMOTE_SERVER_ERROR,
+       CC_CAUSE_BARGE,
+       CC_CAUSE_CBARGE,
+       CC_CAUSE_NOT_FOUND,
+       CC_CAUSE_SECURITY_FAILURE,
+       CC_CAUSE_MONITOR,
+       CC_CAUSE_UI_STATE_BUSY,
+       CC_SIP_CAUSE_ANSWERED_ELSEWHERE,
+       CC_CAUSE_RETRIEVED,
+       CC_CAUSE_FORWARDED,
+       CC_CAUSE_ABANDONED,
+       CC_CAUSE_XFER_LOCAL_WITH_DIALSTRING,
+       CC_CAUSE_BW_OK,
+       CC_CAUSE_XFER_COMPLETE,
+       CC_CAUSE_RESP_TIMEOUT,
+       CC_CAUSE_SERV_ERR_UNAVAIL,
+    CC_CAUSE_REMOTE_DISCONN_REQ_PLAYTONE,
+    CC_CAUSE_OUT_OF_MEM,
+    CC_CAUSE_VALUE_NOT_FOUND,
+       CC_CAUSE_MAX
+} cc_cause_t;
+
+/**
+ * defines subscription event type
+ */
+typedef enum {
+       CC_SUBSCRIPTIONS_DIALOG_EXT = 2,
+    CC_SUBSCRIPTIONS_KPML_EXT = 4,
+    CC_SUBSCRIPTIONS_PRESENCE_EXT = 5,
+    CC_SUBSCRIPTIONS_REMOTECC_EXT = 6,
+    CC_SUBSCRIPTIONS_REMOTECC_OPTIONSIND_EXT = 7,
+    CC_SUBSCRIPTIONS_CONFIGAPP_EXT = 8,
+    CC_SUBSCRIPTIONS_MEDIA_INFO_EXT = 10
+} cc_subscriptions_ext_t;
+
+typedef enum {
+    APPLY_DYNAMICALLY=0,
+    RESTART_NEEDED,
+    APPLY_CONFIG_NONE
+} cc_apply_config_result_t;
+
+
+/**
+ * defines ID of cucm
+ */
+
+typedef enum {
+    PRIMARY_CCM = 0,
+    SECONDARY_CCM,
+    TERTIARY_CCM,
+    MAX_CCM,
+    UNUSED_PARAM
+} CCM_ID;
+
+typedef enum {
+    CC_SIS_B2B_CONF = 0,
+    CC_SIS_SWAP,
+    CC_SIS_CFWD_ANY_LINE
+} cc_sis_feature_id_e;
+
+/**
+ * enum for conference participant status
+ */
+
+typedef enum {
+   CCAPI_CONFPARTICIPANT_UNKNOWN,
+   CCAPI_CONFPARTICIPANT_DIALING_OUT,
+   CCAPI_CONFPARTICIPANT_ALERTING,
+   CCAPI_CONFPARTICIPANT_CONNECTED,
+   CCAPI_CONFPARTICIPANT_ON_HOLD,
+   CCAPI_CONFPARTICIPANT_DISCONNECTED
+} cc_conf_participant_status_t;
+
+
+typedef enum {
+  JSEP_NO_ACTION = -1,
+  JSEP_OFFER,
+  JSEP_ANSWER,
+  JSEP_PRANSWER
+} cc_jsep_action_t;
+
+
+typedef cc_string_t cc_peerconnection_t;
+
+typedef unsigned int cc_media_stream_id_t;
+
+typedef unsigned int cc_media_track_id_t;
+
+
+typedef enum {
+  NO_STREAM = -1,
+  AUDIO,
+  VIDEO,
+  TYPE_MAX
+} cc_media_type_t;
+
+
+typedef struct {
+  char        *name;
+  char        *value;
+  cc_boolean   mandatory;
+} cc_media_constraint_t;
+
+typedef struct {
+  cc_media_constraint_t**  constraints;
+  cc_uint16_t              constraint_count;
+} cc_media_constraints_t;
+
+#endif /* _CC_CONSTANTS_H_ */
+
diff --git a/libs/sipcc/include/cc_debug.h b/libs/sipcc/include/cc_debug.h
new file mode 100644 (file)
index 0000000..308a7f7
--- /dev/null
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CC_DEBUG_H_
+#define CC_DEBUG_H_
+#include "cc_types.h"
+#include <cpr_stdio.h>
+
+extern cc_int32_t VCMDebug;
+extern cc_int32_t PLATDebug;
+
+#define PLAT_ERROR        err_msg
+#define VCM_ERR           err_msg
+
+#define VCM_DEBUG     if (VCMDebug)    buginf
+#define PLAT_DEBUG    if (PLATDebug)  buginf
+
+//DEBUG message prefixes
+#define PLAT_F_PREFIX "PLAT : %s : "
+#define PLAT_A_F_PREFIX "PLAT : %s : %s :"
+#define PLAT_L_C_F_PREFIX "PLAT :  %d/%d : %s : " // line/call/fname as arg
+#define VCM_F_PREFIX "VCM : %s : "
+#define VCM_A_F_PREFIX "VCM : %s : %s :"
+#define VCM_L_C_F_PREFIX "%s :  %d/%d : %s : " // line/call/fname as arg
+#define PLAT_F_PREFIX_ARGS(msg_name, func_name) msg_name, func_name
+#define PLAT_L_C_F_PREFIX_ARGS(msg_name, line, call_id, func_name) \
+    msg_name, line, call_id, func_name
+
+
+//
+#define PLAT_API "PLAT_API" // platform API
+#define VCM_API  "VCM_API"  // vcm api
+
+#endif /* CC_DEBUG_H_ */
diff --git a/libs/sipcc/include/cc_device_feature.h b/libs/sipcc/include/cc_device_feature.h
new file mode 100644 (file)
index 0000000..c772a96
--- /dev/null
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_DEVICE_FEATURE_H_
+#define _CC_DEVICE_FEATURE_H_
+
+#include "cc_constants.h"
+
+
+/**
+ * Enable or disable video capability of the device.
+ * @param enable - a flag to indicate that application wants to enable of
+ * disable video capability of the device.
+ * @return void
+ */
+void CC_DeviceFeature_enableVideo(cc_boolean enable);
+
+/**
+ * Indicate native video support for the device
+ * @param enable - a flag to indicate that device supports native video
+ * @return void
+ */
+void CC_DeviceFeature_supportsVideo(cc_boolean enable);
+
+/**
+ * Enable or disable camera capability of the device.
+ * @param enable - a flag to indicate that application wants to enable of
+ * disable camera capability of the device.
+ * @return void
+ */
+void CC_DeviceFeature_enableCamera(cc_boolean enable);
+
+
+#endif /* _CC_DEVICE_FEATURE_H_ */
diff --git a/libs/sipcc/include/cc_device_listener.h b/libs/sipcc/include/cc_device_listener.h
new file mode 100644 (file)
index 0000000..0fe9e4c
--- /dev/null
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_DEVICE_LISTENER_H_
+#define _CC_DEVICE_LISTENER_H_
+#include "cc_constants.h"
+
+/***********************************Device feature listener methods**************************************/
+/**
+ * Inform an application about the call forward status
+ * @param line line number
+ * @param is_fwd if the call forward set is a success or not.
+ * @param is_local is cfwd a local configured or supportted by cucm
+ * @param cfa_num call farward all number
+ * @return void
+ */
+void CC_DeviceListener_callForwardAllStateChanged(cc_lineid_t line, cc_boolean is_fwd, cc_boolean is_local, cc_string_t cfa_num);
+
+/**
+ * Informa an application that a specifical line has message waiting.
+ * @param line line number
+ * @param state has message waiting or not
+ * @param message_type type of message waiting, e.g. voice message, text message, etc.
+ * @param new_count the count of new message waiting.
+ * @param old_count the count of old message waiting.
+ * @param high_priority_new_count the count of high priority message waiting.
+ * @param high_priority_old_count the count of old high priority message waiting.
+ * @return void
+ */
+void CC_DeviceListener_mwiStateUpdated(cc_lineid_t line, cc_boolean state,
+               cc_message_type_t message_type,
+               int new_count,
+               int old_count,
+               int high_priority_new_count,
+               int high_priority_old_count);
+
+/**
+ * Update the handset lamp light
+ * @param lamp_state lamp state
+ * @return void
+ */
+void CC_DeviceListener_mwiLampUpdated(cc_lamp_state_t lamp_state);
+
+/**
+ * Inform application if the maximum number of call for the particular line is reached or not.
+ * @param line the line is reported
+ * @param state true or false
+ * @return void
+ */
+void CC_DeviceListener_mncReached(cc_lineid_t line, cc_boolean state);
+
+/**
+ * Call Status/prompt notifications
+ * @param time display duration
+ * @param display_progress
+ * @param priority see definition for notification priority in cc_constants.h
+ * @param prompt phrase to display
+ * @return void
+ */
+void CC_DeviceListener_displayNotify(int time, cc_boolean display_progress, char priority, cc_string_t prompt);
+
+/**
+ * Update speed dial label
+ * @param line the line number
+ * @param button_no the button number
+ * @param speed_dial_number the speed dial number
+ * @param label the label to display for the line if line visual presentation is available.
+ * @return void
+ */
+void CC_DeviceListener_labelNSpeedUpdated(cc_lineid_t line,
+               int button_no,
+               cc_string_t speed_dial_number,
+               cc_string_t label);
+
+#endif /* _CC_DEVICE_LISTENER_H_ */
diff --git a/libs/sipcc/include/cc_info.h b/libs/sipcc/include/cc_info.h
new file mode 100644 (file)
index 0000000..478bdff
--- /dev/null
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_INFO_H_
+#define _CC_INFO_H_
+
+#include "cc_constants.h"
+
+/**
+ * Defines to be used for info_package info_type and info body in CC_Info_sendInfo()
+ * to send FAST PICTURE UPDATE INFO out
+ */
+#define FAST_PIC_INFO_PACK  ""
+#define FAST_PIC_CTYPE  "application/media_control+xml"
+#define FAST_PIC_MSG_BODY "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n\
+        <media_control>\n\
+          <vc_primitive>\n\
+            <to_encoder>\n\
+              <picture_fast_update/>\n\
+            </to_encoder>\n\
+          </vc_primitive>\n\
+        </media_control>"
+
+
+/**
+ * Send SIP INFO message with the specified event_type and xml body
+ * @param call_handle call handle
+ * @param info_package the Info-Package header of the INFO message
+ * @param info_type the Content-Type header of the INFO message
+ * @param info_body the message body of the INFO message
+ * @return void
+ */
+void CC_Info_sendInfo(cc_call_handle_t call_handle,
+               cc_string_t info_package,
+               cc_string_t info_type,
+               cc_string_t info_body);
+
+#endif /* _CC_INFO_H_ */
diff --git a/libs/sipcc/include/cc_info_listener.h b/libs/sipcc/include/cc_info_listener.h
new file mode 100644 (file)
index 0000000..9619d62
--- /dev/null
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_INFO_LISTENER_H_
+#define _CC_INFO_LISTENER_H_
+
+#include "cc_constants.h"
+
+/**
+ * Recieve SIP INFO for the specified session
+ * @param call_handle call handle
+ * @param pack_id the info package id number
+ * @param info_package the Info-Package header of the Info Package
+ * @param info_type the Content-Type header of the Info Package
+ * @param info_body the message body of the Info Package
+ * @return void
+ */
+void CC_InfoListener_receivedInfo(cc_call_handle_t call_handle, int pack_id,
+               cc_string_t info_package,
+               cc_string_t info_type,
+               cc_string_t info_body);
+
+#endif /* _CC_INFO_LISTENER_H_ */
diff --git a/libs/sipcc/include/cc_service.h b/libs/sipcc/include/cc_service.h
new file mode 100644 (file)
index 0000000..72eb560
--- /dev/null
@@ -0,0 +1,77 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_SERVICE_H_
+#define _CC_SERVICE_H_
+
+#include "cc_constants.h"
+
+/**
+ * Defines the management methods.
+ */
+/**
+ * The following methods are defined to bring up the pSipcc stack
+ */
+/**
+ * Initialize the pSipcc stack. CC_Service_create() must be called before
+ * calling this function.
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_Service_init();
+
+/**
+ * This function creates various data module needed for initialization of
+ * pSipcc stack. On reboot or after CC_Service_destroy(), application must call
+ * first this function followed by CC_Service_init() and then
+ * CC_Service_start() to bring pSipcc stack in in-service. This function
+ * need to be called only once.
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_Service_create();
+
+/**
+ * Gracefully unload the pSipcc stack. To bring up the pSipcc stack again,
+ * follow the function calling sequence starting from CC_Service_create().
+ * @return SUCCESS
+ */
+cc_return_t CC_Service_destroy();
+
+/**
+ * Bring up the pSipcc stack in service. CC_Service_init() must be called
+ * before calling this function.
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_Service_start();
+
+/**
+ * Shutdown pSipcc stack for restarting
+ * @param mgmt_reason the reason to shutdown pSipcc stack
+ * @param reason_string literal string for shutdown
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_Service_shutdown(cc_shutdown_reason_t mgmt_reason, cc_string_t reason_string);
+
+/**
+ * Unregister all lines of a phone
+ * @param mgmt_reason the reason to bring down the registration
+ * @param reason_string the literal string for unregistration
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_Service_unregisterAllLines(cc_shutdown_reason_t mgmt_reason, cc_string_t reason_string);
+
+/**
+ * Register all lines for a phone.
+ * @param mgmt_reason the reason of registration
+ * @param reason_string the literal string of the registration
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_Service_registerAllLines(cc_shutdown_reason_t mgmt_reason, cc_string_t reason_string);
+
+/**
+ * Restart pSipcc stack
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CC_Service_restart();
+
+#endif /* _CC_SERVICE_H_ */
diff --git a/libs/sipcc/include/cc_service_listener.h b/libs/sipcc/include/cc_service_listener.h
new file mode 100644 (file)
index 0000000..be6f83e
--- /dev/null
@@ -0,0 +1,125 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_SERVICE_LISTENER_H_
+#define _CC_SERVICE_LISTENER_H_
+
+#include "cc_constants.h"
+
+/**
+ * The following section defines all callback method that the pSipcc stack will provide
+ * for configuration purpose.
+ */
+/**
+ * Notify appliation that the pSipcc stack is out of service.
+ * Application can, for example, use this notification to inform user that
+ * device is currently out of service.
+ * @param cucm_mode - the cucm mode of call manager to which device was
+ * connected before device went out of service.
+ * @param service_cause [out]  the cause of out of service.
+ * @return void
+ */
+void CC_ServiceListener_outOfService(cc_cucm_mode_t cucm_mode, cc_service_cause_t service_cause);
+
+/**
+ * Notify application that the pSipcc stack is up in service. Application can,
+ * for example, use this notification to inform user that device is currently
+ * in service.
+ * @param cucm_mode - the cucm mode of call manager to which device is
+ * connected.
+ * @param service_cause [out] - the cause of INS
+ * @param sis_ver_name version name
+ * @param major_number the major version number
+ * @param minor_number the minor version number
+ * @param additional_number the additional version number
+ * @return void
+ */
+void CC_ServiceListener_inService(cc_cucm_mode_t cucm_mode,
+        cc_service_cause_t service_cause,
+        cc_string_t sis_ver_name,
+        cc_uint32_t major_number,
+        cc_uint32_t minor_number,
+        cc_uint32_t additional_number);
+
+
+/**
+ * Inform application that pSipcc stack has receive a NOTIFY message of event
+ * type "service-control" and action=apply-config. The arguments passed to this
+ * function contains the parameter values received in the message.
+ *
+ * @param config_version [in] latest version stamp of phone configuration.
+ * @param dial_plan_version [in] latest version stamp of the dial plan.
+ * @param fcp_version [in] latest version stamp of feature policy control.
+ * @param cucm_result  [in] action taken by the cucm for the changes applied by the
+ * user to the phone configuration. cucm result could be
+ *  @li "no_change" - the new phone configuration changes do not impact Unified CM,
+ *  @li "config_applied" - The Unified CM Administration applied the
+ *  configuration changes dynamically without requiring phone to re-register,
+ *  @li "reregister_needed" - The Unified CM Administration applied the
+ *  configuration changes and required the phone to re-register to make them
+ *  effective.
+ * @param load_id [in] - firmware image name that phone should be running with
+ * @param inactive_load_d [in] firmaware image name for the inactive load image
+ * @param load_server [in] - address of load server to download the  firmware
+ * image load_id.
+ * @param log_server [in] - log server address where error report need to be
+ * sent. Error report, for example, could be related to image download failure
+ * or phone configuration file download failure.
+ * @param ppid [in] whether peer to peer upgrade is available
+ * @return void
+ */
+void CC_ServiceListener_applyConfigNotify(cc_string_t config_version,
+               cc_string_t dial_plan_version,
+               cc_string_t fcp_version,
+               cc_string_t cucm_result,
+               cc_string_t load_id,
+               cc_string_t inactive_load_id,
+               cc_string_t load_server,
+               cc_string_t log_server,
+               cc_boolean ppid);
+
+/**
+ * Notify the server time. Application can use this function to adjust the
+ * device time.
+ * @param time - time in second
+ * @return void
+ */
+void CC_ServiceListener_serverTimeNotify(long time);
+
+/**
+ * Update the phone registration state.
+ * @param line the line
+ * @param reg_state the registration state
+ * @return void
+ */
+void CC_ServiceListener_registrationStateChanged(cc_lineid_t line, cc_line_reg_state_t reg_state);
+
+/**
+ * Update cucm connection status.
+ * @param address the cucm address
+ * @param reg_ccm_state the connection state for the cucm.
+ * @return void
+ */
+void CC_ServiceListener_cucmConnectionStatusChanged(cc_string_t address, cc_ccm_status_t reg_ccm_state);
+
+/**
+ * Sync up configuration version.
+ * @param config_version the configuration version
+ * @param dial_plan_version the dial plan version
+ * @param softkey_version the softkey set version
+ * @return void
+ */
+void CC_ServiceListener_configVersionNotify(cc_string_t config_version,
+               cc_string_t dial_plan_version,
+               cc_string_t softkey_version);
+
+/**
+ * Notifies incoming service control command from CUCM
+ * @param srv_ctrl_type request a reset or restart.
+ * @return void
+ */
+void CC_ServiceListener_serviceControlNotify(cc_srv_ctrl_cmd_t srv_ctrl_type);
+
+
+#endif /* _CC_SERVICE_LISTENER_H_ */
diff --git a/libs/sipcc/include/cc_types.h b/libs/sipcc/include/cc_types.h
new file mode 100644 (file)
index 0000000..ab8b2a3
--- /dev/null
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CC_TYPES_H_
+#define _CC_TYPES_H_
+
+typedef char            cc_int8_t;
+typedef unsigned char   cc_uint8_t;
+typedef short           cc_int16_t;
+typedef unsigned short  cc_uint16_t;
+typedef int             cc_int32_t;
+typedef unsigned int    cc_uint32_t;
+typedef unsigned long   cc_uint64_t;
+
+typedef unsigned long cc_ulong_t;
+typedef unsigned char cc_uchar_t;
+
+typedef cc_uint8_t cc_boolean;
+typedef cc_uint32_t cc_size_t;
+
+#endif /*_CC_TYPES_H_*/
diff --git a/libs/sipcc/include/ccapi_call.h b/libs/sipcc/include/ccapi_call.h
new file mode 100644 (file)
index 0000000..764838d
--- /dev/null
@@ -0,0 +1,246 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_CALL_H_
+#define _CCAPI_CALL_H_
+
+#include "ccapi_types.h"
+
+/**
+ * Get reference to call info
+ * @param [in] handle - call handle
+ * @return cc_call_info_snap_t
+ * NOTE: The info returned by this method must be released using CCAPI_Call_releaseCallInfo()
+ */
+cc_callinfo_ref_t CCAPI_Call_getCallInfo(cc_call_handle_t handle);
+
+/**
+ * Release the resources held by this call info snapshot
+ * @param [in] ref - refrence to the call info to be freed
+ * @return void
+ */
+void CCAPI_Call_releaseCallInfo(cc_callinfo_ref_t ref);
+
+/**
+ *  Retain the call info reference
+ *  @param [in] ref - reference to the call info to be retained
+ *  @return void
+ *  NOTE: Application that has retained callInfo must call CCAPI_Call_releaseCallInfo()
+ *  to free memory associated with this Info.
+ */
+void CCAPI_Call_retainCallInfo(cc_callinfo_ref_t ref);
+
+/**
+ * get the line associated with this call
+ * @param [in] handle - call handle
+ * @return cc_lineid_t
+ */
+cc_lineid_t CCAPI_Call_getLine(cc_call_handle_t call_handle);
+
+/**
+ * Originate call - API to go offhook and dial specified digits on a given call
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @param [in] digits - digits to be dialed. can be empty then this API simply goes offhook
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_originateCall(cc_call_handle_t handle, cc_sdp_direction_t video_pref, cc_string_t digits);
+
+
+cc_return_t CCAPI_CreateOffer(cc_call_handle_t handle, const cc_media_constraints_t *constraints);
+
+cc_return_t CCAPI_CreateAnswer(cc_call_handle_t handle, const cc_media_constraints_t *constraints);
+
+cc_return_t CCAPI_SetLocalDescription(cc_call_handle_t handle, cc_jsep_action_t action, cc_string_t sdp);
+
+cc_return_t CCAPI_SetRemoteDescription(cc_call_handle_t handle, cc_jsep_action_t action, cc_string_t sdp);
+
+cc_return_t CCAPI_SetPeerConnection(cc_call_handle_t handle, cc_peerconnection_t pc);
+
+cc_return_t CCAPI_AddStream(cc_call_handle_t handle, cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type);
+
+cc_return_t CCAPI_RemoveStream(cc_call_handle_t handle, cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type);
+
+cc_return_t CCAPI_AddICECandidate(cc_call_handle_t handle, cc_string_t candidate, cc_string_t mid, cc_level_t level);
+
+/**
+ * Send digits on the call - can be invoked either to dial additional digits or send DTMF
+ * @param [in] handle - call handle
+ * @param [in] digit - digit to be dialed
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_sendDigit(cc_call_handle_t handle, cc_digit_t digit);
+
+/**
+ * Send Backspace - Delete last digit dialed.
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_backspace(cc_call_handle_t handle);
+
+/**
+ * Answer Call
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_answerCall(cc_call_handle_t handle, cc_sdp_direction_t video_pref);
+
+/**
+ * Redial
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_redial(cc_call_handle_t handle, cc_sdp_direction_t video_pref);
+
+/**
+ * Initiate Call Forward All
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_initiateCallForwardAll(cc_call_handle_t handle);
+/**
+ * Hold
+ * @param [in] handle - call handle
+ * @param [in] reason - hold reason
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_hold(cc_call_handle_t handle, cc_hold_reason_t reason);
+
+/**
+ * Resume
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_resume(cc_call_handle_t handle, cc_sdp_direction_t video_pref) ;
+
+/**
+ * end Consult leg - used to end consult leg when the user picks active calls list for xfer/conf
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_endConsultativeCall(cc_call_handle_t handle);
+
+/**
+ * end Call
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_endCall(cc_call_handle_t handle);
+
+/**
+ * Initiate a conference
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on consult call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_conferenceStart(cc_call_handle_t handle, cc_sdp_direction_t video_pref);
+
+/**
+ * complete conference
+ * @param [in] handle - call handle
+ * @param [in] phandle - call handle of the other leg
+ * @param [in] video_pref - video direction desired on consult call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_conferenceComplete(cc_call_handle_t handle, cc_call_handle_t phandle,
+                          cc_sdp_direction_t video_pref);
+
+/**
+ * start transfer
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on consult call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_transferStart(cc_call_handle_t handle, cc_sdp_direction_t video_pref);
+
+/**
+ * complete transfer
+ * @param [in] handle - call handle
+ * @param [in] phandle - call handle of the other leg
+ * @param [in] video_pref - video direction desired on consult call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_transferComplete(cc_call_handle_t handle, cc_call_handle_t phandle,
+                          cc_sdp_direction_t video_pref);
+
+/**
+ * cancel conference or transfer
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_cancelTransferOrConferenceFeature(cc_call_handle_t handle);
+
+/**
+ * direct Transfer
+ * @param [in] handle - call handle
+ * @param [in] target - call handle for transfer target call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_directTransfer(cc_call_handle_t handle, cc_call_handle_t target);
+
+/**
+ * Join Across line
+ * @param [in] handle - call handle
+ * @param [in] target - join target
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_joinAcrossLine(cc_call_handle_t handle, cc_call_handle_t target);
+
+/**
+ * BLF Call Pickup
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction preference
+ * @param [in] speed - speedDial Number
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_blfCallPickup(cc_call_handle_t handle, cc_sdp_direction_t video_pref, cc_string_t speed);
+
+/**
+ * Select a call
+ * @param [in] handle - call handle
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_select(cc_call_handle_t handle);
+
+/**
+ * Update Video Media Cap for the call
+ * @param [in] handle - call handle
+ * @param [in] video_pref - video direction desired on call
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_updateVideoMediaCap(cc_call_handle_t handle, cc_sdp_direction_t video_pref);
+
+/**
+ * send INFO method for the call
+ * @param [in] handle - call handle
+ * @param [in] infopackage - Info-Package header value
+ * @param [in] infotype - Content-Type header val
+ * @param [in] infobody - Body of the INFO message
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Call_sendInfo(cc_call_handle_t handle, cc_string_t infopackage, cc_string_t infotype, cc_string_t infobody);
+
+/**
+ * API to mute/unmute audio
+ * @param [in] val - TRUE=> mute FALSE => unmute
+ * @return SUCCESS or FAILURE
+ * NOTE: The mute state is persisted within the stack and shall be remembered across hold/resume.
+ * This API doesn't perform the mute operation but simply caches the mute state of the session.
+ */
+cc_return_t CCAPI_Call_setAudioMute(cc_call_handle_t handle, cc_boolean val);
+
+/**
+ * API to mute/unmute Video
+ * @param [in] val - TRUE=> mute FALSE => unmute
+ * @return SUCCESS or FAILURE
+ * NOTE: The mute state is persisted within the stack and shall be remembered across hold/resume
+ * This API doesn't perform the mute operation but simply caches the mute state of the session.
+ */
+cc_return_t CCAPI_Call_setVideoMute(cc_call_handle_t handle, cc_boolean val);
+
+
+#endif // _CCAPI_CALL_H_
diff --git a/libs/sipcc/include/ccapi_call_info.h b/libs/sipcc/include/ccapi_call_info.h
new file mode 100644 (file)
index 0000000..f23bef1
--- /dev/null
@@ -0,0 +1,305 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_CALL_INFO_H_
+#define _CCAPI_CALL_INFO_H_
+
+#include "ccapi_types.h"
+#include "peer_connection_types.h"
+
+/**
+ * get Line on which this call is
+ * @param [in] handle - call info handle
+ * @return cc_line_id_t - line ID
+ */
+cc_lineid_t CCAPI_CallInfo_getLine(cc_callinfo_ref_t handle);
+
+/**
+ * get Call state
+ * @param [in] handle - call info handle
+ * @return call state
+ */
+cc_call_state_t CCAPI_CallInfo_getCallState(cc_callinfo_ref_t handle);
+
+/**
+ * get call attributes
+ * @param [in] handle - call info handle
+ * @return call attributes
+ */
+cc_call_attr_t CCAPI_CallInfo_getCallAttr(cc_callinfo_ref_t handle);
+
+/**
+ * get Call Type
+ * @param [in] handle - call info handle
+ * @return call type
+ */
+cc_call_type_t CCAPI_CallInfo_getCallType(cc_callinfo_ref_t handle);
+
+/**
+ * get Called party name
+ * @param [in] handle - call info handle
+ * @return called party name
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getCalledPartyName(cc_callinfo_ref_t handle);
+
+/**
+ * get Called party number
+ * @param [in] handle - call info handle
+ * @return called party number
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getCalledPartyNumber(cc_callinfo_ref_t handle);
+
+/**
+ * get Calling party name
+ * @param [in] handle - call info handle
+ * @return calling party name
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getCallingPartyName(cc_callinfo_ref_t handle);
+
+/**
+ * get Calling party number
+ * @param [in] handle - call info handle
+ * @return calling party number
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getCallingPartyNumber(cc_callinfo_ref_t handle);
+
+/**
+ * get Calling party number
+ * @param [in] handle - call info handle
+ * @return calling party number
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getAlternateNumber(cc_callinfo_ref_t handle);
+
+/**
+ * get Original Called party name
+ * @param [in] handle - call info handle
+ * @return original called party name
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getOriginalCalledPartyName(cc_callinfo_ref_t handle);
+
+/**
+ * get Original Called party number
+ * @param [in] handle - call info handle
+ * @return original called party number
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getOriginalCalledPartyNumber(cc_callinfo_ref_t handle);
+
+/**
+ * get last redirecting party name
+ * @param [in] handle - call info handle
+ * @return last redirecting party name
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getLastRedirectingPartyName(cc_callinfo_ref_t handle);
+
+/**
+ * get past redirecting party number
+ * @param [in] handle - call info handle
+ * @return last redirecting party number
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getLastRedirectingPartyNumber(cc_callinfo_ref_t handle);
+
+/**
+ * get placed call party name
+ * @param [in] handle - call info handle
+ * @return placed party name
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getPlacedCallPartyName(cc_callinfo_ref_t handle);
+
+/**
+ * get placed call party number
+ * @param [in] handle - call info handle
+ * @return placed party number
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getPlacedCallPartyNumber(cc_callinfo_ref_t handle);
+
+
+/**
+ * get call instance number
+ * @param [in] handle - call info handle
+ * @return
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_int32_t CCAPI_CallInfo_getCallInstance(cc_callinfo_ref_t handle);
+
+/**
+ * get call status prompt
+ * @param [in] handle - call info handle
+ * @return call status
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getStatus(cc_callinfo_ref_t handle);
+
+/**
+ * get call security   // TODO XLS has callagent security and endtoend security on call?
+ * @param [in] handle - call info handle
+ * @return call security status
+ */
+cc_call_security_t CCAPI_CallInfo_getSecurity(cc_callinfo_ref_t handle);
+
+/**
+ * get Call Selection Status
+ * @param [in] handle - call info handle
+ * @return cc_boolean - TRUE => selected
+ */
+cc_int32_t CCAPI_CallInfo_getSelectionStatus(cc_callinfo_ref_t handle);
+
+/**
+ * get GCID
+ * @param [in] handle - call info handle
+ * @return GCID
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_CallInfo_getGCID(cc_callinfo_ref_t handle);
+
+/**
+ * get ringer loop count
+ * @param handle - call handle
+ * @return once Vs continuous
+ */
+cc_boolean CCAPI_CallInfo_getIsRingOnce(cc_callinfo_ref_t handle);
+
+/**
+ * get ringer state.
+ * @param handle - call handle
+ * @return ringer state.
+ */
+cc_boolean CCAPI_CallInfo_getRingerState(cc_callinfo_ref_t handle);
+
+/**
+ * get ringer mode
+ * @param handle - call handle
+ * @return ringer mode
+ */
+int CCAPI_CallInfo_getRingerMode(cc_callinfo_ref_t handle);
+
+
+/**
+ * get onhook reason
+ * @param [in] handle - call info handle
+ * @return onhook reason
+ */
+cc_int32_t  CCAPI_CallInfo_getOnhookReason(cc_callinfo_ref_t handle);
+
+/**
+ * is Conference Call?
+ * @param [in] handle - call info handle
+ * @return boolean - is Conference
+ */
+cc_boolean  CCAPI_CallInfo_getIsConference(cc_callinfo_ref_t handle);
+
+/**
+ * getStream Statistics
+ * @param [in] handle - call info handle
+ * @param [in,out] stats - Array to get the stats
+ * @param [in,out] count - in len of stats arraysize of stats / out stats copied
+ * @return cc_return_t - CC_SUCCESS or CC_FAILURE
+ */
+cc_return_t  CCAPI_CallInfo_getStreamStatistics(cc_callinfo_ref_t handle, cc_int32_t stats[], cc_int32_t *count);
+
+
+/**
+ * has capability - is the feature allowed
+ * @param [in] handle - call info handle
+ * @param [in] feat_id - feature id
+ * @return boolean - is Allowed
+ */
+cc_boolean  CCAPI_CallInfo_hasCapability(cc_callinfo_ref_t handle, cc_int32_t feat_id);
+
+/**
+ * get Allowed Feature set
+ * @param [in] handle - call info handle
+ * @param [in,out] feat_set - array of len CC_CALL_CAP_MAX
+ * @return cc_return_t - CC_SUCCESS or CC_FAILURE
+ */
+cc_return_t  CCAPI_CallInfo_getCapabilitySet(cc_callinfo_ref_t handle, cc_int32_t feat_set[]);
+
+/**
+ * Call selection status
+ * @param [in] handle - call info handle
+ * @return cc_boolean - selection status
+ */
+cc_boolean  CCAPI_CallInfo_isCallSelected(cc_callinfo_ref_t handle);
+
+/**
+ * INFO Package for RECEIVED_INFO event
+ * @param [in] handle - call info handle
+ * @return cc_string_t - Info package header
+ */
+cc_string_t  CCAPI_CallInfo_getINFOPack(cc_callinfo_ref_t handle);
+
+/**
+ * INFO type for RECEIVED_INFO event
+ * @param [in] handle - call info handle
+ * @return cc_string_t - content-type  header
+ */
+cc_string_t  CCAPI_CallInfo_getINFOType(cc_callinfo_ref_t handle);
+
+/**
+ * INFO body for RECEIVED_INFO event
+ * @param [in] handle - call info handle
+ * @return cc_string_t - INFO body
+ */
+cc_string_t  CCAPI_CallInfo_getINFOBody(cc_callinfo_ref_t handle);
+
+/**
+ * Get the call log reference
+ * @param [in] handle - call info handle
+ * @return cc_string_t - INFO body
+ * NOTE: Memory associated with the call log is tied to the cc_callinfo_ref_t handle
+ * this would be freed when the callinfo ref is freed.
+ */
+cc_calllog_ref_t  CCAPI_CallInfo_getCallLogRef(cc_callinfo_ref_t handle);
+
+/**
+ * returns the negotiated video direction for this call
+ * @param [in] handle - call handle
+ * @return cc_sdp_direction_t - video direction
+ */
+cc_sdp_direction_t  CCAPI_CallInfo_getVideoDirection(cc_callinfo_ref_t handle);
+
+/**
+ * Returns the Audio mute state for this call
+ * @return boolean true=muted false=not muted
+ */
+cc_boolean CCAPI_CallInfo_isAudioMuted(cc_callinfo_ref_t handle);
+
+/**
+ * Returns the Video  mute state for this call
+ * @return boolean true=muted false=not muted
+ */
+cc_boolean CCAPI_CallInfo_isVideoMuted(cc_callinfo_ref_t handle);
+
+/**
+ * get SDP string CreateOffer and CreateAnswer callback
+ * @param [in] handle - call info handle
+ * @return sdp
+ */
+cc_string_t CCAPI_CallInfo_getSDP(cc_callinfo_ref_t handle);
+
+/**
+ * get status code from internal JSEP functions
+ * @param [in] handle - call info handle
+ * @return status code
+ */
+cc_int32_t  CCAPI_CallInfo_getStatusCode(cc_callinfo_ref_t handle);
+
+/**
+ * get media stream table
+ * @param [in] handle - call info handle
+ * @return media track table
+ */
+MediaStreamTable* CCAPI_CallInfo_getMediaStreams(cc_callinfo_ref_t handle);
+
+#endif /* _CCAPIAPI_CALL_INFO_H_ */
diff --git a/libs/sipcc/include/ccapi_call_listener.h b/libs/sipcc/include/ccapi_call_listener.h
new file mode 100644 (file)
index 0000000..1a78c46
--- /dev/null
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_CALL_LISTENER_H_
+#define _CCAPI_CALL_LISTENER_H_
+
+#include "ccapi_types.h"
+
+/**
+ * Call Listener callback
+ * @param [in] event - call event
+ * @param [in] handle - call handle for the event
+ * @param [in] info - reference to call info
+ * @return void
+ * NOTE: The memory associated with callInfo will be freed immediately upon return from this method.
+ * If the application wishesd to retain a copy it should invoke CCAPI_Call_retainCallInfo() API. Once retained
+ * it can be released by invoking the CCAPI_Call_releaseCallInfo API.
+ */
+void CCAPI_CallListener_onCallEvent(ccapi_call_event_e event, cc_call_handle_t handle, cc_callinfo_ref_t info);
+
+#endif /* _CCAPIAPI_CALL_LISTENER_H_ */
diff --git a/libs/sipcc/include/ccapi_calllog.h b/libs/sipcc/include/ccapi_calllog.h
new file mode 100644 (file)
index 0000000..9a1ba36
--- /dev/null
@@ -0,0 +1,87 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_stdio.h"
+#include "ccapi_call.h"
+#include "sessionHash.h"
+#include "CCProvider.h"
+#include "phone_debug.h"
+
+
+/**
+ * get the placed missed rcvd disposition for the call
+ * @param [in] handle - reference to call log
+ * @return cc_log_disposition_t - log disposition
+ */
+cc_log_disposition_t CCAPI_CallLog_getCallDisposition(cc_calllog_ref_t handle);
+
+/**
+ * get call start Time as seconds since epoch
+ * @param [in] handle - reference to call log
+ * @return  cc_uint32_t - start time as number of seconds since epoch
+ */
+cc_uint32_t CCAPI_CallLog_getStartTime(cc_calllog_ref_t handle);
+
+/**
+ * get call duration in seconds
+ * @param [in] handle - reference to call log
+ * @return cc_uint32_t - call duration in seconds
+ */
+cc_uint32_t CCAPI_CallLog_getCallDuration(cc_calllog_ref_t handle);
+
+/**
+ * get first leg remote party name
+ * @param [in] handle - reference to call log
+ * @return cc_string_t - remote party name for first leg
+ */
+cc_string_t CCAPI_CallLog_getFirstLegRemotePartyName(cc_calllog_ref_t handle);
+
+/**
+ * get last leg remote party name
+ * @param [in] handle - reference to call log
+ * @return cc_string_t - remote party name for last leg
+ */
+cc_string_t CCAPI_CallLog_getLastLegRemotePartyName(cc_calllog_ref_t handle);
+
+/**
+ * get first leg remote party number
+ * @param [in] handle - reference to call log
+ * @return cc_string_t - remote party number for first leg
+ */
+cc_string_t CCAPI_CallLog_getFirstLegRemotePartyNumber(cc_calllog_ref_t handle);
+
+/**
+ * get last leg remote party number
+ * @param [in] handle - reference to call log
+ * @return cc_string_t - remote party number for last leg
+ */
+cc_string_t CCAPI_CallLog_getLastLegRemotePartyNumber(cc_calllog_ref_t handle);
+
+/**
+ * get first leg local party name
+ * @param [in] handle - reference to call log
+ * @return cc_string_t - local party name for first leg
+ */
+cc_string_t CCAPI_CallLog_getLocalPartyName(cc_calllog_ref_t handle);
+
+/**
+ * get first leg local party number
+ * @param [in] handle - reference to call log
+ * @return cc_string_t - local party number for first leg
+ */
+cc_string_t CCAPI_CallLog_getLocalPartyNumber(cc_calllog_ref_t handle);
+
+/**
+ * get first leg alt party number
+ * @param [in] handle - reference to call log
+ * @return cc_string_t - alt party number for first leg
+ */
+cc_string_t CCAPI_CallLog_getFirstLegAltPartyNumber(cc_calllog_ref_t handle);
+
+/**
+ * get last leg alt party number
+ * @param [in] handle - reference to call log
+ * @return cc_string_t - alt party number for last leg
+ */
+cc_string_t CCAPI_CallLog_getLastLegAltPartyNumber(cc_calllog_ref_t handle);
diff --git a/libs/sipcc/include/ccapi_conf_roster.h b/libs/sipcc/include/ccapi_conf_roster.h
new file mode 100644 (file)
index 0000000..b3b7ccc
--- /dev/null
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_CONF_ROSTER_H_
+#define _CCAPI_CONF_ROSTER_H_
+
+#include "cpr_stdio.h"
+#include "ccapi_call.h"
+#include "CCProvider.h"
+#include "phone_debug.h"
+
+// Basic type definitions for conferencing
+typedef string_t cc_participant_ref_t;
+
+/**
+* Get Conference Participants
+*
+* Returns list of conference participant information last received from the UCM.
+* Note that the list may include conference participants in various states in addition to Connected.
+* [ For exampke, a listed participant may be in the ringing state (not yet on conference),
+* or disconnected state (leaving/left the conference).]  Application should invoke
+* getConferenceParticipantStatus for each participant in the list to query exact status.
+*
+* @param [in] handle - call handle
+* @param [in/out] participantHandles - array of participant handles to be returned
+* @param [in/out] count - in:  size of array provided in participantHandles; out:  number of entries populated (up to original value provided)
+* @return void
+*/
+void CCAPI_CallInfo_getConfParticipants (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandles[], int* count);
+
+
+/**
+* Get Maximum Number of Conference Participants ( in case gui wants to show %full conference info )
+* @param [in] handle - call handle
+* @return maximum number of conference participants
+*/
+cc_uint16_t CCAPI_CallInfo_getConfParticipantMax (cc_callinfo_ref_t handle);
+
+
+/**
+* Get Participant Name
+* @param [in] handle - call info handle
+* @param [in] participantHandle - specific handle for conference participant
+* @return display name of the conference participant
+*/
+cc_string_t CCAPI_CallInfo_getConfParticipantName (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle);
+
+/**
+* Get Participant Number
+* @param [in] handle - handle of call
+* @param [in] participantHandle - handle of conference participant
+* @return display number of the conference participant
+*/
+cc_string_t CCAPI_CallInfo_getConfParticipantNumber (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle);
+
+/**
+* Get Conference Participant Status
+* @param [in] handle - call handle
+* @param [in] participantHandle - handle of conference participant
+* @return conference status of specific participant
+*/
+cc_conf_participant_status_t CCAPI_CallInfo_getConfParticipantStatus (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandleHandle);
+
+/**
+* Get Participant Security
+* @param [in] handle - call handle
+* @param [in] participantHandle - handle of conference participant
+* @return security setting of the specific conference participant
+*/
+cc_call_security_t CCAPI_CallInfo_getConfParticipantSecurity (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandleHandle);
+
+/**
+* Able To Remove Others?
+* Find out whether this participant is capable of removing another participant
+* @param [in] handle - call handle
+* @return return code (0=indicates not capable, 1=indicates capable)
+*/
+cc_boolean CCAPI_CallInfo_selfHasRemoveConfParticipantCapability (cc_callinfo_ref_t handle);
+
+/**
+* Check to see if a given conference participant reference is the one using this endpoint
+* @param [in] handle - call handle
+* @param [in] participantHandle - handle of a conference participant
+* @returns cc_boolean (TRUE if yes, FALSE if no)
+*/
+cc_boolean CCAPI_CallInfo_isConfSelfParticipant (cc_callinfo_ref_t handle, cc_participant_ref_t participantHandle);
+
+/**
+* Return our conference participant-id
+* @param [in] handle - call handle
+* @return participant id of self
+*/
+cc_participant_ref_t CCAPI_CallInfo_getConfSelfParticipant (cc_callinfo_ref_t handle);
+
+
+#endif /* _CCAPI_CONF_ROSTER_H_ */
diff --git a/libs/sipcc/include/ccapi_device.h b/libs/sipcc/include/ccapi_device.h
new file mode 100644 (file)
index 0000000..f27b1e2
--- /dev/null
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_DEVICE_H_
+#define _CCAPI_DEVICE_H_
+
+#include "ccapi_types.h"
+
+
+/**
+ * Get device ID
+ * @return cc_deviceinfo_ref_t - reference handle of the device
+ */
+cc_device_handle_t CCAPI_Device_getDeviceID();
+
+/**
+ * Get device reference handle
+ * @param [in] id - device ID
+ * @return cc_deviceinfo_ref_t - reference handle of the device
+ * NOTE: The info returned by this method must be released using CCAPI_Device_releaseDeviceInfo()
+ */
+cc_deviceinfo_ref_t CCAPI_Device_getDeviceInfo(cc_device_handle_t id);
+
+/**
+ * Set full path of device configuration file. API will download and parse the config file using
+ * provided location.
+ * @param handle - device handle
+ * @param [in] file_path - device config file full path
+ * @return void
+ */
+void CCAPI_Device_configUpdate(cc_device_handle_t handle, file_path_t file_path);
+
+/**
+ * Retain the deviceInfo snapshot - this info shall not be freed until a
+ * CCAPI_Device_releaseDeviceInfo() API releases this resource.
+ * @param [in] ref - refrence to the block to be freed
+ * @return void
+ * NOTE: Application may use this API to retain the device info using this API inside
+ * CCAPI_DeviceListener_onDeviceEvent() App must release the Info using
+ * CCAPI_Device_releaseDeviceInfo() once it is done with it.
+ */
+void CCAPI_Device_retainDeviceInfo(cc_deviceinfo_ref_t ref);
+
+/**
+ * Release the deviceInfo snapshot
+ * @param [in] ref - reference to the block to be freed
+ * @return void
+ */
+void CCAPI_Device_releaseDeviceInfo(cc_deviceinfo_ref_t ref);
+
+/**
+ * Create a call on the device
+ * Line selection is on the first available line. Lines that have there MNC reached will be skipped.
+ * @param handle - device handle
+ * @return cc_call_handle_t - handle of the call created
+ */
+cc_call_handle_t CCAPI_Device_CreateCall(cc_device_handle_t handle);
+
+/**
+ * Enable or disable video capability of the device.
+ * @param handle - device handle
+ * @param [in] enable - a flag to indicate that application wants to enable of
+ * disable video capability of the device.
+ * @return void
+ */
+void CCAPI_Device_enableVideo(cc_device_handle_t handle, cc_boolean enable);
+
+/**
+ * Enable or disable camera capability of the device.
+ * @param handle - device handle
+ * @param [in] enable - a flag to indicate that application wants to enable of
+ * disable camera capability of the device.
+ * @return void
+ */
+void CCAPI_Device_enableCamera(cc_device_handle_t handle, cc_boolean enable);
+
+
+/**
+ * CCAPI_Device_setDigestNamePasswd
+ *
+ * @param handle - device handle
+ * @param name - The Digest auth name
+ * @param passwd - The password for that name for the line
+ * @return void
+ */
+void CCAPI_Device_setDigestNamePasswd (cc_device_handle_t handle,
+                                       char *name, char *pw);
+
+
+/**
+ * CCAPI_Device_IP_Update
+ *
+ * There is a update in the IP address and the values of new set
+ * of signaling and media IP addresses are provided.
+ * These value are compared with the current IP address values
+ * and depending on what changed, restart and/or re-invite
+ * action is taken.
+ *
+ * The case being addressed.
+ * 1) If the signaling IP change  happens during a call,
+ *    the change is deferred till phone is idle.
+ * 2)If media IP change happens during a call, it is applied immediately.
+ * 3) If both change, and call is active, that is treated same
+ *    combination of case 1) and 2).
+ * 4) If no call is present, and signaling IP change,
+ *    sipcc will re-register with new IP.
+ *
+ * @param handle - device handle
+ * @param signaling_ip - IP address that Must be used for signalling
+ * @param signaling_interface - Interface name associated with signaling IP
+ * @param signaling_int_type - Interface Type associated with signaling IP
+ * @param media_ip - IP address that Must be used for media
+ * @param media_interface - Interface name associated with Media IP
+ * @param media_int_type - Interface Type associated with Media IP
+ * @return void
+ */
+void CCAPI_Device_IP_Update (cc_device_handle_t handle,
+                              const char *signaling_ip,
+                              const char *sig_interface,
+                              int sig_int_type,
+                              const char *media_ip,
+                              const char *media_interface,
+                              int media_int_type);
+
+
+/**
+ * CCAPI_Device_setVideoAutoTxPreference
+ *
+ * @param handle - device handle
+ * @param txPref - TRUE=> auto Tx Video prefered
+ * @return void
+ */
+void CCAPI_Device_setVideoAutoTxPreference (cc_device_handle_t handle, cc_boolean txPref);
+
+#endif /* _CCAPIAPI_DEVICE_H_ */
diff --git a/libs/sipcc/include/ccapi_device_info.h b/libs/sipcc/include/ccapi_device_info.h
new file mode 100644 (file)
index 0000000..0aaea9d
--- /dev/null
@@ -0,0 +1,203 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_DEVICE_INFO_H_
+#define _CCAPI_DEVICE_INFO_H_
+
+#include "ccapi_types.h"
+
+/**
+ * gets the device name
+ * @returns - a pointer to the device name
+ */
+cc_deviceinfo_ref_t CCAPI_DeviceInfo_getDeviceHandle() ;
+
+/**
+ * gets the device name
+ * @param [in] handle - reference to device info
+ * @returns - a pointer to the device name
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_DeviceInfo_getDeviceName(cc_deviceinfo_ref_t handle) ;
+
+/**
+ * gets the device idle status
+ * @param [in] handle - reference to device info
+ * @returns boolean - idle status
+ */
+cc_boolean CCAPI_DeviceInfo_isPhoneIdle(cc_deviceinfo_ref_t handle) ;
+
+/**
+ * gets the service state
+ * @param [in] handle - reference to device info
+ * @returns cc_service_state_t - INS/OOS
+ */
+cc_service_state_t CCAPI_DeviceInfo_getServiceState(cc_deviceinfo_ref_t handle) ;
+
+/**
+ * gets the service cause
+ * @param [in] handle - reference to device info
+ * @returns cc_service_cause_t - reason for service state
+ */
+cc_service_cause_t CCAPI_DeviceInfo_getServiceCause(cc_deviceinfo_ref_t handle) ;
+
+/**
+ * gets the cucm mode
+ * @param [in] handle - reference to device info
+ * @returns cc_cucm_mode_t - CUCM mode
+ */
+cc_cucm_mode_t CCAPI_DeviceInfo_getCUCMMode(cc_deviceinfo_ref_t handle) ;
+
+/**
+ * gets list of handles to calls on the device
+ * @param [in] handle - reference to device info
+ * @param [out] handles - array of call handle to be returned
+ * @param [in,out] count number allocated in array/elements returned
+ * @returns
+ */
+void CCAPI_DeviceInfo_getCalls(cc_deviceinfo_ref_t handle, cc_call_handle_t handles[], cc_uint16_t *count) ;
+
+/**
+ * gets list of handles to calls on the device by state
+ * @param [in] handle - reference to device info
+ * @param [in] state - call state for which the calls are requested
+ * @param [out] handles - array of call handle to be returned
+ * @param [in,out] count number allocated in array/elements returned
+ * @returns
+ */
+void CCAPI_DeviceInfo_getCallsByState(cc_deviceinfo_ref_t handle, cc_call_state_t state,
+                cc_call_handle_t handles[], cc_uint16_t *count) ;
+
+/**
+ * gets list of handles to lines on the device
+ * @param [in] handle - reference to device info
+ * @param [in] handles - array of line handle to be returned
+ * @param [in,out] count - pointer to count in array in-> memory allocated out-> num populated
+ * @returns
+ */
+void CCAPI_DeviceInfo_getLines(cc_deviceinfo_ref_t handle, cc_lineid_t handles[], cc_uint16_t *count) ;
+
+/**
+ * gets list of handles to features on the device
+ * @param [in] handle - reference to device info
+ * @param [in] handles - array of feature handle to be returned
+ * @param [in,out] count - pointer to count in array in-> memory allocated out-> num populated
+ * @returns
+ */
+void CCAPI_DeviceInfo_getFeatures(cc_deviceinfo_ref_t handle, cc_featureinfo_ref_t handles[], cc_uint16_t *count) ;
+
+/**
+ * gets handles of call agent servers
+ * @param [in] handle - reference to device info
+ * @param [in] handles - array of handles to call agent servers
+ * @param [in,out] count - pointer to count in array in-> memory allocated out-> num populated
+ * @returns
+ */
+void CCAPI_DeviceInfo_getCallServers(cc_deviceinfo_ref_t handle, cc_callserver_ref_t handles[], cc_uint16_t *count) ;
+
+/**
+ * gets call server name
+ * @param [in] handle - handle of call server
+ * @returns name of the call server
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_DeviceInfo_getCallServerName(cc_callserver_ref_t handle);
+
+/**
+ * gets call server mode
+ * @param [in] handle - handle of call server
+ * @returns - mode of the call server
+ */
+cc_cucm_mode_t CCAPI_DeviceInfo_getCallServerMode(cc_callserver_ref_t handle);
+
+/**
+ * gets calls erver name
+ * @param [in] handle - handle of call server
+ * @returns status of the call server
+ */
+cc_ccm_status_t CCAPI_DeviceInfo_getCallServerStatus(cc_callserver_ref_t handle);
+
+/**
+ * get the NOTIFICATION PROMPT
+ * @param [in] handle - reference to device info
+ * @returns
+ */
+cc_string_t CCAPI_DeviceInfo_getNotifyPrompt(cc_deviceinfo_ref_t handle) ;
+
+/**
+ * get the NOTIFICATION PROMPT PRIORITY
+ * @param [in] handle - reference to device info
+ * @returns
+ */
+cc_uint32_t CCAPI_DeviceInfo_getNotifyPromptPriority(cc_deviceinfo_ref_t handle) ;
+
+/**
+ * get the NOTIFICATION PROMPT Progress
+ * @param [in] handle - reference to device info
+ * @returns
+ */
+cc_uint32_t CCAPI_DeviceInfo_getNotifyPromptProgress(cc_deviceinfo_ref_t handle) ;
+
+/**
+ * gets provisioing for missed call logging
+ * This value should be reread on CONFIG_UPDATE event
+ * @param [in] handle - reference to device info
+ * @returns boolean - false => disabled true => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isMissedCallLoggingEnabled (cc_deviceinfo_ref_t handle);
+
+/**
+ * gets provisioing for placed call logging
+ * This value should be reread on CONFIG_UPDATE event
+ * @param [in] handle - reference to device info
+ * @returns boolean - false => disabled true => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isPlacedCallLoggingEnabled (cc_deviceinfo_ref_t handle);
+
+/**
+ * gets provisioing for received call logging
+ * This value should be reread on CONFIG_UPDATE event
+ * @param [in] handle - reference to device info
+ * @returns boolean - false => disabled true => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isReceivedCallLoggingEnabled (cc_deviceinfo_ref_t handle);
+
+/**
+ * gets register/in service time
+ * This value should be read once the register completes successfully
+ * @param [in] handle - reference to device info
+ * @returns long - time registration completed successfully.
+ */
+long long CCAPI_DeviceInfo_getRegTime (cc_deviceinfo_ref_t handle);
+
+/**
+ * Returns dot notation IP address used for registering phone. If phone is not
+ * registered, then "0.0.0.0" is returned.
+ * @param [in] handle - reference to device info
+ * @return  cc_string_t  IP address used to register phone.
+ */
+cc_string_t CCAPI_DeviceInfo_getSignalingIPAddress(cc_deviceinfo_ref_t handle);
+
+/**
+ * Returns camera admin enable/disable status
+ * @param [in] handle - reference to device info
+ * @return  cc_boolean  - TRUE => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isCameraEnabled(cc_deviceinfo_ref_t handle);
+
+/**
+ * Returns admin enable/disable video capablity for device
+ * @param [in] handle - reference to device info
+ * @return  cc_boolean  - TRUE => enabled
+ */
+cc_boolean CCAPI_DeviceInfo_isVideoCapEnabled(cc_deviceinfo_ref_t handle);
+
+/**
+ * gets the device mwi_lamp state
+ * @param [in] handle - reference to device info
+ * @returns boolean - mwi_lamp state
+ */
+cc_boolean CCAPI_DeviceInfo_getMWILampState(cc_deviceinfo_ref_t handle);
+
+#endif /* _CCAPIAPI_DEVICE_INFO_H_ */
diff --git a/libs/sipcc/include/ccapi_device_listener.h b/libs/sipcc/include/ccapi_device_listener.h
new file mode 100644 (file)
index 0000000..ed3a5a6
--- /dev/null
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_DEVICE_LISTENER_H_
+#define _CCAPI_DEVICE_LISTENER_H_
+
+#include "cc_constants.h"
+
+/**
+ * Generates Device event
+ * @param [in] type - event type for device
+ * @param [in] device_id - device id
+ * @param [in] dev_info - reference to device info
+ * @returns
+ * NOTE: The memory associated with deviceInfo will be freed immediately upon
+ * return from this method. If the application wishes to retain a copy it should
+ * invoke CCAPI_Device_retainDeviceInfo() API. once the info is retained it can be
+ * released by invoking CCAPI_Device_releaseDeviceInfo() API
+ */
+void CCAPI_DeviceListener_onDeviceEvent(ccapi_device_event_e type, cc_device_handle_t device_id, cc_deviceinfo_ref_t dev_info);
+
+/**
+ * Generates Feature event
+ * @param [in] type - event type for device
+ * @param [in] device_id - device id
+ * @param [in] feature_info - reference to feature info
+ * @returns
+ * NOTE: The memory associated with featureInfo will be freed immediately upon
+ * return from this method. If the application wishes to retain a copy it should
+ * invoke CCAPI_Device_retainFeatureInfo() API. once the info is retained it can be
+ * released by invoking CCAPI_Device_releaseFeatureInfo() API
+ */
+void CCAPI_DeviceListener_onFeatureEvent(ccapi_device_event_e type, cc_deviceinfo_ref_t device_info, cc_featureinfo_ref_t feature_info);
+
+#endif /* _CCAPIAPI_DEVICE_LISTENER_H_ */
diff --git a/libs/sipcc/include/ccapi_feature_info.h b/libs/sipcc/include/ccapi_feature_info.h
new file mode 100644 (file)
index 0000000..ea28472
--- /dev/null
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPIAPI_FEATURE_INFO_H_
+#define _CCAPIAPI_FEATURE_INFO_H_
+
+#include "ccapi_types.h"
+
+
+/**
+ * Get the physical button number on which this feature is configured
+ * @param feature - feature reference handle
+ * @return cc_int32_t - button assgn to the feature
+ */
+cc_int32_t CCAPI_featureInfo_getButton(cc_featureinfo_ref_t feature);
+
+/**
+ * Get the featureID
+ * @param feature - feature reference handle
+ * @return cc_int32_t - button assgn to the feature
+ */
+cc_int32_t CCAPI_featureInfo_getFeatureID(cc_featureinfo_ref_t feature);
+/**
+ * Get the feature Name
+ * @param feature - feature reference handle
+ * @return cc_string_t - handle of the feature created
+ */
+cc_string_t CCAPI_featureInfo_getDisplayName(cc_featureinfo_ref_t feature);
+
+/**
+ * Get the speeddial Number
+ * @param feature - feature reference handle
+ * @return cc_string_t - handle of the feature created
+ */
+cc_string_t CCAPI_featureInfo_getSpeedDialNumber(cc_featureinfo_ref_t feature);
+
+/**
+ * Get the contact
+ * @param feature - feature reference handle
+ * @return cc_string_t - handle of the feature created
+ */
+cc_string_t CCAPI_featureInfo_getContact(cc_featureinfo_ref_t feature);
+
+/**
+ * Get the retrieval prefix
+ * @param feature - feature reference handle
+ * @return cc_string_t - handle of the feature created
+ */
+cc_string_t CCAPI_featureInfo_getRetrievalPrefix(cc_featureinfo_ref_t feature);
+
+/**
+ * Get BLF state
+ * @param feature - feature reference handle
+ * @return cc_string_t - handle of the feature created
+ */
+cc_blf_state_t CCAPI_featureInfo_getBLFState(cc_featureinfo_ref_t feature);
+
+/**
+ * Get the feature option mask
+ * @param feature - feature reference handle
+ * @return cc_int32_t - button assgn to the feature
+ */
+cc_int32_t CCAPI_featureInfo_getFeatureOptionMask(cc_featureinfo_ref_t feature);
+
+
+#endif /* _CCAPIAPI_FEATURE_INFO_H_ */
diff --git a/libs/sipcc/include/ccapi_line.h b/libs/sipcc/include/ccapi_line.h
new file mode 100644 (file)
index 0000000..45577ac
--- /dev/null
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_LINE_H_
+#define _CCAPI_LINE_H_
+
+#include "ccapi_types.h"
+
+/**
+ * Get reference handle for the line
+ * @param [in] line - lineID on which to create call
+ * @return cc_call_handle_t - handle of the call created
+ * NOTE: The info returned by this method must be released using CCAPI_Line_releaseLineInfo()
+ */
+cc_lineinfo_ref_t CCAPI_Line_getLineInfo(cc_uint32_t line);
+
+/**
+ * Create a call on the line
+ * @param [in] line - lineID on which to create call
+ * @return cc_call_handle_t - handle of the call created
+ */
+cc_call_handle_t CCAPI_Line_CreateCall(cc_lineid_t line);
+
+/**
+ * Retain the lineInfo snapshot - this info shall not be freed until a
+ * CCAPI_Line_releaseLineInfo() API releases this resource.
+ * @param [in] ref - reference to the block to be retained
+ * @return void
+ * NOTE: Application may use this API to retain the device info using this API inside
+ * CCAPI_LineListener_onLineEvent() App must release the Info using
+ * CCAPI_Line_releaseLineInfo() once it is done with it.
+ */
+void CCAPI_Line_retainLineInfo(cc_lineinfo_ref_t ref);
+
+/**
+ * Release the lineInfo snapshot that was retained using the CCAPI_Line_retainLineInfo API
+ * @param [in] ref - line info reference to be freed
+ * @return void
+ */
+void CCAPI_Line_releaseLineInfo(cc_lineinfo_ref_t ref);
+
+#endif /* _CCAPIAPI_LINE_H_ */
diff --git a/libs/sipcc/include/ccapi_line_info.h b/libs/sipcc/include/ccapi_line_info.h
new file mode 100644 (file)
index 0000000..77e77fd
--- /dev/null
@@ -0,0 +1,165 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_LINE_INFO_H_
+#define _CCAPI_LINE_INFO_H_
+
+#include "ccapi_types.h"
+
+/**
+ * Get the line ID
+ * @param [in] line - line reference handle
+ * @return cc_call_handle_t - handle of the call created
+ */
+cc_uint32_t CCAPI_lineInfo_getID(cc_lineinfo_ref_t line);
+
+/**
+ * Get the line Name
+ * @param [in] line - line reference handle
+ * @return cc_string_t - line Name
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_lineInfo_getName(cc_lineinfo_ref_t line);
+
+/**
+ * Get the line Label
+ * @param [in] line - line reference handle
+ * @return cc_string_t - line Label
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_lineInfo_getLabel(cc_lineinfo_ref_t line);
+
+/**
+ * Get the line DN Number
+ * @param [in] line - line reference handle
+ * @return cc_string_t - line DN
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_lineInfo_getNumber(cc_lineinfo_ref_t line);
+
+/**
+ * Get the line External Number
+ * @param [in] line - line reference handle
+ * @return cc_string_t - line DN
+ * NOTE: The memory for return string doesn't need to be freed it will be freedwhen the info reference is freed
+ */
+cc_string_t CCAPI_lineInfo_getExternalNumber(cc_lineinfo_ref_t line);
+
+/**
+ * Get the physical button number on which this line is configured
+ * @param [in] line - line reference handle
+ * @return cc_uint32_t - button number
+ * NOTE: This API is deprecated please don't use this. the CCAPI_lineInfo_getID() returns the button number
+ * as we use the button as line id
+ */
+cc_uint32_t CCAPI_lineInfo_getButton(cc_lineinfo_ref_t line);
+
+/**
+ * Get the Line Type
+ * @param [in] line - line reference handle
+ * @return cc_uint32_t - line featureID ( Line )
+ */
+cc_line_feature_t CCAPI_lineInfo_getLineType(cc_lineinfo_ref_t line);
+
+/**
+ * Get the physical button number on which this line is configured
+ * @param [in] line - line reference handle
+ * @return cc_uint32_t - button number
+ */
+cc_boolean CCAPI_lineInfo_getRegState(cc_lineinfo_ref_t line);
+
+/**
+ * Get the CFWDAll status for the line
+ * @param [in] line - line reference handle
+ * @return cc_boolean - isForwarded
+ */
+cc_boolean CCAPI_lineInfo_isCFWDActive(cc_lineinfo_ref_t line);
+
+/**
+ * Get the CFWDAll destination
+ * @param [in] line - line reference handle
+ * @return cc_string_t - cfwd target
+ * NOTE: The memory for return string doesn't need to be freed it will be freed when the info reference is freed
+ */
+cc_string_t CCAPI_lineInfo_getCFWDName(cc_lineinfo_ref_t line);
+
+/**
+ * Get calls on line
+ * @param [in] line - lineID
+ * @param [in, out] handles[] - Array of callinfo references
+ * @param [in,out] count - count of call references populated
+ * @return void
+ */
+void CCAPI_LineInfo_getCalls(cc_lineid_t line, cc_call_handle_t handles[], int *count);
+
+/**
+ * Get calls on line by state
+ * @param [in] line - lineID
+ * @param [in] state - state
+ * @param [in, out] handles[] - Array of callinfo references
+ * @param [in,out] count - count of call references populated
+ * @return void
+ */
+void CCAPI_LineInfo_getCallsByState(cc_lineid_t line, cc_call_state_t state,
+                cc_call_handle_t handles[], int *count);
+
+/**
+ * Get the MWI Status
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI status (boolean 0 => no MWI)
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIStatus(cc_lineinfo_ref_t line);
+
+/**
+ * Get the MWI Type
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI Type
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIType(cc_lineinfo_ref_t line);
+
+/**
+ * Get the MWI new msg count
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI new msg count
+ */
+cc_uint32_t CCAPI_lineInfo_getMWINewMsgCount(cc_lineinfo_ref_t line);
+
+/**
+ * Get the MWI old msg count
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI old msg count
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIOldMsgCount(cc_lineinfo_ref_t line);
+
+/**
+ * Get the MWI high priority new msg count
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI new msg count
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIPrioNewMsgCount(cc_lineinfo_ref_t line);
+
+/**
+ * Get the MWI high priority old msg count
+ * @param line - line reference handle
+ * @return cc_uint32_t - MWI old msg count
+ */
+cc_uint32_t CCAPI_lineInfo_getMWIPrioOldMsgCount(cc_lineinfo_ref_t line);
+
+/**
+ * has capability - is the feature allowed
+ * @param [in] handle - call info handle
+ * @param [in] feat_id - feature id
+ * @return boolean - is Allowed
+ */
+cc_boolean  CCAPI_LineInfo_hasCapability(cc_lineinfo_ref_t line, cc_int32_t feat_id);
+
+/**
+ * get Allowed Feature set
+ * @param [in] handle - call info handle
+ * @param [in,out] feat_set - array of len CC_CALL_CAP_MAX
+ * @return cc_return_t - CC_SUCCESS or CC_FAILURE
+ */
+cc_return_t  CCAPI_LineInfo_getCapabilitySet(cc_lineinfo_ref_t line, cc_int32_t feat_set[]);
+
+#endif /* _CCAPIAPI_LINE_INFO_H_ */
diff --git a/libs/sipcc/include/ccapi_line_listener.h b/libs/sipcc/include/ccapi_line_listener.h
new file mode 100644 (file)
index 0000000..9cf331e
--- /dev/null
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_LINE_LISTENER_H_
+#define _CCAPI_LINE_LISTENER_H_
+
+#include "cc_constants.h"
+
+/**
+ * Line event notification
+ * @param [in] line_event - event
+ * @param [in] line - line id
+ * @param [in] lineInfo - line info reference
+ * @return cc_call_handle_t - handle of the call created
+ * NOTE: The memory associated with deviceInfo will be freed immediately upon
+ * return from this method. If the application wishes to retain a copy it should
+ * invoke CCAPI_Line_retainlineInfo() API. once the info is retained it can be
+ * released by invoking CCAPI_Line_releaseLineInfo() API
+ */
+void CCAPI_LineListener_onLineEvent(ccapi_line_event_e line_event, cc_lineid_t line, cc_lineinfo_ref_t lineInfo);
+
+#endif /* _CCAPIAPI_LINE_LISTENER_H_ */
diff --git a/libs/sipcc/include/ccapi_service.h b/libs/sipcc/include/ccapi_service.h
new file mode 100644 (file)
index 0000000..0655a4d
--- /dev/null
@@ -0,0 +1,85 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CCAPI_SERVICE_H_
+#define _CCAPI_SERVICE_H_
+#include "cc_constants.h"
+
+/**
+ * Globals defined in ccapi_service.c
+ */
+extern int g_dev_hdl;
+#define G_DEV_NAME_SIZE 64
+extern char g_dev_name[G_DEV_NAME_SIZE];
+#define G_CFG_P_SIZE 256
+extern char g_cfg_p[G_CFG_P_SIZE];
+extern int g_compl_cfg;
+
+/**
+ * Defines the management methods.
+ */
+
+/**
+ * The following methods are defined to bring up the pSipcc stack
+ */
+
+/**
+ * This function creates various data module needed for initialization of
+ * Sipcc stack. On reboot or after CCAPI_Service_destroy(), application must call
+ * first this function followed by CC_Service_start()
+ *  to bring Sipcc stack in in-service. This function
+ * need to be called only once.
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Service_create();
+
+/**
+ * Gracefully unload the Sipcc stack. To bring up the pSipcc stack again,
+ * follow the function calling sequence starting from CCAPI_Service_create().
+ * @return SUCCESS
+ */
+cc_return_t CCAPI_Service_destroy();
+
+/**
+ * Bring up the Sipcc stack in service.
+ *
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Service_start();
+
+/**
+ * Stop Sipcc stack
+ *
+ * @return SUCCESS or FAILURE
+ */
+cc_return_t CCAPI_Service_stop();
+
+/**
+ * CCAPI_Service_reregister
+ *
+ * This API will result in stopping the stip stack (i.e unregister) followed
+ * by parsing of the current config followed by a
+ * start (i.e register) of the SIP stack, without the download of a new config
+ * file. This API is used in the APPLY config case
+ *
+ * @param device_handle handle of the device, the response is for
+ * @param device_name
+ * @param cfg the config file name and path  or the complete configuration
+ *         in memory.
+ * @param from_memory boolean flag to indicate if the complete config
+ *         is sent. This parameter is meant to indicate if the "cfg" parameter
+ *          points to the file name or is a pointer to the complete config in memory.
+ * @return
+ */
+cc_return_t CCAPI_Service_reregister (int device_handle, const char *device_name,
+                             const char *cfg, int from_memory);
+
+/**
+ * Reset request from the Reset Manager
+ *
+ * @return void
+ */
+void CCAPI_Service_reset_request();
+
+#endif /* _CCAPI_SERVICE_H_ */
diff --git a/libs/sipcc/include/ccapi_types.h b/libs/sipcc/include/ccapi_types.h
new file mode 100644 (file)
index 0000000..7fbbc60
--- /dev/null
@@ -0,0 +1,160 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *  @mainpage Portable SIP Stack API
+ *
+ *  @section intro_sec Introduction
+ *  The portable SIP stack is used in multiple SIP endpoints. This document
+ *  describes the API's provided by the portable SIP stack that third party
+ *  vendors must implement to use the stack.
+ *
+ *  @section hlapi NG APIs
+ *  This API provides 3 main sets of APIs (device, line. call) and provides ways to
+ *  invoke actions on these and get status and even callbacks
+ *
+ *  @subsection Management
+ *    @li cc_service.h @par
+ *
+ *  @subsection Call
+ *    @li ccapi_call.h @par
+ *    @li ccapi_call_info.h @par
+ *    @li ccapi_call_listener.h @par to be implemented by the vendor for events related to call
+ *
+ *  @subsection Line
+ *    @li ccapi_line.h @par
+ *    @li ccapi_line_info.h @par
+ *    @li ccapi_line_listener.h @par to be implemented by the vendor for events related to line
+ *
+ *  @subsection Device
+ *    @li ccapi_device.h @par
+ *    @li ccapi_device_info.h @par
+ *    @li ccapi_device_listener.h @par to be implemented by the vendor for events related to device
+ *
+ *  @subsection Misc
+ *    @li ccapi_types.h @par
+ *    @li cc_types.h @par
+ *    @li cc_constants.h @par
+ *
+ */
+
+#ifndef _CCAPI_TYPES_H_
+#define _CCAPI_TYPES_H_
+
+#include "cc_constants.h"
+
+/**
+ *  Device ID typedef
+ */
+typedef unsigned int cc_device_handle_t;
+/**
+ * File location
+ */
+typedef const char *file_path_t;
+/**
+ *  Device info reference typedef
+ */
+typedef struct cc_device_info_t_* cc_deviceinfo_ref_t;
+/**
+ *  Line info reference typedef
+ */
+typedef struct cc_line_info_t_ * cc_lineinfo_ref_t;
+/**
+ *  Feature info reference typedef
+ */
+typedef struct cc_feature_info_t_ * cc_featureinfo_ref_t;
+/**
+ *  Call info reference typedef
+ */
+typedef struct cc_call_info_t_* cc_callinfo_ref_t;
+/**
+ *  CAll server info reference typedef
+ */
+typedef struct cc_call_server_t_* cc_callserver_ref_t;
+
+/**
+ *  CallLog reference typedef
+ */
+typedef struct cc_call_log_t_* cc_calllog_ref_t;
+
+#define CCAPI_MAX_SERVERS 4
+/**
+ * Call event types
+ */
+typedef enum {
+  CCAPI_CALL_EV_CREATED,
+  CCAPI_CALL_EV_STATE,
+  CCAPI_CALL_EV_CALLINFO,
+  CCAPI_CALL_EV_ATTR,
+  CCAPI_CALL_EV_SECURITY,
+  CCAPI_CALL_EV_LOG_DISP,
+  CCAPI_CALL_EV_PLACED_CALLINFO,
+  CCAPI_CALL_EV_STATUS,
+  CCAPI_CALL_EV_SELECT,
+  CCAPI_CALL_EV_LAST_DIGIT_DELETED,
+  CCAPI_CALL_EV_GCID,
+  CCAPI_CALL_EV_XFR_OR_CNF_CANCELLED,
+  CCAPI_CALL_EV_PRESERVATION,
+  CCAPI_CALL_EV_CAPABILITY,
+  CCAPI_CALL_EV_VIDEO_AVAIL,
+  CCAPI_CALL_EV_VIDEO_OFFERED,
+  CCAPI_CALL_EV_RECEIVED_INFO,
+  CCAPI_CALL_EV_RINGER_STATE,
+  CCAPI_CALL_EV_CONF_PARTICIPANT_INFO,
+  CCAPI_CALL_EV_MEDIA_INTERFACE_UPDATE_BEGIN,
+  CCAPI_CALL_EV_MEDIA_INTERFACE_UPDATE_SUCCESSFUL,
+  CCAPI_CALL_EV_MEDIA_INTERFACE_UPDATE_FAIL
+} ccapi_call_event_e;
+
+/**
+ * Line event types
+ */
+typedef enum {
+  CCAPI_LINE_EV_CONFIG_CHANGED,
+  CCAPI_LINE_EV_REG_STATE,
+  CCAPI_LINE_EV_CAPSET_CHANGED,
+  CCAPI_LINE_EV_CFWDALL,
+  CCAPI_LINE_EV_MWI
+} ccapi_line_event_e;
+
+/**
+ * Device event types
+ */
+typedef enum {
+  CCAPI_DEVICE_EV_CONFIG_CHANGED,
+  CCAPI_DEVICE_EV_STATE, // INS/OOS
+  CCAPI_DEVICE_EV_IDLE_SET,
+  CCAPI_DEVICE_EV_MWI_LAMP,
+  CCAPI_DEVICE_EV_NOTIFYPROMPT,
+  CCAPI_DEVICE_EV_SERVER_STATUS,
+  CCAPI_DEVICE_EV_BLF,
+  CCAPI_DEVICE_EV_CAMERA_ADMIN_CONFIG_CHANGED,
+  CCAPI_DEVICE_EV_VIDEO_CAP_ADMIN_CONFIG_CHANGED
+} ccapi_device_event_e;
+
+/**
+ * Call capability feature IDs
+ */
+typedef enum {
+  CCAPI_CALL_CAP_NEWCALL,
+  CCAPI_CALL_CAP_ANSWER,
+  CCAPI_CALL_CAP_ENDCALL,
+  CCAPI_CALL_CAP_HOLD,
+  CCAPI_CALL_CAP_RESUME,
+  CCAPI_CALL_CAP_CALLFWD,
+  CCAPI_CALL_CAP_DIAL,
+  CCAPI_CALL_CAP_BACKSPACE,
+  CCAPI_CALL_CAP_SENDDIGIT,
+  CCAPI_CALL_CAP_TRANSFER,
+  CCAPI_CALL_CAP_CONFERENCE,
+  CCAPI_CALL_CAP_SWAP,
+  CCAPI_CALL_CAP_BARGE,
+  CCAPI_CALL_CAP_REDIAL,
+  CCAPI_CALL_CAP_JOIN,
+  CCAPI_CALL_CAP_SELECT,
+  CCAPI_CALL_CAP_RMVLASTPARTICIPANT,
+  CCAPI_CALL_CAP_MAX
+} ccapi_call_capability_e;
+
+#endif /* _CCAPIAPI_TYPES_H_ */
diff --git a/libs/sipcc/include/ccsdp.h b/libs/sipcc/include/ccsdp.h
new file mode 100644 (file)
index 0000000..7d82ab8
--- /dev/null
@@ -0,0 +1,659 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *  Skip this file if not doing video on the device.
+ *
+ *  The ccsdp_xxx api's provide a means to query and populate the SDP attributes
+ *  for video m lines. These Api are not needed if we are not supporting video on
+ *  the platform. For audio the stack will populate the appropriate attributes
+ *
+ *
+ *  These API's can be invoked from the vcmCheckAttrs() and vcmPopulateAttrs()
+ *  methods to populate or extract the value of specific attributes in the video SDP.
+ *  These api require an handle to the SDP that is passed in the above methods.
+ * <pre>
+ * sdp_handle     The SDP handle
+ * level       The level the attribute is defined.  Can be either
+ *             SDP_SESSION_LEVEL or 0-n specifying a media line level.
+ * inst_num    The instance number of the attribute.  Multiple instances
+ *             of a particular attribute may exist at each level and so
+ *             the inst_num determines the particular attribute at that
+ *             level that should be accessed.  Note that this is the
+ *             instance number of the specified type of attribute, not the
+ *             overall attribute number at the level.  Also note that the
+ *             instance number is 1-based.  For example:
+ *             v=0
+ *             o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
+ *             s=SDP Seminar
+ *             c=IN IP4 10.1.0.2
+ *             t=0 0
+ *             m=audio 1234 RTP/AVP 0 101 102
+ *             a=foo 1
+ *             a=foo 2
+ *             a=bar 1   # This is instance 1 of attribute bar.
+ *             a=foo 3   # This is instance 3 of attribute foo.
+ * cap_num     Almost all of the attributes may be defined as X-cpar
+ *             parameters (with the exception of X-sqn, X-cap, and X-cpar).
+ *             If the cap_num is set to zero, then the attribute is not
+ *             an X-cpar parameter attribute.  If the cap_num is any other
+ *             value, it specifies the capability number that the X-cpar
+ *             attribute is specified for.
+ * </pre>
+ */
+
+#ifndef __CCSDP_H__
+#define __CCSDP_H__
+
+#include "cpr_types.h"
+
+#define SIPSDP_ILBC_MODE20 20
+
+/**
+ * Return codes for sdp helper APIs
+ */
+typedef enum rtp_ptype_
+{
+    RTP_NONE         = -1,
+    RTP_PCMU         = 0,
+    RTP_CELP         = 1,
+    RTP_G726         = 2,
+    RTP_GSM          = 3,
+    RTP_G723         = 4,
+    RTP_DVI4         = 5,
+    RTP_DVI4_II      = 6,
+    RTP_LPC          = 7,
+    RTP_PCMA         = 8,
+    RTP_G722         = 9,
+    RTP_G728         = 15,
+    RTP_G729         = 18,
+    RTP_JPEG         = 26,
+    RTP_NV           = 28,
+    RTP_H261         = 31,
+    RTP_H264_P0      = 97,
+    RTP_H264_P1      = 126,
+    RTP_AVT          = 101,
+    RTP_L16          = 102,
+    RTP_H263         = 103,
+    RTP_ILBC         = 116, /* used only to make an offer */
+    RTP_OPUS         = 109,
+    RTP_VP8          = 120,
+    RTP_I420         = 124,
+    RTP_ISAC         = 124
+} rtp_ptype;
+
+/**
+ * IANA-registered static payload types for the RTP/AVP profile.
+ * See http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
+ */
+typedef enum static_rtp_ptype_
+{
+    STATIC_RTP_AVP_PCMU_8000_1          = 0,
+    STATIC_RTP_AVP_GSM_8000_1           = 3,
+    STATIC_RTP_AVP_G723_8000_1          = 4,
+    STATIC_RTP_AVP_DVI4_8000_1          = 5,
+    STATIC_RTP_AVP_DVI4_16000_1         = 6,
+    STATIC_RTP_AVP_LPC_8000_1           = 7,
+    STATIC_RTP_AVP_PCMA_8000_1          = 8,
+    STATIC_RTP_AVP_G722_8000_1          = 9,
+    STATIC_RTP_AVP_L16_44100_2          = 10,
+    STATIC_RTP_AVP_L16_44100_1          = 11,
+    STATIC_RTP_AVP_QCELP_8000_1         = 12,
+    STATIC_RTP_AVP_CN_8000_1            = 13,
+    STATIC_RTP_AVP_MPA_90000_1          = 14,
+    STATIC_RTP_AVP_G728_8000_1          = 15,
+    STATIC_RTP_AVP_DVI4_11025_1         = 16,
+    STATIC_RTP_AVP_DVI4_22050_1         = 17,
+    STATIC_RTP_AVP_G729_8000_1          = 18,
+    STATIC_RTP_AVP_CELB_90000_1         = 25,
+    STATIC_RTP_AVP_JPEG_90000_1         = 26,
+    STATIC_RTP_AVP_NV_90000_1           = 28,
+    STATIC_RTP_AVP_H261_90000_1         = 31,
+    STATIC_RTP_AVP_MPV_90000_1          = 32,
+    STATIC_RTP_AVP_MP2T_90000_1         = 33,
+    STATIC_RTP_AVP_H263_90000_1         = 34
+} static_rtp_ptype;
+
+typedef struct {
+    const char *name;
+    int         value;
+} ccsdp_key_table_entry_t;
+
+typedef enum max_coded_audio_bandwidth_ {
+    opus_nb  = 0,    /* Narrowband */
+    opus_mb  = 1,    /* Mediumband */
+    opus_wb  = 2,    /* Wideband */
+    opus_swb = 3,    /* Super-wideband */
+    opus_fb  = 4   /* Fullband */
+} max_coded_audio_bandwidth;
+
+static const ccsdp_key_table_entry_t max_coded_audio_bandwidth_table[] = {
+    {"nb",         opus_nb},
+    {"mb",         opus_mb},
+    {"wb",         opus_wb},
+    {"swb",        opus_swb},
+    {"fb",         opus_fb}
+};
+
+typedef enum {
+    SDP_SUCCESS, /**< Success */
+    SDP_FAILURE,
+    SDP_INVALID_SDP_PTR,
+    SDP_NOT_SDP_DESCRIPTION,
+    SDP_INVALID_TOKEN_ORDERING,
+    SDP_INVALID_PARAMETER,
+    SDP_INVALID_MEDIA_LEVEL,
+    SDP_INVALID_CAPABILITY,
+    SDP_NO_RESOURCE,
+    SDP_UNRECOGNIZED_TOKEN,
+    SDP_NULL_BUF_PTR,
+    SDP_POTENTIAL_SDP_OVERFLOW,
+    SDP_MAX_RC
+} sdp_result_e;
+
+/**
+ * Indicates invalid bandwidth value
+ */
+#define SDP_INVALID_VALUE          (-2)
+
+/**
+ * Bandwidth modifier type for b= SDP line
+ */
+typedef enum {
+    SDP_BW_MODIFIER_INVALID = -1,
+    SDP_BW_MODIFIER_AS, /** < b=AS: */
+    SDP_BW_MODIFIER_CT, /** < b=CT: */
+    SDP_BW_MODIFIER_TIAS, /** < b=TIAS: */
+    SDP_MAX_BW_MODIFIER_VAL,
+    SDP_BW_MODIFIER_UNSUPPORTED
+} sdp_bw_modifier_e;
+
+/**
+ *  SDP attribute types
+ */
+/* Attribute Types */
+typedef enum {
+    SDP_ATTR_BEARER = 0,
+    SDP_ATTR_CALLED,
+    SDP_ATTR_CONN_TYPE,
+    SDP_ATTR_DIALED,
+    SDP_ATTR_DIALING,
+    SDP_ATTR_DIRECTION,
+    SDP_ATTR_EECID,
+    SDP_ATTR_FMTP,
+    SDP_ATTR_FRAMING,
+    SDP_ATTR_INACTIVE,
+    SDP_ATTR_PTIME,
+    SDP_ATTR_QOS,
+    SDP_ATTR_CURR,
+    SDP_ATTR_DES,
+    SDP_ATTR_CONF,
+    SDP_ATTR_RECVONLY,
+    SDP_ATTR_RTPMAP,
+    SDP_ATTR_SECURE,
+    SDP_ATTR_SENDONLY,
+    SDP_ATTR_SENDRECV,
+    SDP_ATTR_SUBNET,
+    SDP_ATTR_T38_VERSION,
+    SDP_ATTR_T38_MAXBITRATE,
+    SDP_ATTR_T38_FILLBITREMOVAL,
+    SDP_ATTR_T38_TRANSCODINGMMR,
+    SDP_ATTR_T38_TRANSCODINGJBIG,
+    SDP_ATTR_T38_RATEMGMT,
+    SDP_ATTR_T38_MAXBUFFER,
+    SDP_ATTR_T38_MAXDGRAM,
+    SDP_ATTR_T38_UDPEC,
+    SDP_ATTR_X_CAP,
+    SDP_ATTR_X_CPAR,
+    SDP_ATTR_X_PC_CODEC,
+    SDP_ATTR_X_PC_QOS,
+    SDP_ATTR_X_QOS,
+    SDP_ATTR_X_SQN,
+    SDP_ATTR_TMRGWXID,
+    SDP_ATTR_TC1_PAYLOAD_BYTES,
+    SDP_ATTR_TC1_WINDOW_SIZE,
+    SDP_ATTR_TC2_PAYLOAD_BYTES,
+    SDP_ATTR_TC2_WINDOW_SIZE,
+    SDP_ATTR_RTCP,
+    SDP_ATTR_RTR,
+    SDP_ATTR_SILENCESUPP,
+    SDP_ATTR_SRTP_CONTEXT, /* version 2 sdescriptions */
+    SDP_ATTR_MPTIME,
+    SDP_ATTR_X_SIDIN,
+    SDP_ATTR_X_SIDOUT,
+    SDP_ATTR_X_CONFID,
+    SDP_ATTR_GROUP,
+    SDP_ATTR_MID,
+    SDP_ATTR_SOURCE_FILTER,
+    SDP_ATTR_RTCP_UNICAST,
+    SDP_ATTR_MAXPRATE,
+    SDP_ATTR_SQN,
+    SDP_ATTR_CDSC,
+    SDP_ATTR_CPAR,
+    SDP_ATTR_SPRTMAP,
+    SDP_ATTR_SDESCRIPTIONS,  /* version 9 sdescriptions */
+    SDP_ATTR_LABEL,
+    SDP_ATTR_FRAMERATE,
+    SDP_ATTR_ICE_CANDIDATE,
+    SDP_ATTR_ICE_UFRAG,
+    SDP_ATTR_ICE_PWD,
+    SDP_ATTR_RTCP_MUX,
+    SDP_ATTR_DTLS_FINGERPRINT,
+    SDP_ATTR_MAXPTIME,
+    SDP_MAX_ATTR_TYPES,
+    SDP_ATTR_INVALID
+} sdp_attr_e;
+
+/**
+ * Gets the value of the fmtp attribute- parameter-sets parameter for H.264 codec
+ *
+ * @param[in]  sdp_handle     The SDP handle
+ * @param[in]  level       The level to check for the attribute.
+ * @param[in]  cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in]  inst_num    The attribute instance number to check.
+ *
+ * @return      parameter-sets value.
+ */
+
+const char* ccsdpAttrGetFmtpParamSets(void *sdp_handle, uint16_t level,
+                                            uint8_t cap_num, uint16_t inst_num);
+
+/**
+ * Gets the value of the fmtp attribute- packetization-mode parameter for H.264 codec
+ *
+ * @param[in]  sdp_handle     The SDP handle
+ * @param[in]  level       The level to check for the attribute.
+ * @param[in]  cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in]  inst_num    The attribute instance number to check.
+ * @param[out] *val        packetization-mode value in the range 0 - 2.
+ *
+ * @return     sdp_result_e         SDP_SUCCESS = SUCCESS
+ */
+sdp_result_e ccsdpAttrGetFmtpPackMode(void *sdp_handle, uint16_t level,
+                         uint8_t cap_num, uint16_t inst_num, uint16_t *val);
+/**
+ * Gets the value of the fmtp attribute- level asymmetry allowed parameter for H.264 codec
+ *
+ * @param[in]  sdp_handle     The SDP handle
+ * @param[in]  level       The level to check for the attribute.
+ * @param[in]  cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in]  inst_num    The attribute instance number to check.
+ * @param[out] *val        level-asymmetry-allowed param value in the range 0 - 1.
+ *
+ * @return     sdp_result_e         SDP_SUCCESS = SUCCESS
+ */
+sdp_result_e ccsdpAttrGetFmtpLevelAsymmetryAllowed(void *sdp_handle, uint16_t level,
+                         uint8_t cap_num, uint16_t inst_num, uint16_t *val);
+
+
+/**
+ * Gets the value of the fmtp attribute- profile-level-id parameter for H.264 codec
+ *
+ * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in]  level       The level to check for the attribute.
+ * @param[in]  cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in]  inst_num    The attribute instance number to check.
+ *
+ * @return   char *      profile-level-id value.
+ */
+const char* ccsdpAttrGetFmtpProfileLevelId (void *sdp_handle, uint16_t level,
+                                          uint8_t cap_num, uint16_t inst_num);
+
+/**
+ * Gets the value of the fmtp attribute- max-mbps parameter for H.264 codec
+ *
+ * @param[in] sdp_handle     The SDP handle
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[out] *val        max-mbps value.
+ *
+ * @return     sdp_result_e         SDP_SUCCESS
+ */
+
+sdp_result_e ccsdpAttrGetFmtpMaxMbps (void *sdp_handle, uint16_t level,
+                                uint8_t cap_num, uint16_t inst_num, uint32_t *val);
+
+/**
+ * Gets the value of the fmtp attribute- max-fs parameter for H.264 codec
+ *
+ * @param[in] sdp_handle     The SDP handle
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the
+ *                          attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[out] *val        max-fs value.
+ *
+ * @return     sdp_result_e         SDP_SUCCESS
+ */
+sdp_result_e ccsdpAttrGetFmtpMaxFs (void *sdp_handle, uint16_t level,
+                             uint8_t cap_num, uint16_t inst_num, uint32_t *val);
+
+/**
+ * Gets the value of the fmtp attribute- max-cpb parameter for H.264 codec
+ * @param[in] sdp_handle     The SDP handle
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param [out] *val      max-cpb value.
+ *
+ * @return     sdp_result_e         SDP_SUCCESS
+ */
+sdp_result_e ccsdpAttrGetFmtpMaxCpb (void *sdp_handle, uint16_t level,
+                                 uint8_t cap_num, uint16_t inst_num, uint32_t *val);
+
+/**
+ * Gets the value of the fmtp attribute- max-br parameter for H.264 codec
+ *
+ * @param[in] sdp_handle     The SDP handle
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param [out] *val      max-br value.
+ *
+ * @return     sdp_result_e         SDP_SUCCESS
+ */
+sdp_result_e ccsdpAttrGetFmtpMaxBr (void *sdp_handle, uint16_t level,
+                             uint8_t cap_num, uint16_t inst_num, uint32_t* val);
+
+/**
+ * Returns the bandwidth value parameter from the b= line.
+ *
+ * @param[in] sdp_handle     The SDP handle
+ * @param[in] level       The level from which to get the bw value.
+ * @param[in] inst_num    instance number of bw line at the level. The first
+ *                          instance has a inst_num of 1 and so on.
+ *
+ * @return     A valid numerical bw value or SDP_INVALID_VALUE(-2).
+ */
+int ccsdpGetBandwidthValue (void *sdp_handle, uint16_t level, uint16_t inst_num);
+
+/**
+ * Add a new attribute of the specified type at the given level and capability
+ * level or base attribute if cap_num is zero.
+ *
+ * @param[in] sdp_handle     The SDP handle
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] attr_type   The type of attribute to add.
+ * @param[in] inst_num    Pointer to a uint16_t in which to return the instance number of the newly added attribute.
+ *
+ * @return     sdp_result_e
+ *             SDP_SUCCESS            Attribute was added successfully.
+ *              SDP_NO_RESOURCE        No memory avail for new attribute.
+ *              SDP_INVALID_PARAMETER  Specified media line is not defined.
+ */
+sdp_result_e ccsdpAddNewAttr (void *sdp_handle, uint16_t level, uint8_t cap_num,
+                               sdp_attr_e attr_type, uint16_t *inst_num);
+
+/**
+ * Gets the value of the fmtp attribute- max-dpb parameter for H.264 codec
+ *
+ * @param[in] sdp_handle     The SDP handle
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[out] *val       max-dpb value.
+ *
+ * @return     sdp_result_e
+ *             SDP_SUCCESS            Attribute was added successfully.
+ */
+
+sdp_result_e ccsdpAttrGetFmtpMaxDpb (void *sdp_handle, uint16_t level,
+                               uint8_t cap_num, uint16_t inst_num, uint32_t *val);
+
+
+/**
+ * Sets the value of the fmtp attribute payload type parameter for the given attribute.
+ *
+ * @param[in] sdp_handle     The SDP handle
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] payload_num New payload type value.
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e ccsdpAttrSetFmtpPayloadType (void *sdp_handle, uint16_t level,
+                              uint8_t cap_num, uint16_t inst_num, uint16_t payload_num);
+
+/**
+ * Sets the value of the packetization mode attribute parameter for the given attribute.
+ *
+ * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] pack_mode   Packetization mode value
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e ccsdpAttrSetFmtpPackMode (void *sdp_handle, uint16_t level,
+                                          uint8_t cap_num, uint16_t inst_num, uint16_t pack_mode);
+/**
+ * Sets the value of the level-asymmetry-allowed attribute parameter for the given attribute.
+ *
+ * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] level_asymmetry_allowed   level asymmetry allowed value (0 or 1).
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e ccsdpAttrSetFmtpLevelAsymmetryAllowed (void *sdp_handle, uint16_t level,
+                                          uint8_t cap_num, uint16_t inst_num, uint16_t level_asymmetry_allowed);
+
+/**
+ * Sets the value of the profile-level-id parameter for the given attribute.
+ *
+ * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] profile_level_id profile_level_id to be set
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e ccsdpAttrSetFmtpProfileLevelId (void *sdp_handle, uint16_t level,
+                               uint8_t cap_num, uint16_t inst_num, const char *profile_level_id);
+
+/**
+ * Sets the value of the profile-level-id parameter for the given attribute.
+ *
+ * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] parameter_sets parameter_sets to be set
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e ccsdpAttrSetFmtpParameterSets (void *sdp_handle, uint16_t level,
+                                     uint8_t cap_num, uint16_t inst_num, const char *parameter_sets);
+
+/**
+ * Sets the value of the max-br parameter for the given attribute.
+ *
+ * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] max_br    max_br value to be set
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+
+sdp_result_e ccsdpAttrSetFmtpMaxBr (void *sdp_handle, uint16_t level,
+                              uint8_t cap_num, uint16_t inst_num, uint32_t max_br);
+
+/**
+ * Sets the value of the fmtp attribute- max-mbps parameter for H.264 codec
+ *
+ * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] max_mbps    value of max_mbps to be set
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e ccsdpAttrSetFmtpMaxMbps (void *sdp_handle, uint16_t level,
+                              uint8_t cap_num, uint16_t inst_num, uint32_t max_mbps);
+
+/**
+ * Sets the value of the fmtp attribute- max-fs parameter for H.264 codec
+ * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] max_fs      max_fs value to be set
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e ccsdpAttrSetFmtpMaxFs (void *sdp_handle, uint16_t level,
+                        uint8_t cap_num, uint16_t inst_num, uint32_t max_fs);
+/**
+ * Sets the value of the fmtp attribute- max-cbp parameter for H.264 codec
+ * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] max_cpb      max_cbp value to be set
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e ccsdpAttrSetFmtpMaxCpb (void *sdp_handle, uint16_t level,
+                            uint8_t cap_num, uint16_t inst_num, uint32_t max_cpb);
+/**
+ * Sets the value of the fmtp attribute- max-dbp parameter for H.264 codec
+ * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] max_dpb      max_dbp value to be set
+ *
+ * @return     SDP_SUCCESS            Attribute was added successfully.
+ *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+
+sdp_result_e ccsdpAttrSetFmtpMaxDbp (void *sdp_handle, uint16_t level,
+                                  uint8_t cap_num, uint16_t inst_num, uint32_t max_dpb);
+
+
+/**
+ * Sets the value of the fmtp attribute qcif parameter for the given attribute.
+ * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] qcif        Sets the QCIF value for a video codec
+ *
+ * @return      SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e ccsdpAttrSetFmtpQcif  (void *sdp_handle, uint16_t level,
+                             uint8_t cap_num, uint16_t inst_num, uint16_t qcif);
+
+/**
+ * Sets the value of the fmtp attribute sqcif parameter for the given attribute.
+ *
+ * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in] level       The level to check for the attribute.
+ * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
+ * @param[in] inst_num    The attribute instance number to check.
+ * @param[in] sqcif        Sets the SQCIF value for a video codec
+ *
+ * @return      SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e ccsdpAttrSetFmtpSqcif  (void *sdp_handle, uint16_t level,
+                            uint8_t cap_num, uint16_t inst_num, uint16_t sqcif);
+
+/**
+ *
+ * To specify bandwidth parameters at any level, a bw line must first be
+ * added at that level using this function. This function returns the instance
+ * number of an existing bw_line that matches bw_modifier type, or of a newly
+ * created bw_line of type bw_modifier. After this addition, you can set the
+ * properties of the added bw line by using sdp_set_bw().
+ *
+ * Note carefully though, that since there can be multiple instances of bw
+ * lines at any level, you must specify the instance number when setting
+ * or getting the properties of a bw line at any level.
+ *
+ * This function returns the inst_num variable, the instance number
+ * of the created bw_line at that level. The instance number is 1 based.
+ * <pre>
+ * For example:
+ *             v=0                               :Session Level
+ *             o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
+ *             s=SDP Seminar
+ *             c=IN IP4 10.1.0.2
+ *             t=0 0
+ *             b=AS:60                           : instance number 1
+ *             b=TIAS:50780                      : instance number 2
+ *             m=audio 1234 RTP/AVP 0 101 102    : 1st Media level
+ *             b=AS:12                           : instance number 1
+ *             b=TIAS:8480                       : instance number 2
+ *             m=audio 1234 RTP/AVP 0 101 102    : 2nd Media level
+ *             b=AS:20                           : instance number 1
+ * </pre>
+ * @param[in]  sdp_handle    The SDP handle returned by sdp_init_description.
+ * @param[in]  level      The level to create the bw line.
+ * @param[in]  bw_modifier The Type of bandwidth, CT, AS or TIAS.
+ * @param[out]  inst_num   This memory is set with the instance number of the newly created bw line instance.
+ *
+ * @return      SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e ccsdpAddNewBandwidthLine (void *sdp_handle, uint16_t level, sdp_bw_modifier_e bw_modifier, uint16_t *inst_num);
+
+
+/**
+ * Once a bandwidth line is added under a level, this function can be used to
+ * set the properties of that bandwidth line.
+ *
+ * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
+ * @param[in]  level       The level to at which the bw line resides.
+ * @param[in]  inst_num    The instance number of the bw line that is to be set.
+ * @param[in]  bw_modifier The Type of bandwidth, CT, AS or TIAS.
+ * @param[in]  bw_val      Numerical bandwidth value.
+ *
+ * @note Before calling this function to set the bw line, the bw line must
+ * be added using sdp_add_new_bw_line at the required level.
+ *
+ * @return      SDP_SUCCESS       Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e ccsdpSetBandwidth (void *sdp_handle, uint16_t level, uint16_t inst_num,
+                         sdp_bw_modifier_e bw_modifier, uint32_t bw_val);
+
+/**
+ * Returns a string representation of a codec's name.
+ *
+ * @param[in]  rtp_ptype      The value taken from the rtp_ptype enumeration
+ *
+ * @return     A string representing the name of the codec
+ */
+const char * ccsdpCodecName(rtp_ptype ptype);
+
+#endif
diff --git a/libs/sipcc/include/config_api.h b/libs/sipcc/include/config_api.h
new file mode 100644 (file)
index 0000000..34e54e9
--- /dev/null
@@ -0,0 +1,120 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CONFIG_API_H_
+#define _CONFIG_API_H_
+
+#include "cc_types.h"
+
+
+/**
+ * configFetchReq
+ *
+ * This function tells the config manager to fetch the CTL file
+ * and then fetch the config  from the CUCM. It is expected that
+ * this will result in processing of
+ * the config file after the config managers response is received.
+ *
+ * The response received for this request is asynchronous and
+ * should be handled via event provided by config manager.
+ * The CCAPI_Config_reponse api needs to be called for the
+ * handling of the response to the fetch request
+ *
+ */
+void configFetchReq(int device_handle);
+
+/**
+ * configParserError
+ *
+ * Notify the config manager that the config file has an error
+ * and a new config file needs to be downloaded.
+ *
+ * The error could be XML format error or minimum config not being
+ * present in the config file.  It is expected that
+ * this will result in processing of
+ * the config file after the config managers response is received.
+ *
+ */
+void configParserError(void);
+
+/**
+ * When called this function should register with CUCM without prior device file download.
+ */
+void CCAPI_Start_response(int device_handle, const char *device_name, const char *sipUser, const char *sipPassword, const char *sipDomain);
+
+/**
+ * Inform application that pSipcc stack has receive a NOTIFY message of event
+ * type "service-control" and action=apply-config. The arguments passed to this
+ * function contains the parameter values received in the message.
+ *
+ * @param config_version [in] latest version stamp of phone configuration.
+ * @param dial_plan_version [in] latest version stamp of the dial plan.
+ * @param fcp_version [in] latest version stamp of feature policy control.
+ * @param cucm_result  [in] action taken by the cucm for the changes applied by the
+ * user to the phone configuration. cucm result could be
+ *  @li "no_change" - the new phone configuration changes do not impact Unified CM,
+ *  @li "config_applied" - The Unified CM Administration applied the
+ *  configuration changes dynamically without requiring phone to re-register,
+ *  @li "reregister_needed" - The Unified CM Administration applied the
+ *  configuration changes and required the phone to re-register to make them
+ *  effective.
+ * @param load_id [in] - firmware image name that phone should be running with
+ * @param inactive_load_id [in] - firmware image name that phone should be in inactive partition.
+ * @param load_server [in] - address of load server to download the  firmware
+ * image load_id.
+ * @param log_server [in] - log server address where error report need to be
+ * sent. Error report, for example, could be related to image download failure
+ * or phone configuration file download failure.
+ * @param ppid [in] whether peer to peer upgrade is available
+ * @return void
+ */
+void configApplyConfigNotify(cc_string_t config_version,
+               cc_string_t dial_plan_version,
+               cc_string_t fcp_version,
+               cc_string_t cucm_result,
+               cc_string_t load_id,
+               cc_string_t inactive_load_id,
+               cc_string_t load_server,
+               cc_string_t log_server,
+               cc_boolean ppid);
+
+/**
+ * dialPlanFetchReq
+ *
+ * This function tells the get file request service to fetch the latest dial
+ * plan from the CUCM.
+ *
+ * @param device_handle [in] handle of the device, the response is for
+ * @param dialPlanFileName [in] the name of dialplan file to retrieve
+ * @return cc_boolean indicating success/failure
+ *
+ */
+cc_boolean dialPlanFetchReq(int device_handle, char* dialPlanFileName);
+
+/**
+ * fcpFetchReq
+ *
+ * This function tells the get file request service to fetch the latest fcp
+ * file from the CUCM.
+ *
+ * @param device_handle [in] handle of the device, the response is for
+ * @param dialPlanFileName [in] the name of fcp file to retrieve
+*  @return cc_boolean indicating success/failure
+ *
+ */
+cc_boolean fcpFetchReq(int device_handle, char* fcpFileName);
+
+
+cc_boolean CCAPI_Config_set_server_address(const char *ip_address);
+cc_boolean CCAPI_Config_set_transport_udp(const cc_boolean is_udp);
+cc_boolean CCAPI_Config_set_local_voip_port(const int port);
+cc_boolean CCAPI_Config_set_remote_voip_port(const int port);
+int CCAPI_Config_get_local_voip_port();
+int CCAPI_Config_get_remote_voip_port();
+const char* CCAPI_Config_get_version();
+cc_boolean CCAPI_Config_set_p2p_mode(const cc_boolean is_p2p);
+cc_boolean CCAPI_Config_set_sdp_mode(const cc_boolean is_sdp);
+cc_boolean CCAPI_Config_set_avp_mode(const cc_boolean is_rtpsavpf);
+
+#endif  /* _CONFIG_API_H_ */
diff --git a/libs/sipcc/include/dns_util.h b/libs/sipcc/include/dns_util.h
new file mode 100644 (file)
index 0000000..844fd72
--- /dev/null
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ *  @section DNS APIS
+ *  This module contains method definitions to be implemented by the platform.
+ *  SIP Stack uses these methods to perform DNS queries
+ */
+
+
+#ifndef _DNS_UTIL_H_
+#define _DNS_UTIL_H_
+
+#include "cc_constants.h"
+#include "cpr_types.h"
+
+/**
+ * Defines the return values for DNS query.
+ */
+#define CC_DNS_OK                 0
+#define CC_DNS_ERR_NOBUF          1
+#define CC_DNS_ERR_INUSE          2
+#define CC_DNS_ERR_TIMEOUT        3
+#define CC_DNS_ERR_NOHOST         4
+#define CC_DNS_ERR_HOST_UNAVAIL   5
+
+typedef void *srv_handle_t;
+
+/**
+ * Perform an DNS lookup of the name specified
+ *
+ * @param[in] hname host name
+ * @param[out] ipaddr_ptr the ip address returned by DNS for host with name hname.
+ * @param[in] timeout the timeout for the query
+ * @param[in] retries the retry times for the query
+ *
+ * @return CC_DNS_OK or CC_DNS_ERR_NOHOST
+ */
+cc_int32_t dnsGetHostByName(const char *hname,
+                          cpr_ip_addr_t *ipaddr_ptr,
+                          cc_int32_t timeout,
+                          cc_int32_t retries);
+
+/**
+ * This function calls the dns api to perform a dns srv query.
+ *
+ * @param[in]  service the service name, e.g. "sip"
+ * @param[in]  protocol the protocol name, e.g. "udp"
+ * @param[in]  domain the domain name
+ * @param[out] ipaddr_ptr the returned ip address
+ * @param[out] port the port
+ * @param[in]  timeout the timeout for the query
+ * @param[in]  retries the number of retries for the query
+ * @param[in,out] psrv_handle the handle for this query
+ *
+ * @note if handle is NULL an new query needs to be made and the
+ * first response needs to be returned along with a valid handle.
+ * Subsequent calls should return the next result if a valid handle has been provided.
+ *
+ * @returns CC_DNS_OK for success or
+ *           CC_DNS_ERR_NOHOST, CC_DNS_ERR_HOST_UNAVAIL
+ */
+cc_int32_t dnsGetHostBySRV(cc_int8_t *service,
+                         cc_int8_t *protocol,
+                         cc_int8_t *domain,
+                         cpr_ip_addr_t *ipaddr_ptr,
+                         cc_uint16_t *port,
+                         cc_int32_t timeout,
+                         cc_int32_t retries,
+                         srv_handle_t *psrv_handle);
+
+/**
+ * Free the srvhandle allocated in dnsGetHostBySRV.
+ *
+ * @param[in] srv_handle handle to be freed.
+ *
+ */
+
+void dnsFreeSrvHandle(srv_handle_t srv_handle);
+
+#endif /* _DNS_UTIL_H_ */
diff --git a/libs/sipcc/include/peer_connection_types.h b/libs/sipcc/include/peer_connection_types.h
new file mode 100644 (file)
index 0000000..e1fa8a8
--- /dev/null
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PEER_CONNECTION_TYPES_H_
+#define _PEER_CONNECTION_TYPES_H_
+
+#define MAX_TRACKS 8
+
+enum StatusCode {
+    PC_OK = 0,
+    PC_INVALID_HINTS,
+    PC_INVALID_OFFER,
+    PC_INVALID_REMOTE_SDP,
+    PC_INVALID_LOCAL_SDP,
+    PC_NO_OBSERVER,
+    PC_SDPCHANGED,
+    PC_SETLOCALDESCERROR,
+    PC_SETREMOTEDESCERROR,
+    PC_INTERNAL_ERROR
+};
+
+typedef struct MediaTrack {
+    unsigned int    media_stream_track_id;
+    int             video;
+} MediaTrack;
+
+typedef struct MediaStreamTable {
+    unsigned int    media_stream_id;
+    unsigned int    num_tracks;
+    MediaTrack      track[MAX_TRACKS];
+} MediaStreamTable;
+
+
+#endif /*_PEER_CONNECTION_TYPES_H_*/
diff --git a/libs/sipcc/include/plat_api.h b/libs/sipcc/include/plat_api.h
new file mode 100644 (file)
index 0000000..a3f9ab4
--- /dev/null
@@ -0,0 +1,720 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PLAT_API_H_
+#define _PLAT_API_H_
+
+#include "cc_constants.h"
+#include "cpr_socket.h"
+#include "cc_types.h"
+
+/**
+ * Define unregister reason
+ */
+#define CC_UNREG_REASON_UNSPECIFIED                       0
+//Common with what SCCP uses...need to match with J-Side
+#define CC_UNREG_REASON_TCP_TIMEOUT                       10
+#define CC_UNREG_REASON_CM_RESET_TCP                      12
+#define CC_UNREG_REASON_CM_ABORTED_TCP                    13
+#define CC_UNREG_REASON_CM_CLOSED_TCP                     14
+#define CC_UNREG_REASON_REG_TIMEOUT                       17
+#define CC_UNREG_REASON_FALLBACK                          18
+#define CC_UNREG_REASON_PHONE_KEYPAD                      20
+#define CC_UNREG_REASON_RESET_RESET                       22
+#define CC_UNREG_REASON_RESET_RESTART                     23
+#define CC_UNREG_REASON_PHONE_REG_REJ                     24
+#define CC_UNREG_REASON_PHONE_INITIALIZED                 25
+#define CC_UNREG_REASON_VOICE_VLAN_CHANGED                26
+#define CC_UNREG_REASON_POWER_SAVE_PLUS                   32
+//sip specific ones...need to match with J-Side
+#define CC_UNREG_REASON_VERSION_STAMP_MISMATCH            100
+#define CC_UNREG_REASON_VERSION_STAMP_MISMATCH_CONFIG     101
+#define CC_UNREG_REASON_VERSION_STAMP_MISMATCH_SOFTKEY    102
+#define CC_UNREG_REASON_VERSION_STAMP_MISMATCH_DIALPLAN   103
+#define CC_UNREG_REASON_APPLY_CONFIG_RESTART              104
+#define CC_UNREG_REASON_CONFIG_RETRY_RESTART              105
+#define CC_UNREG_REASON_TLS_ERROR                         106
+#define CC_UNREG_REASON_RESET_TO_INACTIVE_PARTITION       107
+#define CC_UNREG_REASON_VPN_CONNECTIVITY_LOST             108
+
+#define CC_IPPROTO_UDP 17
+#define CC_IPPROTO_TCP 6
+
+
+/**
+ * socket security status
+ */
+typedef enum
+{
+    PLAT_SOCK_SECURE,
+    PLAT_SOCK_NONSECURE
+} plat_soc_status_e;
+
+/**
+ * socket connection status
+ */
+typedef enum
+{
+    PLAT_SOCK_CONN_OK,
+    PLAT_SOCK_CONN_WAITING,
+    PLAT_SOCK_CONN_FAILED
+} plat_soc_connect_status_e;
+
+/**
+ * socket connection type
+ */
+typedef enum
+{
+    PLAT_SOCK_CUCM
+} plat_soc_connect_type_e;
+
+/**
+ * socket connection mode
+ */
+typedef enum
+{
+    PLAT_SOCK_AUTHENTICATED,
+    PLAT_SOCK_ENCRYPTED,
+    PLAT_SOCK_NON_SECURE
+} plat_soc_connect_mode_e;
+
+/**
+ * psipcc core debug categories
+ */
+typedef enum
+{
+    CC_DEBUG_CCAPP,
+    CC_DEBUG_CONFIG_CACHE,
+    CC_DEBUG_SIP_ADAPTER,
+    CC_DEBUG_CCAPI,
+    CC_DEBUG_CC_MSG,
+    CC_DEBUG_FIM,
+    CC_DEBUG_FSM,
+    CC_DEBUG_AUTH,
+    CC_DEBUG_GSM,
+    CC_DEBUG_LSM,
+    CC_DEBUG_FSM_CAC,
+    CC_DEBUG_DCSM,
+    CC_DEBUG_SIP_TASK,
+    CC_DEBUG_SIP_STATE,
+    CC_DEBUG_SIP_MSG,
+    CC_DEBUG_SIP_REG_STATE,
+    CC_DEBUG_SIP_TRX,
+    CC_DEBUG_TIMERS,
+    CC_DEBUG_SIP_DM,
+    CC_DEBUG_CCDEFAULT, /* Always ON by default */
+    CC_DEBUG_DIALPLAN,
+    CC_DEBUG_KPML,
+    CC_DEBUG_REMOTE_CC,
+    CC_DEBUG_SIP_PRESENCE,
+    CC_DEBUG_CONFIG_APP,
+    CC_DEBUG_CALL_EVENT,
+    CC_DEBUG_PLAT,
+    CC_DEBUG_NOTIFY,
+    CC_DEBUG_CPR_MEMORY, /* Has additional parameters - Tracking/poison */
+    CC_DEBUG_MAX        /* NOT USED */
+} cc_debug_category_e;
+
+
+/**
+ * debug show categories
+ */
+typedef enum
+{
+    CC_DEBUG_SHOW_FSMCNF,
+    CC_DEBUG_SHOW_FSMDEF,
+    CC_DEBUG_SHOW_FSMXFR,
+    CC_DEBUG_SHOW_FSMB2BCNF,
+    CC_DEBUG_SHOW_DCSM,
+    CC_DEBUG_SHOW_FIM,
+    CC_DEBUG_SHOW_FSM,
+    CC_DEBUG_SHOW_LSM,
+    CC_DEBUG_SHOW_BULK_REGISTER,
+    CC_DEBUG_SHOW_KPML,
+    CC_DEBUG_SHOW_REMOTE_CC,
+    CC_DEBUG_SHOW_CONFIG_CACHE,
+    CC_DEBUG_SHOW_SUBS_STATS,
+    CC_DEBUG_SHOW_PUBLISH_STATS,
+    CC_DEBUG_SHOW_REGISTER,
+    CC_DEBUG_SHOW_DIALPLAN,
+    CC_DEBUG_SHOW_CPR_MEMORY, /* Has additional parameters -
+                                 config/heap-gaurd/stat/tracking. */
+    CC_DEBUG_SHOW_MAX
+} cc_debug_show_options_e;
+
+/**
+ * debug clear categories
+ */
+typedef enum
+{
+    CC_DEBUG_CLEAR_CPR_MEMORY,
+    CC_DEBUG_CLEAR_MAX
+} cc_debug_clear_options_e;
+
+/**
+ * cpr memory debug sub-categories
+ */
+typedef enum
+{
+    CC_DEBUG_CPR_MEM_TRACKING,
+    CC_DEBUG_CPR_MEM_POISON
+} cc_debug_cpr_mem_options_e;
+
+/**
+ * cpr memory clear sub-commands
+ */
+typedef enum
+{
+    CC_DEBUG_CLEAR_CPR_TRACKING,
+    CC_DEBUG_CLEAR_CPR_STATISTICS
+} cc_debug_clear_cpr_options_e;
+
+/**
+  * cpr memory show sub-commands
+  */
+typedef enum
+{
+    CC_DEBUG_SHOW_CPR_CONFIG,
+    CC_DEBUG_SHOW_CPR_HEAP_GUARD,
+    CC_DEBUG_SHOW_CPR_STATISTICS,
+    CC_DEBUG_SHOW_CPR_TRACKING
+} cc_debug_show_cpr_options_e;
+
+/**
+ * Enabling/disabling debugs
+ */
+typedef enum
+{
+    CC_DEBUG_DISABLE,
+    CC_DEBUG_ENABLE
+} cc_debug_flag_e;
+
+// Corresponds to the values for XML tags DHCPv4Status and DHCPv6Status
+typedef enum {
+       DHCP_STATUS_GOOD = 1,
+       DHCP_STATUS_TIMEOUT,
+       DHCP_STATUS_DISABLED
+ } dhcp_status_e;
+
+
+// Corresponds to the value for XML tag for DNSStatusUnifiedCMX
+typedef enum {
+   DNS_STATUS_GOOD = 1,
+   DNS_STATUS_TIMEOUT,
+   DNS_STATUS_DID_NOT_RESOLVE,
+   DNS_STATUS_NA_IP_CONFIGURED
+} ucm_dns_resolution_status_e;
+
+#define LEN32   32
+#define IP_ADDR_MAX_LEN       32
+#define PORT_MAX_LEN          20
+#define STATUS_MAX_LEN        4
+#define LEN80   80
+#define WIRED_PROP_PREFIX     "dhcp.eth0"
+#define WIRELESS_PROP_PREFIX  "dhcp.mlan0"
+#define WIRED_INT 1
+#define WIFI_INT  2
+
+
+/**
+ * Called by the thread to initialize any thread specific data
+ * once the thread is created.
+ *
+ * @param[in] tname       thread name
+ *
+ * @return 0 - SUCCESS
+ *        -1 - FAILURE
+ */
+int platThreadInit(char * tname);
+
+/**
+ * The initial initialization function for any platform related
+ * modules
+ *
+ *
+ * @return 0 - SUCCESS
+ *        -1 - FAILURE
+ */
+int platInit();
+
+/**
+ * The initial initialization function for the debugging/logging
+ * modules
+ *
+ */
+void debugInit();
+
+/**
+ * Get device model that will be sent to cucm in the UserAgent header
+ *
+ * @return char * Pointer to the string containing the model number of the phone.
+ */
+char *platGetModel();
+
+/**
+ * plat_audio_device_t
+ * Enums for indicating audio device
+ */
+typedef enum vcm_audio_device_type {
+    VCM_AUDIO_DEVICE_NONE,
+    VCM_AUDIO_DEVICE_HEADSET,
+    VCM_AUDIO_DEVICE_SPEAKER
+} plat_audio_device_t;
+
+
+/**
+ * Add cc control classifier
+ *
+ * Called by SIP stack to specify addresses and ports that will be used for call control
+ *
+ * @param[in] myIPAddr - phone local interface IP Address
+ * @param[in] myPort - phone local interface Port
+ * @param[in] cucm1IPAddr - CUCM 1 IP Address
+ * @param[in] cucm1Port - CUCM 1 Port
+ * @param[in] cucm2IPAddr - CUCM 2 IP Address
+ * @param[in] cucm2Port - CUCM 2 Port
+ * @param[in] cucm3IPAddr - CUCM 3 IP Address
+ * @param[in] cucm3Port - CUCM 3 Port
+ * @param[in] protocol - CC_IPPROTO_UDP or CC_IP_PROTO_TCP
+ *
+ * @note : Needed only if using WiFi. If not using Wifi please provide a stub
+ */
+void platAddCallControlClassifiers(unsigned long myIPAddr, unsigned short myPort,
+       unsigned long cucm1IPAddr, unsigned short cucm1Port,
+       unsigned long cucm2IPAddr, unsigned short cucm2Port,
+       unsigned long cucm3IPAddr, unsigned short cucm3Port,
+       unsigned char  protocol);
+
+/**
+ * Remove cc control classifier.
+ *
+ * Undo platAddCallControlClassifiers
+ */
+void platRemoveCallControlClassifiers();
+
+/**
+ * Tell whether wifi is supported and active
+ *
+ * @return boolean wether WLAN is active or not
+ */
+cc_boolean     platWlanISActive();
+
+/**
+ * Check if the netowrk interface changed.
+ *
+ * @return boolean returns TRUE if the network interface has changed
+ *
+ * @note Most common case is for softphone clients where if a PC is
+ * undocked the network interface changes from wired to wireless.
+ */
+boolean        platIsNetworkInterfaceChanged();
+
+/**
+ * Get active phone load name
+ *
+ * Returns the phone images in the active and inactive partitions
+ * The phone reports these phone loads to CUCM for display on the Admin page
+ *
+ * @param[in] image_a : Populate the image name from partition a
+ * @param[in] image_b : Populate the image name from partition b
+ * @param[in] len : Length of the pointers for image_a and image_b
+ * @return 1 - image_a is active
+ *         2 - image_b is active
+ *        -1 - Failure
+ */
+int platGetActiveInactivePhoneLoadName(char * image_a, char * image_b, int len);
+
+/**
+ * Get localized phrase for the specified index
+ *
+ * @param[in] index  the phrase index, see
+ * @param[in] phrase the return phrase holder
+ * @param[in] len the input length to cap the maximum value
+ *
+ * @return SUCCESS or FAILURE
+ */
+int platGetPhraseText(int index, char* phrase, unsigned int len);
+
+/**
+ * Set the unregistration reason
+ *
+ * @param[in] reason see the unregister reason definitions.
+ *
+ * @note we expect the platform to save this value in Non Volatile memory
+ * This will be retrieved later by using platGetUnregReason. This reason is reported to CUCM
+ */
+void platSetUnregReason(int reason);
+
+
+/**
+ * Get the unregistration reason code.
+ *
+ * @return reason code for unregistration
+ */
+int platGetUnregReason();
+
+/**
+ * Sets the time based on Date header in 200 OK from REGISTER request
+ * @param void
+ * @return void
+ */
+void platSetCucmRegTime (void);
+
+/**
+ * Set the kpml value for application.
+ *
+ * @param kpml_config the kpml value
+ */
+void platSetKPMLConfig(cc_kpml_config_t kpml_config);
+
+/**
+ * Check if a line has active MWI status
+ *
+ * @param line
+ *
+ * @return boolean
+ *
+ * @note The stack doesn't store the MWI status and expects
+ * the application to store that information. The stack
+ * queries the mwi status from the application using this method.
+ */
+boolean platGetMWIStatus(cc_lineid_t line);
+
+/**
+ * Check if the speaker or headset is enabled.
+ *
+ * @return boolean if the speaker or headset is enabled, returns true.
+ */
+boolean platGetSpeakerHeadsetMode();
+
+/**
+ * Secure Socket API's.
+ * The pSIPCC expects the following Secure Socket APIs to be implemented in the
+ * vendor porting layer.
+ */
+
+/**
+ * platSecIsServerSecure
+ *
+ * @brief Lookup the secure status of the server
+ *
+ * This function looks at the the CCM server type by using the security library
+ * and returns appropriate indication to the pSIPCC.
+ *
+ *
+ * @return   Server is security enabled or not
+ *           PLAT_SOCK_SECURE or PLAT_SOCK_NONSECURE
+ *
+ * @note This API maps to the following HandyIron API:
+ *  int secIsServerSecure(SecServerType type) where type should be SRVR_TYPE_CCM
+ */
+plat_soc_status_e platSecIsServerSecure(void);
+
+
+/**
+ * platSecSocConnect
+ * @brief  Securely connect to a remote server
+ *
+ * This function uses the security library APIs to connect to a remote server.
+ * @param[in]  host         server addr
+ * @param[in]  port         port number
+ * @param[in]  ipMode       IP mode to indicate v6, v4 or both
+ * @param[in]  mode         blocking connect or not
+ *                          FALSE: non-blocking; TRUE: blocking
+ * @param[in]  tos          TOS value
+ * @param[in]  connectionMode The mode of the connection
+ *                            (Authenticated/Encrypted)
+ * @param[out] localPort    local port used for the connection
+ *
+ * @return     client socket descriptor
+ *             >=0: connected or in progress
+ *             INVALID SOCKET: failed
+ *
+ * @pre        (hostAndPort not_eq NULL)
+ * @pre        (localPort   not_eq NULL)
+ *
+ * @note localPort is undefined when the return value is INVALID_SOCKET
+ *
+ * @note This API maps to the HandyIron APIs as follows:
+ * If mode == TRUE (blocking):
+ *    int secEstablishSecureConnection(const char* serverAddr, *uint32_t port, secConnectionType type)
+ *    @li ipMode is UNUSED
+ *    @li "host" maps to "serverAddr", "type" should be determined by an application and use the value from SecServerType.
+ *    @li localPort is passed in as 0
+ * If mode == FALSE (non-blocking):
+ *     int secConnect(const char* serverAddr, uint32_t port, *secConnectionType type, uint32_t localPort)
+ *    @li ipMode is UNUSED
+ *    @li "host" maps to "serverAddr", "type" should be determined by an application and use the value from SecServerType.
+ *
+ * @note The implementation should use the "setsockopt" to set the "tos" value passed
+ * in this API on the created socket.
+ *
+ */
+cpr_socket_t
+platSecSocConnect (char *host,
+                  int     port,
+                  int     ipMode,
+                  boolean mode,
+                  unsigned int tos,
+                  plat_soc_connect_mode_e connectionMode,
+                  cc_uint16_t *localPort);
+
+/**
+ * platSecSockIsConnected
+ * Determine the status of a secure connection that was initiated
+ * in non-blocking mode
+ *
+ * @param[in]    sock   socket descriptor
+ *
+ * @return   connection status
+ *           @li connection complete: PLAT_SOCK_CONN_OK
+ *           @li connection waiting:  PLAT_SOCK_CONN_WAITING
+ *           @li connection failed:   PLAT_SOCK_CONN_FAILED
+ *
+ * @note This API maps to the following HandyIron API:
+ * int secIsConnectionReady (int connDesc)
+ * The "sock" is the connection descriptor.
+ */
+plat_soc_connect_status_e platSecSockIsConnected (cpr_socket_t sock);
+
+/**
+ * platGenerateCryptoRand
+ * @brief Generates a Random Number
+ *
+ * Generate crypto graphically random number for a desired length.
+ * The function is expected to be much slower than the cpr_rand().
+ * This function should be used when good random number is needed
+ * such as random number that to be used for SRTP key for an example.
+ *
+ * @param[in] buf  - pointer to the buffer to store the result of random
+ *                   bytes requested.
+ * @param[in] len  - pointer to the length of the desired random bytes.
+ *             When calling the function, the integer's value
+ *             should be set to the desired number of random
+ *             bytes ('buf' should be of at least this size).
+ *             upon success, its value will be set to the
+ *             actual number of random bytes being returned.
+ *             (realistically, there is a maximum number of
+ *             random bytes that can be returned at a time.
+ *             if the caller request more than that, the
+ *             'len' will indicate how many bytes are actually being
+ *             returned) on failure, its value will be set to 0.
+ *
+ * @return
+ *     1 - success.
+ *     0 - fail.
+ *
+ * @note The intent of this function is to generate a cryptographically strong
+ * random number. Vendors can map this to HandyIron or OpenSSL random number
+ * generation functions.
+ *
+ * @note This API maps to the following HandyIron API:
+ * int secGetRandomData(uint8_t *buf, uint32_t size). Also note that a
+ * "secAddEntropy(...)" may be required the first time to feed entropy data to
+ * the random number generator.
+ */
+int platGenerateCryptoRand(cc_uint8_t *buf, int *len);
+
+/**
+ * platSecSocSend
+ *
+ * @brief The platSecSocSend() function is used to send data over a secure
+ * socket.
+ *
+ * The platSecSocSend() function shall transmit a message from the specified socket to
+ * its peer. The platSecSocSend() function shall send a message only when the socket is
+ * connected. The length of the message to be sent is specified by the length
+ * argument. If the message is too long to pass through the underlying protocol,
+ * platSecSocSend() shall fail and no data is transmitted.  Delivery of the message is
+ * not guaranteed.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ *
+ * @return Upon successful completion, platSecSocSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+platSecSocSend (cpr_socket_t soc,
+         CONST void *buf,
+         size_t len);
+
+/**
+ * platSecSocRecv
+ *
+ * @brief The platSecSocRecv() function shall receive a message from a secure socket.
+ *
+ * This function is normally used with connected sockets because it does not permit
+ * the application to retrieve the source address of received data.  The
+ * platSecSocRecv() function shall return the length of the message written to
+ * the buffer pointed to by the "buf" argument.
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+platSecSocRecv (cpr_socket_t soc,
+         void * RESTRICT buf,
+         size_t len);
+
+/**
+ * platSecSocClose
+ *
+ * @brief The platSecSocClose function shall close a secure socket
+ *
+ * The platSecSocClose() function shall destroy the socket descriptor indicated
+ * by socket.
+ *
+ * @param[in] soc  - The socket that needs to be destroyed
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+platSecSocClose (cpr_socket_t soc);
+
+/**
+ * Sets the SIS protocol version
+ *
+ * @param a - major version
+ * @param b - minor version
+ * @param c - additional version information
+ * @param name - version name
+ *
+ * @return void
+ * @note the platform should store this information and provide it when asked via the platGetSISProtocolVer()
+ */
+
+void platSetSISProtocolVer(cc_uint32_t a, cc_uint32_t b, cc_uint32_t c, char* name);
+
+/**
+ * Provides the SIS protocol version
+ *
+ * @param *a pointer to fill in the major version
+ * @param *b pointer to fill in the minor version
+ * @param *c pointer to fill in the additonal version
+ * @param *name pointer to fill in the version name
+ *
+ * @return void
+ */
+void
+platGetSISProtocolVer (cc_uint32_t *a, cc_uint32_t *b, cc_uint32_t *c, char* name);
+
+/**
+ * Provides the local IP address
+ *
+ * @return char *  returns the local IP address
+ */
+char *platGetIPAddr();
+
+/**
+ * Provides the local MAC address
+ *
+ * @param *maddr the pointer to the string holding MAC address
+ *             in the MAC address format after converting from string format.
+ * @return void
+ */
+void platGetMacAddr(char *addr);
+
+
+/**
+ *  platGetFeatureAllowed
+ *
+ *      Get whether the feature is allowed
+ *
+ *  @param featureId - sis feature id
+ *
+ *  @return  1 - allowed, 0 - not allowed
+ *
+ */
+int platGetFeatureAllowed(cc_sis_feature_id_e featureId);
+
+
+/**
+ * Set the Status message for failure reasons
+ * @param char *msg
+ * @return void
+ */
+void platSetStatusMessage(char *msg);
+
+/**
+ * The equivalent of the printf function.
+ *
+ * This function MUST be implemented by the vendors. This is called by the core
+ * library whenever some debugging information needs to be printed out.
+ * call this function in order to clear the CPR Memory/Tracking statistics
+ *
+ * Please also be aware that cpr_stdio.h has other logging functions as well.
+ * Vendors need to implement this function and the functions (err_msg,
+ * buginf....etc) found in cpr_stdio.h
+ *
+ * @param[in] _format  format string
+ * @param[in] ...     variable arg list
+ *
+ * @return  Return code from printf
+ */
+int debugif_printf(const char *_format, ...);
+
+/**
+ * Enable / disable speaker
+ *
+ * @param[in] state - true -> enable speaker, false -> disable speaker
+ *
+ * @return void
+ */
+
+void platSetSpeakerMode(cc_boolean state);
+
+/**
+ * Get the status (on/off) of the audio device
+ *
+ * @param[in]  device_type - headset or speaker (see vcm_audio_device_t)
+ *
+ * @return 1 -> On, 0 -> off, ERROR -> unknown (error)
+ */
+
+int platGetAudioDeviceStatus(plat_audio_device_t device_type);
+
+/*
+ * Returns the default gateway
+ *
+ * @param void
+ * @return u_long
+ */
+cc_ulong_t platGetDefaultgw();
+
+
+#endif /* _PLATFORM_API_H_ */
diff --git a/libs/sipcc/include/reset_api.h b/libs/sipcc/include/reset_api.h
new file mode 100644 (file)
index 0000000..dc629d0
--- /dev/null
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RESET_API_H_
+#define _RESET_API_H_
+
+
+/**
+ * resetRequest
+ *
+ * This function tells the reset Manager that the SIPCC module
+ * wants to do a HARD RESET. This is most likely because of a request
+ * from the CUCM.
+ *
+ * The response received for this request is asynchronous and
+ * should be handled via event provided by reset manager.
+ * The CCAPI_Service_shutdown api needs to be called for the
+ * handling of the response to the reset request
+ *
+ */
+void resetRequest();
+
+
+/**
+ * resetReady
+ *
+ * This function tells the reset manager that call control is
+ * ready for reset. This is called whenever the call control
+ * determines that it is idle
+ *
+ * The resetManager will keep track of events can initate
+ * reset when it has received ready.
+ *
+ */
+void resetReady();
+
+/**
+ * resetNotReady
+ *
+ * This function tells the reset manager that call control is
+ * NOT ready for reset. This is called whenever the call control
+ * is not idle
+ *
+ * The resetManager will keep track of events  and it CANNOT initate
+ * reset until a resetReady event is received
+ *
+ */
+void resetNotReady();
+
+#endif  /* _RESET_API_H_ */
diff --git a/libs/sipcc/include/sll_lite.h b/libs/sipcc/include/sll_lite.h
new file mode 100644 (file)
index 0000000..ee7f20b
--- /dev/null
@@ -0,0 +1,137 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SLL_LITE_H
+#define _SLL_LITE_H
+
+#include "cpr_types.h"
+/*
+ * List node structure
+ */
+typedef struct sll_lite_node_t_ {
+    struct sll_lite_node_t_ *next_p;   /* pointer to the next node */
+} sll_lite_node_t;
+
+/*
+ * List control structure
+ */
+typedef struct sll_lite_list_t_ {
+    uint16_t        count;    /* number of elements on the list    */
+    sll_lite_node_t *head_p;  /* pointer to the head or first node */
+    sll_lite_node_t *tail_p;  /* pointer to the tail or last node  */
+} sll_lite_list_t;
+
+typedef enum {
+    SLL_LITE_RET_SUCCESS,
+    SLL_LITE_RET_INVALID_ARGS,
+    SLL_LITE_RET_NODE_NOT_FOUND,
+    SLL_LITE_RET_OTHER_FAILURE
+} sll_lite_return_e;
+
+/*
+ * Convenient macros
+ */
+#define SLL_LITE_LINK_HEAD(link) \
+     (((sll_lite_list_t *)link)->head_p)
+
+#define SLL_LITE_LINK_TAIL(link) \
+     (((sll_lite_list_t *)link)->tail_p)
+
+#define SLL_LITE_LINK_NEXT_NODE(node) \
+     (((sll_lite_node_t *)node)->next_p)
+
+#define SLL_LITE_NODE_COUNT(link) \
+     (((sll_lite_list_t *)link)->count)
+
+/**
+ * sll_lite_init initializes list control structure given by the
+ * caller.
+ *
+ * @param[in] list - pointer to the list control structure
+ *                  sll_lite_list_t
+ *
+ * @return        - SLL_LITE_RET_SUCCESS for success
+ *                - SLL_LITE_RET_INVALID_ARGS when arguments are
+ *                  invalid.
+ */
+extern sll_lite_return_e
+sll_lite_init(sll_lite_list_t *list);
+
+/**
+ * sll_lite_link_head puts node to the head of the list.
+ *
+ * @param[in] list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ * @param[in] node - pointer to the list node structure.
+ *
+ * @return        - SLL_LITE_RET_SUCCESS for success
+ *                - SLL_LITE_RET_INVALID_ARGS when arguments are
+ *                  invalid.
+ */
+extern sll_lite_return_e
+sll_lite_link_head(sll_lite_list_t *list, sll_lite_node_t *node);
+
+/**
+ * sll_lite_link_tail puts node to the tail of the list.
+ *
+ * @param[in] list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ * @param[in] node - pointer to the list node structure.
+ *
+ * @return        - SLL_LITE_RET_SUCCESS for success
+ *                - SLL_LITE_RET_INVALID_ARGS when arguments are
+ *                  invalid.
+ */
+sll_lite_return_e
+sll_lite_link_tail(sll_lite_list_t *list, sll_lite_node_t *node);
+
+/**
+ * sll_lite_unlink_head removes head node from the head of the list and
+ * returns it to the caller.
+ *
+ * @param[in] list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ *
+ * @return        Pointer to the head node if one exists otherwise
+ *                return NULL.
+ */
+extern sll_lite_node_t *
+sll_lite_unlink_head(sll_lite_list_t *list);
+
+/**
+ * sll_lite_unlink_tail removes tail node from the list and
+ * returns it to the caller.
+ *
+ * @param[in] list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ *
+ * @return        Pointer to the tail node if one exists otherwise
+ *                return NULL.
+ */
+sll_lite_node_t *
+sll_lite_unlink_tail(sll_lite_list_t *list);
+
+/**
+ * sll_lite_remove removes the given node from the list.
+ *
+ * @param[in] list - pointer to the list control structure
+ *                  sll_lite_list_t. The list must be
+ *                  initialized prior.
+ * @param[in] node - pointer to the list node structure to be
+ *                  removed.
+ *
+ * @return        - SLL_LITE_RET_SUCCESS for success
+ *                - SLL_LITE_RET_INVALID_ARGS when arguments are
+ *                  invalid.
+ *                - SLL_LITE_RET_NODE_NOT_FOUND when the node
+ *                  to remove is not found.
+ */
+extern sll_lite_return_e
+sll_lite_remove(sll_lite_list_t *list, sll_lite_node_t *node);
+
+#endif
diff --git a/libs/sipcc/include/vcm.h b/libs/sipcc/include/vcm.h
new file mode 100755 (executable)
index 0000000..e318574
--- /dev/null
@@ -0,0 +1,1014 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/** @section vcm  VCM APIs
+ *
+ *  @section Introduction
+ *  This module contains command APIs to the media layer
+ */
+
+/**
+ * @file   vcm.h
+ * @brief  APIs to interface with the Media layer.
+ *
+ * This file contains API that interface to the media layer on the platform.
+ * The following APIs need to be implemented to have the sip stack interact
+ * and issue commands to the media layer.
+ */
+
+#ifndef _VCM_H_
+#define _VCM_H_
+
+#include "cpr_types.h"
+#include "cc_constants.h"
+#include "ccsdp.h"
+
+
+/** Evaluates to TRUE for audio media streams where id is the mcap_id of the given stream */
+#define CC_IS_AUDIO(id) ((id == CC_AUDIO_1) ? TRUE:FALSE)
+/** Evaluates to TRUE for video media streams where id is the mcap_id of the given stream */
+#define CC_IS_VIDEO(id) ((id == CC_VIDEO_1) ? TRUE:FALSE)
+
+
+/** Definitions for direction requesting Play tone to user */
+#define VCM_PLAY_TONE_TO_EAR      1
+/** Definitions value for direction requesting Play tone to network stream or far end */
+#define VCM_PLAY_TONE_TO_NET      2
+/** Definitions value for direction requesting Play tone to both user and network */
+#define VCM_PLAY_TONE_TO_ALL      3
+
+/** Definitions for alert_info in vcmToneStartWithSpeakerAsBackup API */
+#define VCM_ALERT_INFO_OFF        0
+/** Definitions for alert_info in vcmToneStartWithSpeakerAsBackup API */
+#define VCM_ALERT_INFO_ON         1
+
+/** Definitions for DSP Codec Resources. */
+#define VCM_CODEC_RESOURCE_G711     0x00000001
+#define VCM_CODEC_RESOURCE_G729A    0x00000002
+#define VCM_CODEC_RESOURCE_G729B    0x00000004
+#define VCM_CODEC_RESOURCE_LINEAR   0x00000008
+#define VCM_CODEC_RESOURCE_G722     0x00000010
+#define VCM_CODEC_RESOURCE_iLBC     0x00000020
+#define VCM_CODEC_RESOURCE_iSAC     0x00000040
+#define VCM_CODEC_RESOURCE_H264     0x00000080
+#define VCM_CODEC_RESOURCE_H263     0x00000002
+#define VCM_CODEC_RESOURCE_VP8      0x00000100
+#define VCM_CODEC_RESOURCE_I420     0x00000200
+#define VCM_CODEC_RESOURCE_OPUS     0x00000400
+
+#define VCM_DSP_DECODEONLY  0
+#define VCM_DSP_ENCODEONLY  1
+#define VCM_DSP_FULLDUPLEX  2
+#define VCM_DSP_IGNORE      3
+
+#define CC_KFACTOR_STAT_LEN   (256)
+
+
+/**
+ *  vcm_tones_t
+ *  Enum identifying various tones that the media layer should implement
+ */
+
+typedef enum
+{
+    VCM_INSIDE_DIAL_TONE,
+    VCM_OUTSIDE_DIAL_TONE,
+    VCM_DEFAULT_TONE = 1,
+    VCM_LINE_BUSY_TONE,
+    VCM_ALERTING_TONE,
+    VCM_BUSY_VERIFY_TONE,
+    VCM_STUTTER_TONE,
+    VCM_MSG_WAITING_TONE,
+    VCM_REORDER_TONE,
+    VCM_CALL_WAITING_TONE,
+    VCM_CALL_WAITING_2_TONE,
+    VCM_CALL_WAITING_3_TONE,
+    VCM_CALL_WAITING_4_TONE,
+    VCM_HOLD_TONE,
+    VCM_CONFIRMATION_TONE,
+    VCM_PERMANENT_SIGNAL_TONE,
+    VCM_REMINDER_RING_TONE,
+    VCM_NO_TONE,
+    VCM_ZIP_ZIP,
+    VCM_ZIP,
+    VCM_BEEP_BONK,
+/*#$#$#$#$#@$#$#$#$#$#$#$#$#$#$#$#$$#$#$#$#$#$#$#$#$
+ * There is a corresponding table defined in
+ * dialplan.c tone_names[]. Make sure to add tone
+ * name in that table if you add any new entry above
+ */
+    VCM_RECORDERWARNING_TONE,
+    VCM_RECORDERDETECTED_TONE,
+    VCM_MONITORWARNING_TONE,
+    VCM_SECUREWARNING_TONE,
+    VCM_NONSECUREWARNING_TONE,
+    VCM_MAX_TONE,
+    VCM_MAX_DIALTONE = VCM_BEEP_BONK
+} vcm_tones_t;
+
+
+/**
+ *  vcm_tones_t
+ *  Enum identifying various tones that the media layer should implement
+ */
+
+
+/**
+ * vcm_ring_mode_t
+ * VCM_RING_OFFSET is used to map the list
+ * of ring names to the correct enum type
+ * when parsing the alert-info header.
+ */
+typedef enum
+{
+    VCM_RING_OFF     = 0x1,
+    VCM_INSIDE_RING  = 0x2,
+    VCM_OUTSIDE_RING = 0x3,
+    VCM_FEATURE_RING = 0x4,
+    VCM_BELLCORE_DR1 = 0x5,
+    VCM_RING_OFFSET  = 0x5,
+    VCM_BELLCORE_DR2 = 0x6,
+    VCM_BELLCORE_DR3 = 0x7,
+    VCM_BELLCORE_DR4 = 0x8,
+    VCM_BELLCORE_DR5 = 0x9,
+    VCM_BELLCORE_MAX = VCM_BELLCORE_DR5,
+    VCM_FLASHONLY_RING = 0xA,
+    VCM_STATION_PRECEDENCE_RING = 0xB,
+    VCM_MAX_RING = 0xC
+} vcm_ring_mode_t;
+
+/**
+ * vcm_ring_duration_t
+ * Enums for specifying normal vs single ring
+ */
+typedef enum {
+    vcm_station_normal_ring = 0x1,
+    vcm_station_single_ring = 0x2
+} vcm_ring_duration_t;
+
+/**
+ * Structure to carry key codec information
+ */
+typedef struct
+{
+  rtp_ptype codec_type;
+
+  /*
+   * NOTE: We keep track of the RTP "PT" field for sending separate from the
+   * one for receiving. This is to support asymmetric payload type values
+   * for a given codec. When we get an offer, we answer with the same payload
+   * type value that the remote offers. If we send an offer and the remote
+   * choses to answer with different value than we offer, we support asymmetric.
+   */
+
+  /* RTP "PT" field we use to send this codec ("remote") */
+  int remote_rtp_pt;
+
+  /* RTP "PT" field we use to receive this codec ("local") */
+  int local_rtp_pt;
+
+  /* Parameters for specific media types */
+  union
+  {
+    struct
+    {
+      int frequency;
+      int packet_size; /* Number of samples in a packet */
+      int channels;
+      int bitrate;     /* Wire bitrate of RTP packet payloads */
+    } audio;
+
+    struct
+    {
+      int width;
+      int height;
+    } video;
+  };
+
+  /* Codec-specific parameters */
+  union
+  {
+    struct {
+        uint16_t mode;
+    } ilbc;
+
+    /* These are outdated, and need to be updated to match the current
+       specification. */
+    struct {
+        uint32_t max_average_bitrate;
+        const char *maxcodedaudiobandwidth;
+        boolean usedtx;
+        boolean stereo;
+        boolean useinbandfec;
+        boolean cbr;
+    } opus;
+  };
+} vcm_payload_info_t;
+
+/**
+ * vcm_vad_t
+ * Enums for Voice Activity Detection
+ */
+typedef enum vcm_vad_t_ {
+    VCM_VAD_OFF = 0,
+    VCM_VAD_ON = 1
+} vcm_vad_t;
+
+/**
+ * vcm_audio_bits_t
+ * Enums for indicating audio path
+ */
+typedef enum vcm_audio_bits_ {
+    VCM_AUDIO_NONE,
+    VCM_AUDIO_HANDSET,
+    VCM_AUDIO_HEADSET,
+    VCM_AUDIO_SPEAKER
+} vcm_audio_bits_t;
+
+/**
+ * vcm_crypto_algorithmID
+ * Crypto parameters for SRTP media
+ */
+typedef enum {
+    VCM_INVLID_ALGORITM_ID = -1,    /* invalid algorithm ID.             */
+    VCM_NO_ENCRYPTION = 0,          /* no encryption                     */
+    VCM_AES_128_COUNTER             /* AES 128 counter mode (32 bits HMAC)*/
+} vcm_crypto_algorithmID;
+
+/**
+ * vcm_mixing_mode_t
+ * Mixing mode for media
+ */
+typedef enum {
+    VCM_NO_MIX,
+    VCM_MIX
+} vcm_mixing_mode_t;
+
+/**
+ * vcm_session_t
+ * Media Session Specification enum
+ */
+typedef enum {
+    PRIMARY_SESSION,
+    MIX_SESSION,
+    NO_SESSION
+} vcm_session_t;
+
+
+/**
+ * vcm_mixing_party_t
+ * Media mix party enum
+ * Indicates the party to be mixed none/local/remote/both/TxBOTH_RxNONE
+ * TxBOTH_RxNONE means that for Tx stream it's both, for Rx stream it's none
+ */
+typedef enum {
+    VCM_PARTY_NONE,
+    VCM_PARTY_LOCAL,
+    VCM_PARTY_REMOTE,
+    VCM_PARTY_BOTH,
+    VCM_PARTY_TxBOTH_RxNONE
+} vcm_mixing_party_t;
+
+/**
+ * media_control_to_encoder_t
+ * Enums for far end control for media
+ * Only Fast Picture Update for video is supported
+ */
+typedef enum {
+    VCM_MEDIA_CONTROL_PICTURE_FAST_UPDATE
+} vcm_media_control_to_encoder_t;
+
+/**
+ * Maximum key and salt must be adjust to the largest possible
+ * supported key.
+ */
+#define VCM_SRTP_MAX_KEY_SIZE  16   /* maximum key in bytes (128 bits) */
+/**
+ * Maximum key and salt must be adjust to the largest possible
+ * supported salt.
+ */
+#define VCM_SRTP_MAX_SALT_SIZE 14   /* maximum salt in bytes (112 bits)*/
+
+/** Key size in bytes for AES128 algorithm */
+#define VCM_AES_128_COUNTER_KEY_SIZE  16
+/** Salt size in bytes for AES128 algorithm */
+#define VCM_AES_128_COUNTER_SALT_SIZE 14
+
+/** Structure to carry crypto key and salt for SRTP streams */
+typedef struct vcm_crypto_key_t_ {
+    cc_uint8_t key_len; /**< key length*/
+    cc_uint8_t key[VCM_SRTP_MAX_KEY_SIZE]; /**< key*/
+    cc_uint8_t salt_len; /**< salt length*/
+    cc_uint8_t salt[VCM_SRTP_MAX_SALT_SIZE]; /**< salt*/
+} vcm_crypto_key_t;
+
+/**
+ *  vcm_videoAttrs_t
+ *  An opaque handle to store and pass video attributes
+ */
+typedef struct vcm_videoAttrs_t_ {
+  void * opaque; /**< pointer to opaque data from application as received from vcm_negotiate_attrs()*/
+} vcm_videoAttrs_t;
+
+/**  A structure carrying audio media specific attributes */
+typedef struct vcm_audioAttrs_t_ {
+  cc_uint16_t packetization_period; /**< ptime value received in SDP */
+  cc_uint16_t max_packetization_period; /**< ptime value received in SDP */
+  cc_int32_t avt_payload_type; /**< RTP payload type for AVT */
+  vcm_vad_t vad; /**< Voice Activity Detection on or off */
+  vcm_mixing_party_t mixing_party; /**< mixing_party */
+  vcm_mixing_mode_t  mixing_mode;  /**< mixing_mode*/
+} vcm_audioAttrs_t;
+
+
+/**
+ *  vcm_mediaAttrs_t
+ *  A structure to contain audio or video media attributes
+ */
+typedef struct vcm_attrs_t_ {
+  cc_boolean         mute;
+  cc_boolean         is_video;
+  vcm_audioAttrs_t audio; /**< audio line attribs */
+  vcm_videoAttrs_t video; /**< Video Atrribs */
+} vcm_mediaAttrs_t;
+
+//Using C++ for gips. This is required for gips.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ *  The initialization of the VCM module
+ *
+ */
+
+void vcmInit();
+
+/**
+ * The unloading of the VCM module
+ */
+void vcmUnload();
+
+
+/**
+ *   Should we remove this from external API
+ *
+ *  @param[in] mcap_id - group identifier to which stream belongs.
+ *  @param[in]     group_id         - group identifier
+ *  @param[in]     stream_id        - stream identifier
+ *  @param[in]     call_handle      - call handle
+ *  @param[in]     port_requested   - requested port.
+ *  @param[in]     listen_ip        - local IP for listening
+ *  @param[in]     is_multicast     - multicast stream?
+ *  @param[in,out] port_allocated   - allocated(reserved) port
+ *
+ *  tbd need to see if we can deprecate this API
+ *
+ *  @return       0 success, ERROR failure.
+ *
+ */
+
+short vcmRxOpen(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t call_handle,
+        cc_uint16_t port_requested,
+        cpr_ip_addr_t *listen_ip,
+        cc_boolean is_multicast,
+        int *port_allocated);
+/*!
+ *  should we remove from external API
+ *
+ *  @param[in]  mcap_id - Media Capability ID
+ *  @param[in]  group_id - group to which stream belongs
+ *  @param[in]  stream_id - stream identifier
+ *  @param[in]  call_handle - call handle
+ *
+ *  @return  zero(0) for success; otherwise, ERROR for failure
+ *
+ */
+
+short vcmTxOpen(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t call_handle);
+
+/*!
+ *  Allocate(Reserve) a receive port.
+ *
+ *  @param[in]  mcap_id - Media Capability ID
+ *  @param[in]  group_id - group identifier to which stream belongs.
+ *  @param[in]  stream_id - stream identifier
+ *  @param[in]  call_handle  - call handle
+ *  @param[in]  port_requested - port requested (if zero -> give any)
+ *  @param[out]  port_allocated - port that was actually allocated.
+ *
+ *  @return    void
+ *
+ */
+
+void vcmRxAllocPort(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t  call_handle,
+        cc_uint16_t port_requested,
+        int *port_allocated);
+
+
+void vcmRxAllocICE(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t  call_handle,
+        const char *peerconnection,
+        uint16_t level,
+        char **default_addr, /* Out */
+        int *default_port, /* Out */
+        char ***candidates, /* Out */
+        int *candidate_ct /* Out */
+);
+
+
+/* Get ICE global parameters (ufrag and pwd)
+ *
+ *  @param[in]  peerconnection - the peerconnection in use
+ *  @param[out] ufragp - where to put the ufrag
+ *  @param[out] pwdp - where to put the pwd
+ *
+ *  @return void
+ */
+void vcmGetIceParams(const char *peerconnection, char **ufragp, char **pwdp);
+
+/* Set remote ICE global parameters.
+ *
+ *  @param[in]  peerconnection - the peerconnection in use
+ *  @param[in]  ufrag - the ufrag
+ *  @param[in]  pwd - the pwd
+ *
+ *  @return 0 success, error failure
+ */
+short vcmSetIceSessionParams(const char *peerconnection, char *ufrag, char *pwd);
+
+/* Set ice candidate for trickle ICE.
+ *
+ *  @param[in]  peerconnection - the peerconnection in use
+ *  @param[in]  icecandidate - the icecandidate
+ *  @param[in]  level - the m line level
+ *
+ *  @return 0 success, error failure
+ */
+short vcmSetIceCandidate(const char *peerconnection, const char *icecandidate, uint16_t level);
+
+/* Set remote ICE media-level parameters.
+ *
+ *  @param[in]  peerconnection - the peerconnection in use
+ *  @param[in]  level - the m-line
+ *  @param[in]  ufrag - the ufrag
+ *  @param[in]  pwd - the pwd
+ *  @param[in]  candidates - the candidates
+ *  @param[in]  candidate_ct - the number of candidates
+ *  @return 0 success, error failure
+ */
+short vcmSetIceMediaParams(const char *peerconnection, int level, char *ufrag, char *pwd,
+                      char **candidates, int candidate_ct);
+
+/* Start ICE checks
+ *  @param[in]  peerconnection - the peerconnection in use
+ *  @param[in]  level - the m-line
+ *  @return 0 success, error failure
+ */
+short vcmStartIceChecks(const char *peerconnection);
+
+
+
+/*
+ * Create a remote stream
+ *
+ *  @param[in] mcap_id - group identifier to which stream belongs.
+ *  @param[in]  peerconnection - the peerconnection in use
+ *  @param[out] pc_stream_id - the id of the allocated stream
+ *
+ *  TODO(ekr@rtfm.com): Revise along with everything else for the
+ *  new stream model.
+ *
+ *  Returns: zero(0) for success; otherwise, ERROR for failure
+ */
+short vcmCreateRemoteStream(
+             cc_mcapid_t mcap_id,
+             const char *peerconnection,
+             int *pc_stream_id);
+
+/*!
+ *  Release the allocated port
+ * @param[in] mcap_id   - media capability id (0 is audio)
+ * @param[in] group_id  - group identifier
+ * @param[in] stream_id - stream identifier
+ * @param[in] call_handle   - call handle
+ * @param[in] port     - port to be released
+ *
+ * @return void
+ */
+void vcmRxReleasePort(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t  call_handle,
+        int port);
+
+/*!
+ *  Start receive stream
+ *  Note: For video calls, for a given call handle there will be
+ *        two media lines and the corresponding group_id/stream_id pair.
+ *        One RTP session is requested from media server for each
+ *        media line(group/stream) i.e. a video call would result in
+ *        two rtp_sessions in our session info list created by two
+ *        calls to vcm_rx/tx with mcap_id of AUDIO and VIDEO respectively.
+ *
+ *  @param[in]    mcap_id     - media type id
+ *  @param[in]    group_id    - group identifier associated with the stream
+ *  @param[in]    stream_id   - id of the stream one per each media line
+ *  @param[in]    call_handle - call handle
+ *  @param[in]    payload     - payload information
+ *  @param[in]    local_addr  - local ip address to use.
+ *  @param[in]    port        - local port (receive)
+ *  @param[in]    algorithmID - crypto alogrithm ID
+ *  @param[in]    rx_key      - rx key used when algorithm ID is encrypting
+ *  @param[in]    attrs       - media attributes
+ *
+ *  @return   zero(0) for success; otherwise, -1 for failure
+ *
+ */
+
+int vcmRxStart(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t  call_handle,
+        const vcm_payload_info_t *payload,
+        cpr_ip_addr_t *local_addr,
+        cc_uint16_t port,
+        vcm_crypto_algorithmID algorithmID,
+        vcm_crypto_key_t *rx_key,
+        vcm_mediaAttrs_t *attrs);
+
+
+/**
+ *  start rx stream
+ *  Same concept as vcmRxStart but for ICE/PeerConnection-based flows
+ *
+ *  @param[in]   mcap_id      - media cap id
+ *  @param[in]   group_id     - group identifier to which the stream belongs
+ *  @param[in]   stream_id    - stream id of the given media type.
+ *  @param[in]   level        - the m-line index
+ *  @param[in]   pc_stream_id - the media stream index (from PC.addStream())
+ *  @param[in]   pc_track_id  - the track within the media stream
+ *  @param[in]   call_handle  - call handle
+ *  @param[in]   peerconnection - the peerconnection in use
+ *  @param[in]   num_payloads  - number of codecs negotiated
+ *  @param[in]   payloads      - list of negotiated codec details
+ *  @param[in]   fingerprint_alg - the DTLS fingerprint algorithm
+ *  @param[in]   fingerprint  - the DTLS fingerprint
+ *  @param[in]   attrs        - media attributes
+ *
+ *  Returns: zero(0) for success; otherwise, ERROR for failure
+ *
+ */
+
+int vcmRxStartICE(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        int level,
+        int pc_stream_id,
+        int pc_track_id,
+        cc_call_handle_t  call_handle,
+        const char *peerconnection,
+        int num_payloads,
+        const vcm_payload_info_t* payloads,
+        const char *fingerprint_alg,
+        const char *fingerprint,
+        vcm_mediaAttrs_t *attrs);
+
+/**
+ *  start tx stream
+ *  Note: For video calls, for a given call handle there will be
+ *        two media lines and the corresponding group_id/stream_id pair.
+ *        One RTP session is requested from media server for each
+ *        media line(group/stream) i.e. a video call would result in
+ *        two rtp_sessions in our session info list created by two
+ *        calls to vcm_rx/tx with mcap_id of AUDIO and VIDEO respectively.
+ *
+ *  @param[in]   mcap_id      - media cap id
+ *  @param[in]   group_id     - group identifier to which the stream belongs
+ *  @param[in]   stream_id    - stream id of the given media type.
+ *  @param[in]   call_handle  - call handle
+ *  @param[in]   payload      - payload information
+ *  @param[in]   tos          - bit marking
+ *  @param[in]   local_addr   - local address
+ *  @param[in]   local_port   - local port
+ *  @param[in]   remote_ip_addr - remote ip address
+ *  @param[in]   remote_port  - remote port
+ *  @param[in]   algorithmID  - crypto alogrithm ID
+ *  @param[in]   tx_key       - tx key used when algorithm ID is encrypting.
+ *  @param[in]   attrs        - media attributes
+ *
+ *  Returns: zero(0) for success; otherwise, ERROR for failure
+ *
+ */
+
+int vcmTxStart(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t  call_handle,
+        const vcm_payload_info_t *payload,
+        short tos,
+        cpr_ip_addr_t *local_addr,
+        cc_uint16_t local_port,
+        cpr_ip_addr_t *remote_ip_addr,
+        cc_uint16_t remote_port,
+        vcm_crypto_algorithmID algorithmID,
+        vcm_crypto_key_t *tx_key,
+        vcm_mediaAttrs_t *attrs);
+
+
+/**
+ *  start tx stream
+ *  Same concept as vcmTxStart but for ICE/PeerConnection-based flowso
+ *
+ *  @param[in]   mcap_id      - media cap id
+ *  @param[in]   group_id     - group identifier to which the stream belongs
+ *  @param[in]   stream_id    - stream id of the given media type.
+ *  @param[in]   level        - the m-line index
+ *  @param[in]   pc_stream_id - the media stream index (from PC.addStream())
+ *  @param[in]   pc_track_id  - the track within the media stream
+ *  @param[in]   call_handle  - call handle
+ *  @param[in]   peerconnection - the peerconnection in use
+ *  @param[in]   payload      - payload information
+ *  @param[in]   tos          - bit marking
+ *  @param[in]   fingerprint_alg - the DTLS fingerprint algorithm
+ *  @param[in]   fingerprint  - the DTLS fingerprint
+ *  @param[in]   attrs        - media attributes
+ *
+ *  Returns: zero(0) for success; otherwise, ERROR for failure
+ *
+ */
+
+  int vcmTxStartICE(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        int level,
+        int pc_stream_id,
+        int pc_track_id,
+        cc_call_handle_t  call_handle,
+        const char *peerconnection,
+        const vcm_payload_info_t *payload,
+        short tos,
+        const char *fingerprint_alg,
+        const char *fingerprint,
+        vcm_mediaAttrs_t *attrs);
+
+
+  short vcmGetDtlsIdentity(const char *peerconnection,
+        char *digest_alg,
+        size_t max_digest_alg_len,
+        char *digest,
+        size_t max_digest_len);
+
+
+  short vcmSetDataChannelParameters(const char *peerconnection,
+        cc_uint16_t streams,
+        int sctp_port,
+        const char* protocol);
+
+/*!
+ *  Close the receive stream.
+ *
+ *  @param[in]    mcap_id - Media Capability ID
+ *  @param[in]    group_id - group identifier that belongs to the stream.
+ *  @param[in]    stream_id - stream id of the given media type.
+ *  @param[in]    call_handle - call handle
+ *
+ *  @return   None
+ *
+ */
+
+void vcmRxClose(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t  call_handle);
+
+/**
+ *  Close the transmit stream
+ *
+ *  @param[in] mcap_id - Media Capability ID
+ *  @param[in] group_id - identifier of the group to which stream belongs
+ *  @param[in] stream_id - stream id of the given media type.
+ *  @param[in] call_handle - call handle
+ *
+ *  @return     void
+ */
+
+void vcmTxClose(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t  call_handle);
+
+/**
+ *  To be Deprecated
+ *  This may be needed to be implemented if the DSP doesn't automatically enable the side tone
+ *  The stack will make a call to this method based on the call state. Provide a stub if this is not needed.
+ *
+ *  @param[in] side_tone - cc_boolean to enable/disable side tone
+ *
+ *  @return void
+ *
+ */
+void vcmEnableSidetone(cc_uint16_t side_tone);
+
+/**
+ *  Start a tone (continuous)
+ *
+ *  Parameters:
+ *  @param[in] tone       - tone type
+ *  @param[in] alert_info - alertinfo header
+ *  @param[in] call_handle- call handle
+ *  @param[in] group_id - identifier of the group to which stream belongs
+ *  @param[in] stream_id   - stream identifier.
+ *  @param[in] direction  - network, speaker, both
+ *
+ *  @return void
+ *
+ */
+
+void vcmToneStart(vcm_tones_t tone,
+        short alert_info,
+        cc_call_handle_t  call_handle,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_uint16_t direction);
+
+/**
+ * Plays a short tone. uses the open audio path.
+ * If no audio path is open, plays on speaker.
+ *
+ * @param[in] tone       - tone type
+ * @param[in] alert_info - alertinfo header
+ * @param[in] call_handle- call handle
+ * @param[in] group_id - identifier of the group to which stream belongs
+ * @param[in] stream_id   - stream identifier.
+ * @param[in] direction  - network, speaker, both
+ *
+ * @return void
+ */
+
+void vcmToneStartWithSpeakerAsBackup(vcm_tones_t tone,
+        short alert_info,
+        cc_call_handle_t call_handle,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_uint16_t direction);
+
+/**
+ *  Stop the tone being played.
+ *
+ *  Description: Stop the tone being played currently
+ *
+ *
+ * @param[in] tone - tone to be stopeed
+ * @param[in] group_id - associated stream's group
+ * @param[in] stream_id - associated stream id
+ * @param[in] call_handle - the context (call) for this tone.
+ *
+ * @return void
+ *
+ */
+
+void vcmToneStop(vcm_tones_t tone,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t call_handle);
+
+/**
+ *  start/stop ringing
+ *
+ *  @param[in] ringMode   - VCM ring mode (ON/OFF)
+ *  @param[in] once       - type of ring - continuous or one shot.
+ *  @param[in] alert_info - header specified ring mode.
+ *  @param[in] line       - the line on which to start/stop ringing
+ *
+ *  @return    void
+ */
+
+void vcmControlRinger(vcm_ring_mode_t ringMode,
+        short once,
+        cc_boolean alert_info,
+        int line,
+        cc_callid_t call_id);
+
+
+/**
+ * Get current list of audio codec that could be used
+ * @param request_type -
+ * The request_type should be VCM_DECODEONLY/ENCODEONLY/FULLDUPLEX/IGNORE
+ *
+ * @return A bit mask should be returned that specifies the list of the audio
+ * codecs. The bit mask should conform to the defines in this file.
+ * #define VCM_RESOURCE_G711     0x00000001
+ * #define VCM_RESOURCE_G729A    0x00000002
+ * ....
+ */
+
+int vcmGetAudioCodecList(int request_type);
+
+/**
+ * Get current list of video codec that could be used
+ * @param request_type -
+ * The request_type should be VCM_DECODEONLY/ENCODEONLY/FULLDUPLEX/IGNORE
+ *
+ * @return A bit mask should be returned that specifies the list of the audio
+ * codecs. The bit mask should conform to the defines in this file.
+ * #define VCM_RESOURCE_G711     0x00000001
+ * #define VCM_RESOURCE_G729A    0x00000002
+ * ....
+ */
+
+int vcmGetVideoCodecList(int request_type);
+
+/**
+ * Get max supported H.264 video packetization mode.
+ * @return maximum supported video packetization mode for H.264. Value returned
+ * must be 0 or 1. Value 2 is not supported yet.
+ */
+int vcmGetVideoMaxSupportedPacketizationMode();
+
+/**
+ * Get the rx/tx stream statistics associated with the call.
+ * The rx/tx stats are defined as comma seperated string as follows.
+ * Rx_stats:
+ *   snprintf(rx_stats, CC_KFACTOR_STAT_LEN,
+ *               "Dur=%d,Pkt=%d,Oct=%d,LatePkt=%d,LostPkt=%d,AvgJit=%d,VQMetrics=\"%s\"",
+ *               duration, numberOfPackageReceived, numberOfByteReceived, numberOfLatePackage, numberOfPackageLost, averageJitter, qualityMatrics);
+ * Tx_stats:
+ *   snprintf(tx_stats, CC_KFACTOR_STAT_LEN, "Dur=%d,Pkt=%d,Oct=%d",
+ *               duration, numberOfPackageSent, numberOfByteSend);
+ *
+ *Example:
+ *
+ *   vcm_rtp_get_stats : rx_stats:Dur=1,Pkt=90,Oct=15480,LatePkt=0,LostPkt=0,AvgJit=0,VQMetrics="MLQK=0.0;MLQKav=0.0;MLQKmn=0.0;MLQKmx=0.0;MLQKvr=0.95;CCR=0.0;ICR=0.0;ICRmx=0.0;CS=0;SCS=0"
+ *   vcm_rtp_get_stats : tx_stats:Dur=1,Pkt=92,Oct=14720
+ *   Where the duration can be calculated, the numberOfLatePackage set to 0.
+ *
+ * @param[in]  mcap_id  - media type (audio/video)
+ * @param[in]  group_id - group id of the stream
+ * @param[in]  stream_id - stram id of the stream
+ * @param[in]  call_handle - call handle
+ * @param[out] rx_stats - ptr to the rx field in the stats struct, see above.
+ * @param[out] tx_stats - ptr to the tx field in the stats struct, see above.
+ *
+ */
+
+int vcmGetRtpStats(cc_mcapid_t mcap_id,
+        cc_groupid_t group_id,
+        cc_streamid_t stream_id,
+        cc_call_handle_t call_handle,
+        char *rx_stats,
+        char *tx_stats);
+
+/**
+ *
+ * The wlan interface puts into unique situation where call control
+ * has to allocate the worst case bandwith before creating a
+ * inbound or outbound call. The function call will interface through
+ * media API into wlan to get the call bandwidth. The function
+ * return is asynchronous and will block till the return media
+ * callback signals to continue the execution.
+ *
+ * @note If not using WLAN interface simply return true
+ *
+ * @return true if the bandwidth can be allocated else false.
+ */
+
+cc_boolean vcmAllocateBandwidth(cc_call_handle_t call_handle, int sessions);
+
+/**
+ *
+ * Free the bandwidth allocated for this call
+ * using the vcmAllocateBandwidth API
+ *
+ * @note  If not using WLAN provide a stub
+ */
+
+void vcmRemoveBandwidth(cc_call_handle_t call_handle);
+
+/**
+ * @brief vcmActivateWlan
+ *
+ * Free the bandwidth allocated for this call
+ * using the vcmAllocateBandwidth API
+ *
+ * @note If not using WLAN provide a stub
+ */
+
+void vcmActivateWlan(cc_boolean is_active);
+
+/**
+ *  free the media pointer allocated in vcm_negotiate_attrs method
+ *
+ *  @param ptr - pointer to be freed
+ *
+ *  @return  void
+ */
+void vcmFreeMediaPtr(void *ptr);
+
+/**
+ *  MEDIA control received from far end on signaling path
+ *
+ *  @param call_handle - call handle of the call
+ *  @param to_encoder - the control request received
+ *        Only FAST_PICTURE_UPDATE is supported
+ *
+ *  @return  void
+ *
+ */
+
+void vcmMediaControl(cc_call_handle_t call_handle, vcm_media_control_to_encoder_t to_encoder);
+
+/**
+ *  specifies DSCP marking for RTCP streams
+ *
+ *  @param group_id - group id of the stream
+ *  @param dscp - the DSCP value to be used
+ *
+ *  @return  void
+ */
+
+void vcmSetRtcpDscp(cc_groupid_t group_id, int dscp);
+
+/**
+ * Verify if the SDP attributes for the requested video codec are acceptable
+ *
+ * This method is called for video codecs only. This method should parse the
+ * Video SDP attributes using the SDP helper API and verify if received
+ * attributes are acceptable. If the attributes are acceptable any attribute
+ * values if needed by vcmTxStart method should be bundled in the desired
+ * structure and its pointer should be returned in rccappptr. This opaque
+ * pointer shall be provided again when vcmTxStart is invoked.
+ *
+ * @param [in] media_type - codec for which we are negotiating
+ * @param [in] sdp_p - opaque SDP pointer to be used via SDP helper APIs
+ * @param [in] level - Parameter to be used with SDP helper APIs
+ * @param [out] rcapptr - variable to return the allocated attrib structure
+ *
+ * @return cc_boolean - true if attributes are accepted false otherwise
+ */
+
+cc_boolean vcmCheckAttribs(cc_uint32_t media_type, void *sdp_p, int level, void **rcapptr);
+
+/**
+ * Add Video attributes in the offer/answer SDP
+ *
+ * This method is called for video codecs only. This method should populate the
+ * Video SDP attributes using the SDP helper API
+ *
+ * @param [in] sdp_p - opaque SDP pointer to be used via SDP helper APIs
+ * @param [in] level - Parameter to be used with SDP helper APIs
+ * @param [in] media_type - codec for which the SDP attributes are to be populated
+ * @param [in] payload_number - RTP payload type used for the SDP
+ * @param [in] isOffer - cc_boolean indicating we are encoding an offer or an aswer
+ *
+ * @return void
+ */
+
+
+void vcmPopulateAttribs(void *sdp_p, int level, cc_uint32_t media_type,
+                          cc_uint16_t payload_number, cc_boolean isOffer);
+
+
+/**
+ * Send a DTMF digit
+ *
+ * This method is called for sending a DTMF tone for the specified duration
+ *
+ * @param [in] digit - the DTMF digit that needs to be played out.
+ * @param [in] duration - duration of the tone
+ * @param [in] direction - direction in which the tone needs to be played.
+ *
+ * @return void
+ */
+int vcmDtmfBurst(int digit, int duration, int direction);
+
+/**
+ * vcmGetILBCMode
+ *
+ * This method should return the mode that needs to be used in
+ * SDP
+ * @return int
+ */
+int vcmGetILBCMode();
+
+//Using C++ for gips. This is the end of extern "C" above.
+#ifdef __cplusplus
+}
+#endif
+
+
+
+
+#endif /* _VCM_H_ */
diff --git a/libs/sipcc/include/xml_parser_defines.h b/libs/sipcc/include/xml_parser_defines.h
new file mode 100644 (file)
index 0000000..114b168
--- /dev/null
@@ -0,0 +1,636 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef XML_PARSER_DEFINES_H
+#define XML_PARSER_DEFINES_H
+
+#include "sll_lite.h"
+#include "cc_constants.h"
+
+
+/**
+ * In general, when a parser constructs a xml string,
+ * it should translate the enum to cooresponding string
+ * value that is defined in the accompanied xsd files.
+ */
+/**
+ * Define the state values
+ */
+typedef enum {
+    XML_STATE_PARTIAL = 0, //Encode as "partial"
+    XML_STATE_FULL         //"full"
+} xml_state_t;
+
+/**
+ * Define the call orientation
+ */
+typedef enum {
+    XML_CALL_ORIENTATION_UNSPECIFIED = 0,
+    XML_CALL_ORIENTATION_TO,
+    XML_CALL_ORIENTATION_FROM
+} xml_call_orientation_t;
+
+/**
+ * Define the call lock status
+ */
+typedef enum {
+    XML_CALLLOCK_UNLOCKED = 0,
+    XML_CALLLOCK_LOCKED,
+    XML_CALLLOCK_REMOTE_LOCKED
+} xml_calllock_t;
+
+/**
+ * Define the direction values
+ */
+typedef enum {
+    XML_DIRECTION_INITIATOR = 0,
+    XML_DIRECTION_RECIPIENT
+} xml_direction_t;
+
+/**
+ * Define the event values
+ */
+typedef enum {
+    XML_EVENT_CANCELLED = 0,
+    XML_EVENT_REJECTED,
+    XML_EVENT_REPLACED,
+    XML_EVENT_LOCAL_BYE,
+    XML_EVENT_REMOTE_BYE,
+    XML_EVENT_ERROR,
+    XML_EVENT_TIMEOUT
+} xml_event_t;
+
+/**
+ * Define the yes or no values
+ */
+typedef enum {
+    XML_NO = 0,
+    XML_YES,
+    XML_NONEAPPLICABLE //"na"
+} xml_yes_no_t;
+
+/**
+ * Define the on or off value
+ */
+typedef enum {
+    XML_OFF = 0,
+    XML_ON
+} xml_on_off_t;
+
+/**
+ * Define the true or false values
+ */
+typedef enum {
+    XML_FALSE = 0,
+    XML_TRUE
+} xml_true_false_t;
+
+/**
+ * Define the line key events
+ */
+typedef enum {
+    XML_LINE_KEY_EVENT_LINE = 0,
+    XML_LINE_KEY_EVENT_SPEEDDIAL
+} xml_line_key_event_t;
+
+/**
+ * Define the persist types
+ */
+typedef enum {
+    XML_PERSIST_TYPE_ONE_SHOT = 0,
+    XML_PERSIST_TYPE_PERSIST,
+    XML_PERSIST_TYPE_SINGLE_NOTIFY
+} xml_persist_type_t;
+
+/**
+ * Define the soft key invoke type
+ */
+typedef enum {
+    XML_SKEY_INVOKE_EXPLICIT = 0,
+    XML_SKEY_NVOKE_IMPLICIT
+} xml_skey_invoke_t;
+
+/**
+ * Define the soft key event data
+ */
+typedef enum {
+    XML_SKEY_EVENT_UNDEFINED = 0,
+    XML_SKEY_EVENT_REDIAL,
+    XML_SKEY_EVENT_NEWCALL,
+    XML_SKEY_EVENT_HOLD,
+    XML_SKEY_EVENT_TRANSFER,
+    XML_SKEY_EVENT_CFWDALL, //5
+    XML_SKEY_EVENT_CFWDBUSY,
+    XML_SKEY_EVENT_CFWDNOANSWER,
+    XML_SKEY_EVENT_BACKSPACE,
+    XML_SKEY_EVENT_ENDCALL,
+    XML_SKEY_EVENT_RESUME, //10
+    XML_SKEY_EVENT_ANSWER,
+    XML_SKEY_EVENT_INFO,
+    XML_SKEY_EVENT_CONFERENCE,
+    XML_SKEY_EVENT_JION, //15
+    XML_SKEY_EVENT_REMVOVE_LAST_CONF_PARTICIPANT,
+    XML_SKEY_EVENT_DIRECT_XFER,
+    XML_SKEY_EVENT_SELECT, //25
+    XML_SKEY_EVENT_TRANSFER_TO_VOICE_MAIL,
+    XML_SKEY_EVENT_SAC,
+    XML_SKEY_EVENT_UNSELECT, //35
+    XML_SKEY_EVENT_CANCEL,
+    XML_SKEY_EVENT_COPNFERENCE_DETAILS,//40
+    XML_SKEY_EVENT_TRASFMG = 65,
+    XML_SKEY_EVENT_INTRCPT,
+    XML_SKEY_EVENT_SETWTCH,
+    XML_SKEY_EVENT_TRNSFVM,
+    XML_SKEY_EVENT_TRNSFAS
+} xml_skey_event_code_t;
+
+/**
+ * Define the map for station sequence mapping
+ */
+typedef enum {
+    XML_STATION_SEQ_FIRST = 0,
+    XML_STATION_SEQ_MORE,
+    XML_STATION_SEQ_LAST
+} xml_stataionseq_t;
+
+/**
+ * Define the hold reasons
+ */
+typedef enum {
+    XML_HOLD_REASON_NONE = 0,
+    XML_HOLD_REASON_TRANSFER,
+    XML_HOLD_REASON_CONFERENCE,
+    XML_HOLD_REASON_INTERNAL
+} xml_hold_reason_t;
+
+/**
+ * Define the lamp status
+ */
+typedef enum {
+    XML_LAMP_STATE_OFF = 0,
+    XML_LAMP_STATE_ON,
+    XML_LAMP_STATE_BLINK,
+    XML_LAMP_STATE_FLASH
+} xml_lamp_state_t;
+
+/**
+ * Define the lamp type
+ */
+typedef enum {
+    XML_LAMP_TYPE_LINE = 1,
+    XML_LAMP_TYPE_VOICE_MAIL
+} xml_lamp_type_t;
+
+/**
+ * Define the image down load method
+ */
+typedef enum {
+    XML_IMAGE_DOWNLOAD_METHOD_TFTP = 1,
+    XML_IMAGE_DOWNLAOD_METHOD_HTTP,
+    XML_IMAGE_DOWNLOAD_METHOD_PPID
+} xml_image_dl_method_t;
+
+/**
+ * Define the image download failure reason
+ */
+typedef enum {
+    XML_IMAGE_DOWNLOAD_FAILURE_REASON_DISKFULL = 1,
+    XML_IMAGE_DOWNLOAD_FAILURE_REASON_IMAGE_NOT_AVAILABLE,
+    XML_IMAGE_DOWNLOAD_FAILURE_REASON_ACCESS_VIOLATION
+} xml_image_dl_failure_reason_t;
+
+typedef signed long         xml_signed32;
+typedef unsigned long       xml_unsigned32;
+typedef unsigned short      xml_unsigned16;
+typedef unsigned char       xml_unsigned8;
+
+// start of copy from ccsip_eventbodies.h
+typedef struct State {
+       xml_signed32            event;
+       xml_signed32            code;
+       xml_signed32            state;
+} State;
+
+typedef struct Replaces {
+       char                    call_id[128];
+       char                    local_tag[64];
+       char                    remote_tag[64];
+} Replaces;
+
+typedef struct RefferedBy {
+       char                    display_name[64];
+       char                    uri[64];
+} RefferedBy;
+
+typedef struct RouteSet {
+       char                    hop[5][16];
+} RouteSet;
+
+typedef struct Identity {
+       char                    display_name[64];
+       char                    uri[64];
+} Identity;
+
+typedef struct Param {
+       char                    pname[32];
+       char                    pval[32];
+} Param;
+
+typedef struct Target {
+       Param   param[4];
+       char                    uri[64];
+} Target;
+
+typedef struct SessionDescription {
+       char                    type[32];
+} SessionDescription;
+
+typedef struct Participant {
+       Identity        identity;
+       Target  target;
+       SessionDescription      session_description;
+       xml_unsigned16          cseq;
+} Participant;
+
+typedef struct primCall {
+       char                    call_id[128];
+       char                    local_tag[64];
+       char                    remote_tag[64];
+       xml_signed32            h_reason;
+} primCall;
+
+typedef struct callFeature {
+    char            cfwdall_set[128];
+    char            cfwdall_clear[128];
+} callFeature;
+
+typedef struct Stream {
+       char                    reverse[16];
+} Stream;
+
+typedef struct Regex {
+       char                    regexData[32];
+       char                    tag[32];
+       char                    pre[32];
+} Regex;
+
+typedef struct Pattern {
+       xml_signed32            flush;
+       Regex   regex;
+       xml_signed32            persist;
+       xml_unsigned32          interdigittimer;
+       xml_unsigned32          criticaldigittimer;
+       xml_unsigned32          extradigittimer;
+       xml_unsigned16          longhold;
+       xml_unsigned8           longrepeat;
+       xml_unsigned8           nopartial;
+       char                    enterkey[8];
+} Pattern;
+
+typedef struct KPMLRequest {
+       Stream  stream;
+       Pattern pattern;
+       char                    version[16];
+} KPMLRequest;
+
+typedef struct KPMLResponse {
+       char                    version[16];
+       char                    code[16];
+       char                    text[16];
+       xml_unsigned8           suppressed;
+       char                    forced_flush[16];
+       char                    digits[16];
+       char                    tag[16];
+} KPMLResponse;
+
+typedef struct dialogID {
+       char                    callid[128];
+       char                    localtag[64];
+       char                    remotetag[64];
+} dialogID;
+
+typedef struct consultDialogID {
+       char                    callid[128];
+       char                    localtag[64];
+       char                    remotetag[64];
+} consultDialogID;
+
+typedef struct joindialogID {
+       char                    callid[128];
+       char                    localtag[64];
+       char                    remotetag[64];
+} joindialogID;
+
+typedef struct reg_contact_t {
+       char                    Register[16];
+       char                    Unregister[16];
+       xml_unsigned32          line;
+       xml_unsigned32          low;
+       xml_unsigned32          high;
+       xml_signed32            all;
+} reg_contact_t;
+
+
+typedef struct remotecc {
+       char                    status[16];
+} remotecc;
+
+typedef struct combine {
+       xml_unsigned16          max_bodies;
+       remotecc        remotecc;
+       char                    service_control[16];
+} combine;
+
+typedef struct dialog {
+       char                    usage[64];
+       char                    unot[16];
+       char                    sub[16];
+} dialog;
+
+typedef struct presence {
+       char                    usage[64];
+       char                    unot[16];
+       char                    sub[16];
+} presence;
+
+typedef struct voice_msg_t {
+       xml_signed32            newCount;
+       xml_signed32            oldCount;
+} voice_msg_t;
+
+typedef struct voice_msg_hp_t {
+       xml_signed32            newCount;
+       xml_signed32            oldCount;
+} voice_msg_hp_t;
+
+typedef struct fax_msg_t {
+       xml_signed32            newCount;
+       xml_signed32            oldCount;
+} fax_msg_t;
+
+typedef struct fax_msg_hp_t {
+       xml_signed32            newCount;
+       xml_signed32            oldCount;
+} fax_msg_hp_t;
+
+typedef struct emwi_t {
+       voice_msg_t     voice_msg;
+       voice_msg_hp_t  voice_msg_hp;
+       fax_msg_t       fax_msg;
+       fax_msg_hp_t    fax_msg_hp;
+} emwi_t;
+
+typedef struct cfwdallupdate {
+       char                    fwdAddress[256];
+} cfwdallupdate;
+
+typedef struct Contact_t {
+       xml_unsigned32          line;
+       xml_unsigned32          high;
+       xml_unsigned32          low;
+       xml_signed32            all;
+       xml_signed32            mwi;
+       emwi_t  emwi;
+       cfwdallupdate   cfwdallupdate;
+} Contact_t;
+
+typedef struct dialog_t {
+       char                    usage[64];
+       char                    unot[12];
+       char                    sub[12];
+} dialog_t;
+
+typedef struct presence_t {
+       char                    usage[64];
+       char                    unot[12];
+       char                    sub[12];
+} presence_t;
+
+typedef struct options_ans_t {
+       combine combine;
+       dialog_t        dialog;
+       presence_t      presence;
+} options_ans_t;
+
+typedef struct PersonStatusStruct {
+       char                    basic[32];
+} PersonStatusStruct;
+
+typedef struct ActivitiesStruct {
+       char                    alerting[12];
+       char                    onThePhone[12];
+       char                    busy[12];
+       char                    away[12];
+       char                    meeting[12];
+} ActivitiesStruct;
+
+typedef struct PersonStruct {
+       char                    id[256];
+       PersonStatusStruct      personStatus;
+       ActivitiesStruct        activities;
+} PersonStruct;
+
+typedef struct StatusStruct {
+       char                    basic[32];
+       ActivitiesStruct        activities;
+} StatusStruct;
+
+typedef struct TupleStruct {
+       char                    id[256];
+       StatusStruct    status;
+       char                    contact[1][256];
+       char                    note[1][1024];
+} TupleStruct;
+
+typedef struct PresenceRPIDStruct {
+       char                    entity[256];
+       PersonStruct    person;
+       TupleStruct     tuple[1];
+       char                    note[5][1024];
+} PresenceRPIDStruct;
+
+typedef struct sipProfile {
+       xml_unsigned16          kpml_val;
+} sipProfile;
+
+typedef struct ConfigApp_req_data_t {
+       sipProfile      sip_profile;
+} ConfigApp_req_data_t;
+
+typedef struct to_encoder_t {
+       xml_unsigned32          picture_fast_update;
+} to_encoder_t;
+
+typedef struct vc_primivite_t {
+       to_encoder_t    to_encoder;
+       char                    stream_id[128];
+} vc_primivite_t;
+
+typedef struct Media_Control_t {
+       vc_primivite_t  vc_primitive;
+       char                    general_error[128];
+} Media_Control_t;
+
+// end of copy from ccsip_eventbodies.h
+
+typedef struct Presence_ext_t_ {
+    PresenceRPIDStruct presence_body;
+/*
+ * Some of the tags' mere presence in the rpid document has a meaning. These tags
+ * may not contain any value between starting and ending tag. So we need a way to
+ * indicate the presence of a tag. We will use the following boolean memeber fields.
+ */
+    boolean onThePhone;
+    boolean busy;
+    boolean away;
+    boolean meeting;
+    boolean alerting;
+} Presence_ext_t;
+
+
+typedef enum {
+    EVENT_DATA_INVALID = 0,
+    EVENT_DATA_KPML_REQUEST,
+    EVENT_DATA_KPML_RESPONSE,
+    EVENT_DATA_REMOTECC_REQUEST,
+    EVENT_DATA_PRESENCE,
+    EVENT_DATA_DIALOG,
+    EVENT_DATA_RAW,
+    EVENT_DATA_CONFIGAPP_REQUEST,
+    EVENT_DATA_MEDIA_INFO
+} ccsip_event_data_type_e;
+
+typedef struct {
+    char    *data;
+    uint32_t length;
+} raw_data_t;
+
+typedef struct {
+    Media_Control_t media_control;
+    uint32_t   picture_fast_update;
+} media_control_ext_t;
+
+#define TAG_LENGTH  16
+typedef struct {
+    char current_method[TAG_LENGTH];
+    char hookstate[TAG_LENGTH];
+    char presence[TAG_LENGTH];
+} Options_ind_t;
+
+
+
+typedef struct rcc_response_t {
+       xml_unsigned16          code;
+       char                    reason[128];
+       xml_unsigned32          applicationid;
+       xml_unsigned32          transactionid;
+       xml_signed32            stationsequence;
+       xml_unsigned16          displaypriority;
+       xml_unsigned16          appinstance;
+       xml_unsigned16          linenumber;
+       xml_unsigned32          routingid;
+       xml_unsigned32          confid;
+       char                    callID[128];
+       options_ans_t   options_ind;
+} rcc_response_t;
+
+typedef enum {
+    RCC_NULL_REQ = 0,
+    RCC_INITCALL_REQ = 1,
+    RCC_MONITORCALL_REQ,
+    RCC_DIALCALL_REQ,
+    RCC_DIALDTMF_REQ,
+    RCC_ANSCALL_REQ,
+    RCC_DISCCALL_REQ,
+    RCC_XFERSETUP_REQ,
+    RCC_XFERCOMPLETE_REQ,
+    RCC_CONFSETUP_REQ,
+    RCC_CONFCOMPLETE_REQ,
+    RCC_HOLD_REQ,
+    RCC_HOLDRETRIEVE_REQ,
+    RCC_DATAPASSTHROUGH_REQ,
+    RCC_CFWDALL_REQ,
+    RCC_LINEKEY_EVT,
+    RCC_STATUS_UPDATE_REQ,
+    RCC_SET_IDLE_STATUS_PROMPT_REQ,
+    RCC_PLAY_TONE_REQ,
+    RCC_STOP_TONE_REQ,
+    RCC_CALL_SELECT_REQ,
+    RCC_SOFTKEY_EVT,
+    RCC_LINE_RINGER_SET_REQ,
+    RCC_HOLD_REVERSION_REQ,
+    RCC_LAMP_CONTROL_REQ,
+    RCC_LINEKEY_UPDATE,
+    RCC_BULKREGISTER_REQ,
+    RCC_OPTIONS_IND,
+    RCC_BULK_UPDATE,
+    RCC_CALL_JOIN_REQ,
+    RCC_NOTIFY_REQ,
+    RCC_MONITOR_UPDATE_REQ,
+    RCC_MAX_REQ
+} rcc_request_type_t;
+
+typedef struct rcc_softkey_event_msg_t {
+       xml_signed32            softkeyevent;
+       dialogID        dialogid;
+       xml_unsigned16          linenumber;
+       xml_unsigned16          participantnum;
+       dialogID        consultdialogid;
+       xml_unsigned8           state;
+       dialogID        joindialogid;
+       //eventData     eventdata;
+       char                    userdata[32];
+       xml_unsigned16          soktkeyid;
+       xml_unsigned16          applicationid;
+} rcc_softkey_event_msg_t;
+
+
+typedef struct RCC_req_data {
+       rcc_softkey_event_msg_t rcc_softkey_event_msg;
+} RCC_req_data;
+
+typedef struct rcc_int_t_ {
+    RCC_req_data rcc_int;
+    // User added fields
+    xml_unsigned8 iterations;
+    cc_lineid_t line;
+    cc_callid_t gsm_id;
+    cc_callid_t consult_gsm_id;
+    cc_callid_t join_gsm_id;
+    rcc_request_type_t rcc_request_type;
+} RCC_data;
+
+
+// Data for event generation
+typedef struct ccsip_event_data_t_ {
+    struct ccsip_event_data_t_ *next;
+    ccsip_event_data_type_e type;
+    union {
+        KPMLResponse   kpml_response;
+        KPMLRequest    kpml_request;
+        RCC_data       remotecc_data;
+        rcc_response_t remotecc_data_response;
+        Options_ind_t  options_ind;
+        Presence_ext_t presence_rpid;
+        raw_data_t     raw_data; // used for cmxml and other body types
+        ConfigApp_req_data_t configapp_data;
+        media_control_ext_t  media_control_data;
+    } u;
+} ccsip_event_data_t;
+
+
+/**
+ * Request to allocate memory for external xml parser
+ * @param [in] size ofrequested memory
+ * @return pointer to memory allocated.
+ */
+void *ccAllocXML(cc_size_t size);
+
+/**
+ * Free xml memory
+ * @param [in] mem - memory to free
+ * @return void
+ */
+void ccFreeXML(void *mem);
+#endif
diff --git a/libs/sipcc/plat/common/dns_utils.c b/libs/sipcc/plat/common/dns_utils.c
new file mode 100644 (file)
index 0000000..b6fdeca
--- /dev/null
@@ -0,0 +1,165 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cpr_ipc.h"
+#include "cpr_types.h"
+
+#ifndef SIP_OS_WINDOWS
+#include "netdb.h"
+#include "arpa/inet.h"
+#endif
+
+#include "dns_utils.h"
+#include "phone_debug.h"
+#include "util_string.h"
+
+#ifdef SIP_OS_WINDOWS
+cc_int32_t
+dnsGetHostByName (const char *hname,
+                   cpr_ip_addr_t *ipaddr_ptr,
+                   cc_int32_t timeout,
+                   cc_int32_t retries)
+{
+       return DNS_ERR_NOHOST;
+}
+#else
+/*
+ *  dnsGetHostByName
+ *
+ *  DESCRIPTION: Perform an DNS lookup of the name specified
+ *
+ *  RETURNS: returns 0 for success or DNS_ERR_NOHOST
+ *
+ */
+cc_int32_t
+dnsGetHostByName (const char *hname,
+                   cpr_ip_addr_t *ipaddr_ptr,
+                   cc_int32_t timeout,
+                   cc_int32_t retries)
+{
+    uint32_t ip_address;
+    struct hostent *dns_response;
+#ifdef IPV6_STACK_ENABLED
+    uint32_t err;
+    uint16_t   ip_valid;
+    char     ip_addr[MAX_IPADDR_STR_LEN];
+    struct addrinfo hints, *result;
+    struct sockaddr sa;
+#endif
+
+#ifdef IPV6_STACK_ENABLED
+    /* Check if the IPv6 address */
+    ip_valid = cpr_inet_pton(AF_INET6, hname, ip_addr);
+
+    if (ip_valid) {
+        ipaddr_ptr->type = CPR_IP_ADDR_IPV6;
+        cpr_memcopy(ipaddr_ptr->u.ip6.addr.base8, ip_addr, 16);
+        return(0);
+    }
+#endif
+
+    ip_address = inet_addr(hname);
+    if (ip_address != INADDR_NONE) {
+        ipaddr_ptr->u.ip4 = ip_address;
+        ipaddr_ptr->type = CPR_IP_ADDR_IPV4;
+        return 0;
+    } else {
+
+#ifdef IPV6_STACK_ENABLED
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_family = PF_INET6;
+        err = getaddrinfo(hname, NULL, &hints, &result);
+
+        if (err) {
+            return DNS_ERR_NOHOST;
+
+        }
+
+        while (result) {
+            sa = result->ai_addr;
+            if (sa->family == AF_INET) {
+                ipaddr_ptr->type = CPR_IP_ADDR_IPV4;
+                ip_addr->u.ip4 = ((cpr_sockaddr_in_t *)from)->sin_addr.s_addr;
+
+            } else if (sa->family == AF_INET6) {
+
+                //Todo IPv6: Add getaddrinfo() functioncall to get the IPv6 address.
+                ipaddr_ptr->type = CPR_IP_ADDR_IPV6;
+                ip_addr->u.ip6 = ((cpr_sockaddr_in6_t *)sa)->sin6_addr;
+            }
+        }
+
+        freeaddrinfo(result);
+#endif
+
+        dns_response = gethostbyname(hname);
+        if (dns_response) {
+            ipaddr_ptr->u.ip4 = *(uint32_t *) dns_response->h_addr_list[0];
+            ipaddr_ptr->type = CPR_IP_ADDR_IPV4;
+            return 0;
+        }
+
+
+    }
+    return DNS_ERR_NOHOST;
+}
+
+#endif
+
+#ifdef SIP_OS_WINDOWS
+
+cc_int32_t
+dnsGetHostBySRV (cc_int8_t *service,
+                  cc_int8_t *protocol,
+                  cc_int8_t *domain,
+                  cpr_ip_addr_t *ipaddr_ptr,
+                  cc_uint16_t *port,
+                  cc_int32_t timeout,
+                  cc_int32_t retries,
+                  srv_handle_t *psrv_handle)
+{
+       return DNS_ERR_NOHOST;
+}
+
+#else
+/*
+ *  dnsGetHostBySRV
+ *
+ *  DESCRIPTION: This function calls the dns api to
+ *  perform a dns srv query
+ *
+ *  RETURNS: returns 0 for success or
+ *           DNS_ERR_NOHOST, DNS_ERR_HOST_UNAVAIL
+ *
+ */
+cc_int32_t
+dnsGetHostBySRV (cc_int8_t *service,
+                  cc_int8_t *protocol,
+                  cc_int8_t *domain,
+                  cpr_ip_addr_t *ipaddr_ptr,
+                  cc_uint16_t *port,
+                  cc_int32_t timeout,
+                  cc_int32_t retries,
+                  srv_handle_t *psrv_handle)
+{
+    uint32_t ip_address;
+
+
+    ip_address = inet_addr(domain);
+    if (ip_address != INADDR_NONE) {
+        ipaddr_ptr->u.ip4 = ip_address;
+        ipaddr_ptr->type = CPR_IP_ADDR_IPV4;
+        return 0;
+    }
+
+    return DNS_ERR_NOBUF; /* not yet implemented */
+}
+#endif
+
+void
+dnsFreeSrvHandle (srv_handle_t srv_handle)
+{
+  // this function does nothing
+  //  srv_handle = NULL;
+}
diff --git a/libs/sipcc/plat/common/plat_debug.h b/libs/sipcc/plat/common/plat_debug.h
new file mode 100644 (file)
index 0000000..e77b47f
--- /dev/null
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __PLAT_DEBUG_H__
+#define __PLAT_DEBUG_H__
+#include "cc_constants.h"
+#include "debug.h"
+
+typedef cc_int32_t (*ci_callback)(cc_int32_t argc, const char *argv[]);
+typedef struct ci_cmd_block
+{
+    const char           *cmd;
+    ci_callback          func;
+    struct ci_cmd_block  *next;
+} ci_cmd_block_t;
+
+void debug_bind_keyword(const char *cmd, cc_int32_t *flag_ptr);
+void bind_debug_func_keyword(const char *cmd, debug_callback func);
+void bind_show_keyword(const char *cmd, show_callback func);
+void bind_show_tech_keyword(const char *cmd, show_callback func,
+                                    cc_boolean show_tech);
+void bind_clear_keyword(const char *cmd, clear_callback func);
+void ci_bind_cmd(const char *cmd, ci_callback func, ci_cmd_block_t *blk);
+
+#endif
diff --git a/libs/sipcc/plat/common/tnp_blf.h b/libs/sipcc/plat/common/tnp_blf.h
new file mode 100755 (executable)
index 0000000..7e279ed
--- /dev/null
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _TNP_BLF_H
+#define _TNP_BLF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "sessionConstants.h"
+
+void blf_notification(int request_id, int status, int app_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libs/sipcc/plat/csf2g/model.c b/libs/sipcc/plat/csf2g/model.c
new file mode 100644 (file)
index 0000000..1d4ca47
--- /dev/null
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/**
+ * Set device model that will be sent to CUCM.
+ */
+char *
+platGetModel()
+{
+    return "CSF";
+}
+
diff --git a/libs/sipcc/plat/csf2g/reset_api.c b/libs/sipcc/plat/csf2g/reset_api.c
new file mode 100644 (file)
index 0000000..35314bf
--- /dev/null
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "phone_debug.h"
+
+/**
+ * resetReady
+ *
+ */
+void resetReady() {
+    return;
+}
+
+/**
+ * resetNotReady
+ *
+ */
+void resetNotReady() {
+    return;
+}
+
+
+/**
+ * resetRequest
+ */
+void resetRequest() {
+
+    return;
+}
diff --git a/libs/sipcc/plat/darwin/netif.c b/libs/sipcc/plat/darwin/netif.c
new file mode 100644 (file)
index 0000000..b64e36e
--- /dev/null
@@ -0,0 +1,253 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+
+#define IN_ADDR_PLEN 16 /* including trailing '\0' */
+#define IN6_ADDR_PLEN 128 /* including trailing '\0' */
+#define MAC_ADDR_PLEN 18
+
+
+//static char netif_ip_addr[IN6_ADDR_PLEN];
+//static char netif_mac_addr[MAC_ADDR_PLEN];
+
+//static int
+//eth_macaddr_ntop(const struct sockaddr_dl *sdl, char *dst, size_t dstsize)
+//{
+//    const unsigned char *lladdr = LLADDR(sdl);
+//    int r;
+//
+//    r = snprintf(dst, dstsize, "%02x:%02x:%02x:%02x:%02x:%02x",
+//                (int) lladdr[0], (int) lladdr[1], (int) lladdr[2],
+//                (int) lladdr[3], (int) lladdr[4], (int) lladdr[5]);
+//
+//    if (r >= dstsize)
+//        /* The destination buffer is too small */
+//        return -1;
+//    else
+//        return 0;
+//}
+
+
+/*
+ * Find the local IP (v4 or v6) address used by the system to reach a given IP
+ * address.
+ */
+//static int
+//findlocaladdr(int af, struct sockaddr *dest, socklen_t destsize,
+//                struct sockaddr *local, socklen_t *localsize)
+//{
+//    int fd;
+//
+//    if ((fd = socket(af, SOCK_DGRAM, 0)) == -1) {
+//        perror("socket");
+//        return -1;
+//    }
+//
+//    if (connect(fd, dest, destsize) != 0) {
+//        perror("connect");
+//        close(fd);
+//        return -1;
+//    }
+//
+//    /*
+//     * Retrieve the local address associated with the socket.
+//     */
+//    if (getsockname(fd, (struct sockaddr *) local, localsize) != 0) {
+//        perror("getsockname");
+//        close(fd);
+//        return -1;
+//    }
+//
+//    close(fd);
+//
+//    /* Check that the retrieved address is of the same family */
+//    if (local->sa_family == af)
+//        return 0;
+//    else
+//        return -1;
+//}
+//
+//static int
+//inaddrcmp(struct sockaddr *s1, struct sockaddr *s2)
+//{
+//    if (s1->sa_family == s2->sa_family && s1->sa_family == AF_INET) {
+//        return bcmp(&((struct sockaddr_in *) s1)->sin_addr,
+//                    &((struct sockaddr_in *) s2)->sin_addr,
+//                    sizeof(struct in_addr));
+//    } else if (s1->sa_family == s2->sa_family && s1->sa_family == AF_INET6) {
+//        return bcmp(&((struct sockaddr_in6 *) s1)->sin6_addr,
+//                    &((struct sockaddr_in6 *) s2)->sin6_addr,
+//                    sizeof(struct in6_addr));
+//    } else {
+//        return -1;
+//    }
+//}
+
+/*
+ * Retrieve the IP address (and associated link-layer address) that will be
+ * used by the operating system as the source IP address when sending packets
+ * to the given destination address.
+ */
+//static int
+//getifaddr(int dstaf, struct sockaddr *dst, socklen_t dstsize,
+//          struct sockaddr *ipaddr, size_t ipaddrsize, struct sockaddr_dl *lladdr)
+//{
+//    struct sockaddr_storage ss;
+//    socklen_t sslen;
+//    struct ifaddrs *ifap, *ifp;
+//    char *ifname = NULL;
+//    int found_lladdr = 0;
+//
+//    /*
+//     * First, determine the local IP address that will be used to reach the
+//     * given destination IP address.  From that we will retrieve the interface
+//     * and then the MAC address.
+//     * `dstaf' can only be AF_INET or AF_INET6.
+//     */
+//    bzero(&ss, sizeof(struct sockaddr_storage));
+//    sslen = sizeof(struct sockaddr_storage);
+//    if (findlocaladdr(dstaf, dst, dstsize, (struct sockaddr *) &ss, &sslen)
+//            != 0) {
+//        return -1;
+//    }
+//
+//    /*
+//     * Find the name of the network interface matching the address discovered
+//     * using findlocaladdr().  Note that this is not garanteed to yield the
+//     * correct result (or a result at all) because the network configuration
+//     * may change between the call to findlocaladdr() and getifaddrs().
+//     * But this should work in most cases.
+//     */
+//    if (getifaddrs(&ifap) == -1)
+//        return -1;
+//
+//    for (ifp = ifap; ifp->ifa_next != NULL; ifp = ifp->ifa_next) {
+//        if (inaddrcmp(ifp->ifa_addr, (struct sockaddr *) &ss) == 0) {
+//            ifname = ifp->ifa_name;
+//            break;
+//        }
+//    }
+//
+//    /*
+//     * Get the link-layer address matching the interface name.
+//     */
+//    if (ifname != NULL) {
+//        for (ifp = ifap; ifp->ifa_next != NULL; ifp = ifp->ifa_next) {
+//            if (ifp->ifa_addr->sa_family == AF_LINK
+//                && strcmp(ifname, ifp->ifa_name) == 0) {
+//                bcopy(ifp->ifa_addr, lladdr, sizeof(struct sockaddr_dl));
+//                found_lladdr = 1;
+//                break;
+//            }
+//        }
+//    }
+//
+//    freeifaddrs(ifap);
+//
+//    if (!found_lladdr) {
+//        /* The link-layer address was not found! */
+//        return -1;
+//    } else {
+//        /* Copy the IP address to the buffer provided by the caller */
+//        bcopy(&ss, ipaddr, ipaddrsize);
+//        return 0;
+//    }
+//}
+//
+//static int
+//getifaddr2(int af, struct sockaddr *ipaddr, size_t ipaddrsize,
+//           struct sockaddr_dl *lladdr)
+//{
+//    struct ifaddrs *ifap, *ifp;
+//    char *ifname;
+//    int found_lladdr = 0;
+//
+//    if (getifaddrs(&ifap) == -1)
+//        return -1;
+//
+//    /* Walk the list to find an active interface with an IPv4 or IPv6 address */
+//    for (ifp = ifap; ifp->ifa_next != NULL; ifp = ifp->ifa_next) {
+//        if (ifp->ifa_flags & (IFF_UP|IFF_RUNNING)
+//            && !(ifp->ifa_flags & IFF_LOOPBACK)
+//            && ifp->ifa_addr->sa_family == af) {
+//            ifname = ifp->ifa_name;
+//            bcopy(ifp->ifa_addr, ipaddr, ipaddrsize);
+//            break;
+//        }
+//    }
+//
+//    /* Get the matching link-layer address */
+//    for (ifp = ifap; ifp->ifa_next != NULL; ifp = ifp->ifa_next) {
+//        if (ifp->ifa_addr->sa_family == AF_LINK
+//            && strcmp(ifname, ifp->ifa_name) == 0) {
+//            bcopy(ifp->ifa_addr, lladdr, sizeof(struct sockaddr_dl));
+//            found_lladdr = 1;
+//        }
+//    }
+//
+//    freeifaddrs(ifap);
+//
+//    if (found_lladdr)
+//        return 0;
+//    else
+//        return -1;
+//}
+
+//char *
+//platGetIPAddr(void)
+//{
+//    struct sockaddr_in inaddr;
+//    struct sockaddr_dl lladdr; /* Unused */
+//
+//    /*
+//     * XXX We should use getifaddr() but need to figure out how this can be done
+//     * (we would need the IP address of the CUCM at this stage).  Using
+//     * getifaddr2() ATM.
+//     */
+//
+//    /*
+//     * XXX This will return an IPv4 address.  getifaddr() and getifaddr2() can
+//     * handle IPv6 addresses properly though.
+//     */
+//
+//    if (getifaddr2(AF_INET, (struct sockaddr *) &inaddr, sizeof(inaddr),
+//            &lladdr) != 0)
+//        return NULL;
+//
+//    inet_ntop(AF_INET, &inaddr.sin_addr, netif_ip_addr, IN_ADDR_PLEN);
+//    return netif_ip_addr;
+//}
+
+//void
+//platGetMacAddr(char *maddr)
+//{
+//    struct sockaddr_in inaddr; /* Unused */
+//    struct sockaddr_dl lladdr;
+//
+    /*
+     * XXX Same comment applies (see platGetIPAddr).  Additionally, it is just
+     * not possible to properly implement platGetIpAddr() and platGetMacAddr()
+     * so that the caller has a guarantee that both address come from the same
+     * network address.
+     */
+
+//    if (getifaddr2(AF_INET, (struct sockaddr *) &inaddr, sizeof(inaddr),
+//            &lladdr) != 0)
+//        /* XXX */
+//        bzero(maddr, MAC_ADDR_PLEN);
+//
+//    eth_macaddr_ntop(&lladdr, maddr, MAC_ADDR_PLEN);
+//}
+
diff --git a/libs/sipcc/plat/darwin/plat_api_stub.c b/libs/sipcc/plat/darwin/plat_api_stub.c
new file mode 100755 (executable)
index 0000000..556ce1d
--- /dev/null
@@ -0,0 +1,448 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string.h>
+
+#include "cpr_types.h"
+#include "cc_constants.h"
+#include "cpr_socket.h"
+#include "plat_api.h"
+
+/**
+ * Initialize the platform threa.
+ * @todo add more explanation here.
+ */
+int platThreadInit(char * tname)
+{
+    return 0;
+}
+
+/**
+ * The initial initialization function for any platform related
+ * modules
+ *
+ *
+ * @return 0 - SUCCESS
+ *        -1 - FAILURE
+ */
+int platInit()
+{
+    return 0;
+}
+
+/**
+ * The initial initialization function for the debugging/logging
+ * modules
+ *
+ *
+ * @return 0 - SUCCESS
+ *        -1 - FAILURE
+ */
+void debugInit()
+{
+    return ;
+}
+
+/**
+ * Add cc control classifier
+ */
+void platAddCallControlClassifiers(unsigned long myIPAddr, unsigned short myPort,
+       unsigned long cucm1IPAddr, unsigned short cucm1Port,
+       unsigned long cucm2IPAddr, unsigned short cucm2Port,
+       unsigned long cucm3IPAddr, unsigned short cucm3Port,
+       unsigned char  protocol)
+{
+    return;
+}
+
+/**
+ * Set ip address mode
+ * e.g.
+ *
+ */
+cpr_ip_mode_e platGetIpAddressMode()
+{
+    return CPR_IP_MODE_IPV4;
+}
+
+/**
+ * Remove cc control classifier.
+ */
+void platRemoveCallControlClassifiers()
+{
+    return;
+}
+
+/**
+ * Tell whether wifi is supported and active
+ */
+cc_boolean     platWlanISActive()
+{
+    return FALSE;
+}
+
+/**
+ * Check if the netowrk interface changed.
+ */
+boolean        platIsNetworkInterfaceChanged()// (NOOP)
+{
+    return TRUE;
+}
+
+/**
+ * Set active phone load name
+ * @return FAILURE or true length. "-1" means no active load found.
+ *
+ */
+int platGetActiveInactivePhoneLoadName(char * image_a, char * image_b, int len)
+{
+    memset(image_a, 0, len);
+    memset(image_b, 0, len);
+
+    return 0;
+}
+
+/**
+ * Get or Set user defined phrases
+ * @param index  the phrase index, see
+ * @param phrase the return phrase holder
+ * @param len the input length to cap the maximum value
+ * @return SUCCESS or FAILURE
+ */
+int platGetPhraseText(int index, char* phrase, unsigned int len)
+{
+    return 0;
+}
+
+/**
+ * Set the unregistration reason
+ * @param reason see the unregister reason definitions.
+ * @return void
+ */
+void platSetUnregReason(int reason)
+{
+    return;
+}
+
+/**
+ * Get the unregistration reason code.
+ * @return reason code for unregistration, see the definition.
+ */
+int platGetUnregReason()
+{
+    return 0;
+}
+
+/**
+ * Set the kpml value for application.
+ * @param kpml_config the kpml value
+ * @return void
+ */
+void platSetKPMLConfig(cc_kpml_config_t kpml_config)
+{
+    return ;
+}
+
+/**
+ * Check if a line has active MWI status
+ * @param line
+ * @return boolean
+ */
+boolean platGetMWIStatus(cc_lineid_t line)
+{
+    return TRUE;
+}
+
+
+/**
+ * Secure Socket API's.
+ * The pSIPCC expects the following Secure Socket APIs to be implemented in the
+ * vendor porting layer.
+ */
+
+/**
+ * platSecIsServerSecure
+ *
+ * @brief Lookup the secure status of the server
+ *
+ * This function looks at the the CCM server type by using the security library
+ * and returns appropriate indication to the pSIPCC.
+ *
+ *
+ * @return   Server is security enabled or not
+ *           PLAT_SOCK_SECURE or PLAT_SOCK_NONSECURE
+ *
+ * @note This API maps to the following HandyIron API:
+ *  int secIsServerSecure(SecServerType type) where type should be SRVR_TYPE_CCM
+ */
+plat_soc_status_e platSecIsServerSecure(void)
+{
+    return PLAT_SOCK_NONSECURE;
+}
+
+
+/**
+ * platSecSocConnect
+ * @brief  Securely connect to a remote server
+ *
+ * This function uses the security library APIs to connect to a remote server.
+ * @param[in]  host         server addr
+ * @param[in]  port         port number
+ * @param[in]  ipMode       IP mode to indicate v6, v4 or both
+ * @param[in]  mode         blocking connect or not
+ *                          FALSE: non-blocking; TRUE: blocking
+ * @param[in]  tos          TOS value
+ * @param[in]  connectionType Are we talking to Call-Agent
+ * @param[in]  connectionMode The mode of the connection
+ *                            (Authenticated/Encrypted)
+ * @param[out] localPort    local port used for the connection
+ *
+ * @return     client socket descriptor
+ *             >=0: connected or in progress
+ *             INVALID SOCKET: failed
+ *
+ * @pre        (hostAndPort not_eq NULL)
+ * @pre        (localPort   not_eq NULL)
+ *
+ * @note localPort is undefined when the return value is INVALID_SOCKET
+ *
+ * @note This API maps to the HandyIron APIs as follows:
+ * If mode == TRUE (blocking):
+ *    int secEstablishSecureConnection(const char* serverAddr, *uint32_t port, secConnectionType type)
+ *    @li ipMode is UNUSED
+ *    @li "host" maps to "serverAddr", "connectionType" maps to "type"
+ *    @li localPort is passed in as 0
+ * If mode == FALSE (non-blocking):
+ *     int secConnect(const char* serverAddr, uint32_t port, *secConnectionType type, uint32_t localPort)
+ *    @li ipMode is UNUSED
+ *    @li "host" maps to "serverAddr", "connectionType" maps to "type"
+ *
+ * @note The implementation should use the "setsockopt" to set the "tos" value passed
+ * in this API on the created socket.
+ *
+ */
+cpr_socket_t
+platSecSocConnect (char *host,
+                  int     port,
+                  int     ipMode,
+                  boolean mode,
+                  unsigned int tos,
+                  plat_soc_connect_mode_e connectionMode,
+                  uint16_t *localPort)
+{
+    return 0;
+}
+
+/**
+ * platSecSockIsConnected
+ * Determine the status of a secure connection that was initiated
+ * in non-blocking mode
+ *
+ * @param[in]    sock   socket descriptor
+ *
+ * @return   connection status
+ *           @li connection complete: PLAT_SOCK_CONN_OK
+ *           @li connection waiting:  PLAT_SOCK_CONN_WAITING
+ *           @li connection failed:   PLAT_SOCK_CONN_FAILED
+ *
+ * @note This API maps to the following HandyIron API:
+ * int secIsConnectionReady (int connDesc)
+ * The "sock" is the connection descriptor.
+ */
+plat_soc_connect_status_e platSecSockIsConnected (cpr_socket_t sock)
+{
+    return PLAT_SOCK_CONN_OK;
+}
+
+/**
+ * platSecSocSend
+ *
+ * @brief The platSecSocSend() function is used to send data over a secure
+ * socket.
+ *
+ * The platSecSocSend() function shall transmit a message from the specified socket to
+ * its peer. The platSecSocSend() function shall send a message only when the socket is
+ * connected. The length of the message to be sent is specified by the length
+ * argument. If the message is too long to pass through the underlying protocol,
+ * platSecSocSend() shall fail and no data is transmitted.  Delivery of the message is
+ * not guaranteed.
+ *
+ * @param[in] soc  Specifies the socket created with cprSocket() to send
+ * @param[in] buf  A pointer to the buffer of the message to send.
+ * @param[in] len  Specifies the length in bytes of the message pointed to by the buffer argument.
+ *
+ * @return Upon successful completion, platSecSocSend() shall return the number of
+ *     bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
+ *     indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  socket does not refer to a socket descriptor
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data can
+ *                          be sent
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this
+ *                            type of socket or protocol.
+ *        @li [CPR_EMSGSIZE]  The message is too large to be sent all at once
+ *        @li [CPR_EDESTADDRREQ]  The socket has no peer address set
+ *
+ */
+ssize_t
+platSecSocSend (cpr_socket_t soc,
+         CONST void *buf,
+         size_t len)
+{
+    return 0;
+}
+
+/**
+ * platSecSocRecv
+ *
+ * @brief The platSecSocRecv() function shall receive a message from a secure socket.
+ *
+ * This function is normally used with connected sockets because it does not permit
+ * the application to retrieve the source address of received data.  The
+ * platSecSocRecv() function shall return the length of the message written to
+ * the buffer pointed to by the "buf" argument.
+ *
+ * @param[in] soc  - Specifies the socket to receive data
+ * @param[out] buf  - Contains the data received
+ * @param[out] len  - The length of the data received
+ *
+ * @return On success the length of the message in bytes (including zero).
+ *         On failure SOCKET_ERROR shall be returned and cpr_errno set to
+ *         indicate the error.
+ *
+ * @note The possible error values this function should return are
+ *        @li [CPR_EBADF]  The socket argument is not a valid file descriptor
+ *        @li [CPR_ENOTSOCK]  The descriptor references a file, not a socket.
+ *        @li [CPR_EAGAIN]    The socket is marked non-blocking and no data is
+ *                        waiting to be received.
+ *        @li [CPR_EWOULDBLOCK]   Same as CPR_EAGAIN
+ *        @li [CPR_ENOTCONN]  A receive attempt is made on a connection-mode socket that is not connected
+ *        @li [CPR_ENOTSUPP]  The specified flags are not supported for this type of socket or protocol
+ *
+ */
+ssize_t
+platSecSocRecv (cpr_socket_t soc,
+         void * RESTRICT buf,
+         size_t len)
+{
+    return 0;
+}
+
+/**
+ * platSecSocClose
+ *
+ * @brief The platSecSocClose function shall close a secure socket
+ *
+ * The platSecSocClose() function shall destroy the socket descriptor indicated
+ * by socket.
+ *
+ * @param[in] soc  - The socket that needs to be destroyed
+ *
+ * @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
+ *
+ * @note The possible error values this function should return are
+ *         @li [CPR_EBADF]      socket is not a valid socket descriptor.
+ */
+cpr_status_e
+platSecSocClose (cpr_socket_t soc)
+{
+    return CPR_SUCCESS;
+}
+
+/**
+ * Sets the SIS protocol version
+ *
+ * @param a - major version
+ * @param b - minor version
+ * @param c - additional version information
+ *
+ * @return void
+ * @note the platform should store this information and provide it when asked via the platGetSISProtocolVer()
+ */
+void platSetSISProtocolVer(uint32_t a, uint32_t b, uint32_t c, char* name)
+{
+    return;
+}
+
+/**
+ * Provides the SIS protocol version
+ *
+ * @param *a pointer to fill in the major version
+ * @param *b pointer to fill in the minor version
+ * @param *c pointer to fill in the additonal version
+ *
+ * @return void
+ */
+void
+platGetSISProtocolVer (uint32_t *a, uint32_t *b, uint32_t *c, char* name)
+{
+    return;
+}
+
+
+void debug_bind_keyword(const char *cmd, int32_t *flag_ptr)
+{
+    return;
+}
+
+void debugif_add_keyword(const char *x, const char *y)
+{
+    return;
+}
+
+void platSetSpeakerMode(cc_boolean state)
+{
+    return;
+}
+
+boolean platGetSpeakerHeadsetMode()
+{
+    return TRUE;
+}
+
+/**
+ *  platGetFeatureAllowed
+ *
+ *      Get whether the feature is allowed
+ *
+ *  @param featureId - sis feature id
+ *
+ *  @return  1 - allowed, 0 - not allowed
+ *
+ */
+int platGetFeatureAllowed(cc_sis_feature_id_e featureId)
+{
+    return TRUE;
+}
+
+
+int platGetAudioDeviceStatus(plat_audio_device_t device_type)
+{
+    return 0;
+}
+
+
+/*
+ * Returns the default gateway
+ *
+ * @param void
+ * @return u_long
+ */
+cc_ulong_t platGetDefaultgw(){
+       return 0;
+}
+
+/**
+ * Sets the time based on Date header in 200 OK from REGISTER request
+ * @param void
+ * @return void
+ */
+void platSetCucmRegTime (void) {
+    //Noop
+}
+
diff --git a/libs/sipcc/plat/unix-common/random.c b/libs/sipcc/plat/unix-common/random.c
new file mode 100644 (file)
index 0000000..f84fc9a
--- /dev/null
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <inttypes.h>
+
+/**
+ * platGenerateCryptoRand
+ * @brief Generates a Random Number
+ *
+ * Generate crypto graphically random number for a desired length.
+ * The function uses "secd" 's provided API. The random bytes are
+ * generated by "secd" which runs as another process. The function
+ * will be much slower than the cpr_rand(). This function should be
+ * used when good random number is needed such as random number that
+ * to be used for SRTP key for an example.
+ *
+ * @param[in] buf  - pointer to the buffer to store the result of random
+ *                   bytes requested.
+ * @param[in] len  - pointer to the length of the desired random bytes.
+ *             When calling the function, the integer's value
+ *             should be set to the desired number of random
+ *             bytes ('buf' should be of at least this size).
+ *             upon success, its value will be set to the
+ *             actual number of random bytes being returned.
+ *             (realistically, there is a maximum number of
+ *             random bytes that can be returned at a time.
+ *             if the caller request more than that, the
+ *             'len' will indicate how many bytes are actually being
+ *             returned) on failure, its value will be set to 0.
+ *
+ * @return
+ *     1 - success.
+ *     0 - fail.
+ *
+ * @note This function MUST BE REWRITTEN BY THE VENDORS
+ * @note The intent of this function is to generate a cryptographically strong
+ * random number. Vendors can map this to HandyIron or OpenSSL random number
+ * generation functions.
+ */
+int
+platGenerateCryptoRand(uint8_t *buf, int *len)
+{
+    int fd;
+    int rc = 0;
+    ssize_t s;
+
+    if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
+        syslog(LOG_ERR, "Failed to open prng driver");
+        return 0;
+    }
+
+    /*
+     * Try to read the given amount of bytes from the PRNG device.  We do not
+     * handle short reads but just return the number of bytes read from the
+     * device.  The caller has to manage this.
+     * E.g. gsmsdp_generate_key() in core/gsm/gsm_sdp_crypto.c
+     */
+    s = read(fd, buf, (size_t) *len);
+
+    if (s > 0) {
+        *len = s;
+        rc = 1; /* Success */
+    } else {
+        *len = 0;
+        rc = 0; /* Failure */
+    }
+
+    (void) close(fd);
+    return rc;
+}
+
diff --git a/libs/sipcc/stub/cc_blf_stub.c b/libs/sipcc/stub/cc_blf_stub.c
new file mode 100755 (executable)
index 0000000..ef7beab
--- /dev/null
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cc_constants.h"
+
+/**
+ * Initialize the BLF stack
+ * @return
+ */
+int CC_BLF_init()
+{
+    return 0;
+}
+/**
+ * Start BLF subscription
+ * @param request_id the request id
+ * @param duration the subscription duration
+ * @param watcher the name of subscription watcher
+ * @param presentity
+ * @param app_id the application id for the BLF
+ * @param feature_mask
+ * @return void
+ */
+void CC_BLF_subscribe(int request_id,
+               int duration,
+               const char *watcher,
+        const char *presentity,
+        int app_id,
+        int feature_mask)
+{
+    return;
+}
+
+/**
+ * Unsubscribe the BLF subscription
+ * @param request_id the request id
+ * @return void
+ */
+void CC_BLF_unsubscribe(int request_id)
+{
+    return;
+}
+
+/**
+ * Unsubscribe all BLF subscription
+ * @return void
+ */
+void CC_BLF_unsubscribe_All()
+{
+    return;
+}
diff --git a/libs/sipcc/stub/vcm_stub.c b/libs/sipcc/stub/vcm_stub.c
new file mode 100755 (executable)
index 0000000..8089e5d
--- /dev/null
@@ -0,0 +1,539 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/** @mainpage VCM APIs.
+ *
+ *  @section Introduction
+ *  This module contains command APIs to the media layer
+ */
+
+/**
+ * @file   vcm.h
+ * @brief  APIs to interface with the Media layer.
+ *
+ * This file contains API that interface to the media layer on the platform.
+ * The following APIs need to be implemented to have the sip stack interact
+ * and issue commands to the media layer.
+ */
+
+#include "cpr_types.h"
+#include "vcm.h"
+#include "rtp_defs.h"
+#include "ccsdp.h"
+
+
+/**
+ *  The initialization of the VCM module
+ *
+ */
+void vcmInit()
+{
+    return ;
+}
+
+/**
+ *   Should we remove this from external API
+ *
+ *  @param[in] mcap_id - group identifier to which stream belongs.
+ *  @param[in]     group_id         - group identifier
+ *  @param[in]     cc_stream_id        - stream identifier
+ *  @param[in]     call_handle      - call handle
+ *  @param[in]     port_requested   - requested port.
+ *  @param[in]     listen_ip        - local IP for listening
+ *  @param[in]     is_multicast     - multicast stream?
+ *  @param[in,out] port_allocated   - allocated(reserved) port
+ *
+ *  tbd need to see if we can deprecate this API
+ *
+ *  @return       0 success, ERROR failure.
+ *
+ */
+
+short vcmRxOpen(cc_mcapid_t mcap_id, cc_groupid_t group_id, cc_streamid_t stream_id,  cc_call_handle_t call_handle,
+                  uint16_t port_requested, cpr_ip_addr_t *listen_ip,
+                  boolean is_multicast, int *port_allocated)
+{
+    return 0;
+}
+/*!
+ *  should we remove from external API
+ *
+ *  @param[in]  mcap_id - Media Capability ID
+ *  @param[in]  group_id - group to which stream belongs
+ *  @param[in]  cc_stream_id - stream identifier
+ *  @param[in]  call_handle - call handle
+ *
+ *  @return  zero(0) for success otherwise, ERROR for failure
+ *
+ */
+
+short vcmTxOpen(cc_mcapid_t mcap_id, cc_groupid_t group_id, cc_streamid_t stream_id, cc_call_handle_t call_handle)
+{
+    return 0;
+}
+
+/*!
+ *  Allocate(Reserve) a receive port.
+ *
+ *  @param[in]  mcap_id - Media Capability ID
+ *  @param[in]  group_id - group identifier to which stream belongs.
+ *  @param[in]  cc_stream_id - stream identifier
+ *  @param[in]  call_handle  - call handle
+ *  @param[in]  port_requested - port requested (if zero -> give any)
+ *  @param[out]  port_allocated - port that was actually allocated.
+ *
+ *  @return    void
+ *
+ */
+
+void vcmRxAllocPort(cc_mcapid_t mcap_id, cc_groupid_t group_id, cc_streamid_t stream_id,  cc_call_handle_t call_handle,
+                       uint16_t port_requested, int *port_allocated)
+{
+    return;
+}
+
+/*!
+ *  Release the allocated port
+ * @param[in] mcap_id   - media capability id (0 is audio)
+ * @param[in] group_id  - group identifier
+ * @param[in] cc_stream_id - stream identifier
+ * @param[in] call_handle   - call handle
+ * @param[in] port     - port to be released
+ *
+ * @return void
+ */
+
+void vcmRxReleasePort(cc_mcapid_t mcap_id, cc_groupid_t group_id,cc_streamid_t stream_id,  cc_call_handle_t call_handle, int port)
+{
+    return;
+}
+
+/*!
+ *  Start receive stream
+ *  Note: For video calls, for a given call_id there will be
+ *        two media lines and the corresponding group_id/cc_stream_id pair.
+ *        One RTP session is requested from media server for each
+ *        media line(group/stream) i.e. a video call would result in
+ *        two rtp_sessions in our session info list created by two
+ *        calls to vcm_rx/tx with mcap_id of AUDIO and VIDEO respectively.
+ *
+ *  @param[in]    mcap_id     - media type id
+ *  @param[in]    group_id    - group identifier associated with the stream
+ *  @param[in]    cc_stream_id   - id of the stream one per each media line
+ *  @param[in]    call_handle     - call handle
+ *  @param[in]    payload     - payload type
+ *  @param[in]    local_addr  - local ip address to use.
+ *  @param[in]    port        - local port (receive)
+ *  @param[in]    algorithmID - crypto alogrithm ID
+ *  @param[in]    rx_key      - rx key used when algorithm ID is encrypting
+ *  @param[in]    attrs       - media attributes
+ *
+ *  @return   zero(0) for success otherwise, -1 for failure
+ *
+ */
+
+int vcmRxStart(cc_mcapid_t mcap_id, cc_groupid_t group_id, cc_streamid_t
+               stream_id, cc_call_handle_t call_handle,
+               const vcm_payload_info_t *payload, cpr_ip_addr_t *local_addr,
+               uint16_t port, vcm_crypto_algorithmID algorithmID,
+               vcm_crypto_key_t *rx_key, vcm_mediaAttrs_t *attrs)
+{
+    return 0;
+}
+
+/**
+ *  start tx stream
+ *  Note: For video calls, for a given call_id there will be
+ *        two media lines and the corresponding group_id/cc_stream_id pair.
+ *        One RTP session is requested from media server for each
+ *        media line(group/stream) i.e. a video call would result in
+ *        two rtp_sessions in our session info list created by two
+ *        calls to vcm_rx/tx with mcap_id of AUDIO and VIDEO respectively.
+ *
+ *  @param[in]   mcap_id      - media cap id
+ *  @param[in]   group_id     - group identifier to which the stream belongs
+ *  @param[in]   cc_stream_id    - stream id of the given media type.
+ *  @param[in]   call_handle  - call handle
+ *  @param[in]   payload      - payload type
+ *  @param[in]   tos          - bit marking
+ *  @param[in]   local_addr   - local address
+ *  @param[in]   local_port   - local port
+ *  @param[in]   remote_ip_addr - remote ip address
+ *  @param[in]   remote_port  - remote port
+ *  @param[in]   algorithmID  - crypto alogrithm ID
+ *  @param[in]   tx_key       - tx key used when algorithm ID is encrypting.
+ *  @param[in]   attrs        - media attributes
+ *
+ *  Returns: zero(0) for success otherwise, ERROR for failure
+ *
+ */
+
+int vcmTxStart(cc_mcapid_t mcap_id, cc_groupid_t group_id,
+               cc_streamid_t stream_id,  cc_call_handle_t call_handle,
+               const vcm_payload_info_t *payload, short tos,
+               cpr_ip_addr_t *local_addr, uint16_t local_port,
+               cpr_ip_addr_t *remote_ip_addr, uint16_t remote_port,
+               vcm_crypto_algorithmID algorithmID, vcm_crypto_key_t *tx_key,
+               vcm_mediaAttrs_t *attrs)
+{
+    return 0;
+}
+
+/*!
+ *  Close the receive stream.
+ *
+ *  @param[in]    mcap_id - Media Capability ID
+ *  @param[in]    group_id - group identifier that belongs to the stream.
+ *  @param[in]    cc_stream_id - stream id of the given media type.
+ *  @param[in]    call_handle  - call handle
+ *
+ *  @return   None
+ *
+ */
+
+void vcmRxClose(cc_mcapid_t mcap_id, cc_groupid_t group_id,cc_streamid_t stream_id,  cc_call_handle_t call_handle)
+{
+    return;
+}
+
+/**
+ *  Close the transmit stream
+ *
+ *  @param[in] mcap_id - Media Capability ID
+ *  @param[in] group_id - identifier of the group to which stream belongs
+ *  @param[in] cc_stream_id - stream id of the given media type.
+ *  @param[in] call_handle  - call handle
+ *
+ *  @return     void
+ */
+
+void vcmTxClose(cc_mcapid_t mcap_id, cc_groupid_t group_id, cc_streamid_t stream_id, cc_call_handle_t call_handleS)
+{
+    return;
+}
+
+/**
+ *  To be Deprecated
+ *  This may be needed to be implemented if the DSP doesn't automatically enable the side tone
+ *  The stack will make a call to this method based on the call state. Provide a stub if this is not needed.
+ *
+ *  @param[in] side_tone - boolean to enable/disable side tone
+ *
+ *  @return void
+ *
+ */
+void vcmEnableSidetone(uint16_t side_tone)
+{
+    return;
+}
+
+/**
+ *  Start a tone (continuous)
+ *
+ *  Parameters:
+ *  @param[in] tone       - tone type
+ *  @param[in] alert_info - alertinfo header
+ *  @param[in] call_handle    - call handle
+ *  @param[in] group_id - identifier of the group to which stream belongs
+ *  @param[in] cc_stream_id   - stream identifier.
+ *  @param[in] direction  - network, speaker, both
+ *
+ *  @return void
+ *
+ */
+
+void vcmToneStart(vcm_tones_t tone, short alert_info, cc_call_handle_t call_handle, cc_groupid_t group_id,
+                    cc_streamid_t stream_id, uint16_t direction)
+{
+    return;
+}
+
+/**
+ * Plays a short tone. uses the open audio path.
+ * If no audio path is open, plays on speaker.
+ *
+ * @param[in] tone       - tone type
+ * @param[in] alert_info - alertinfo header
+ * @param[in] call_handle - call handle
+ * @param[in] group_id - identifier of the group to which stream belongs
+ * @param[in] cc_stream_id   - stream identifier.
+ * @param[in] direction  - network, speaker, both
+ *
+ * @return void
+ */
+
+void vcmToneStartWithSpeakerAsBackup(vcm_tones_t tone, short alert_info, cc_call_handle_t call_handle, cc_groupid_t group_id,
+                    cc_streamid_t stream_id, uint16_t direction)
+{
+    return;
+}
+
+/**
+ *  Stop the tone being played.
+ *
+ *  Description: Stop the tone being played currently
+ *
+ *
+ * @param[in] tone - tone to be stopeed
+ * @param[in] group_id - associated stream's group
+ * @param[in] cc_stream_id - associated stream id
+ * @param[in] call_handle - the context (call) for this tone.
+ *
+ * @return void
+ *
+ */
+
+void vcmToneStop(vcm_tones_t tone, cc_groupid_t group_id, cc_streamid_t cc_stream_id, cc_call_handle_t call_handle)
+{
+    return;
+}
+
+/**
+ *  start/stop ringing
+ *
+ *  @param[in] ringMode   - VCM ring mode (ON/OFF)
+ *  @param[in] once       - type of ring - continuous or one shot.
+ *  @param[in] alert_info - header specified ring mode.
+ *  @param[in] line       - the line on which to start/stop ringing
+ *
+ *  @return    void
+ */
+
+void vcmControlRinger(vcm_ring_mode_t ringMode, short once,
+                        boolean alert_info, int line, cc_callid_t call_id)
+{
+    return;
+}
+
+
+/**
+ * Enable / disable speaker
+ *
+ * @param[in] state - true -> enable speaker, false -> disable speaker
+ *
+ * @return void
+ */
+
+void vcmSetSpeakerMode(boolean state)
+{
+    return;
+}
+
+/**
+ * Get current list of audio codec that could be used
+ * @param request_type - sendonly/recvonly/sendrecv
+ */
+
+int vcmGetAudioCodecList(int request_type)
+{
+    return 0;
+}
+/**
+ * Get current list of video codec that could be used
+ * @param request_type - sendonly/recvonly/sendrecv
+ */
+
+int vcmGetVideoCodecList(int request_type)
+{
+    return 0;
+}
+
+/**
+ * Get max supported video packetization mode for H.264 video
+ */
+/*
+int vcmGetVideoMaxSupportedPacketizationMode()
+{
+    return 0;
+}
+*/
+/**
+ * Get the rx/tx stream statistics associated with the call.
+ *
+ * @param[in]  mcap_id  - media type (audio/video)
+ * @param[in]  group_id - group id of the stream
+ * @param[in]  cc_stream_id - stram id of the stream
+ * @param[in]  call_handle - call handle
+ * @param[out] rx_stats - ptr to the rx field in the stats struct
+ * @param[out] tx_stats - ptr to the tx field in the stats struct
+ *
+ */
+
+/*
+int vcmGetRtpStats(cc_mcapid_t mcap_id, cc_groupid_t group_id,
+                      cc_streamid_t stream_id, cc_call_handle_t call_handle,
+                      char *rx_stats, char *tx_stats)
+{
+    return 0;
+}
+*/
+
+/**
+ *
+ * The wlan interface puts into unique situation where call control
+ * has to allocate the worst case bandwith before creating a
+ * inbound or outbound call. The function call will interface through
+ * media API into wlan to get the call bandwidth. The function
+ * return is asynchronous and will block till the return media
+ * callback signals to continue the execution.
+ *
+ * @note If not using WLAN interface simply return true
+ *
+ * @return true if the bandwidth can be allocated else false.
+ */
+
+/*
+boolean vcmAllocateBandwidth(cc_call_handle_t call_handle, int sessions)
+{
+    return TRUE;
+}
+*/
+
+/**
+ *
+ * Free the bandwidth allocated for this call
+ * using the vcmAllocateBandwidth API
+ *
+ * @note  If not using WLAN provide a stub
+ */
+
+/*
+void vcmRemoveBandwidth(cc_call_handle_t call_handle)
+{
+    return;
+}
+*/
+
+/**
+ * @brief vcmActivateWlan
+ *
+ * Free the bandwidth allocated for this call
+ * using the vcmAllocateBandwidth API
+ *
+ * @note If not using WLAN provide a stub
+ */
+
+/*
+void vcmActivateWlan(boolean is_active)
+{
+    return;
+}
+*/
+
+/**
+ *  free the media pointer allocated in vcm_negotiate_attrs method
+ *
+ *  @param ptr - pointer to be freed
+ *
+ *  @return  void
+ */
+void vcmFreeMediaPtr(void *ptr)
+{
+    return;
+}
+
+/**
+ *  MEDIA control received from far end on signaling path
+ *
+ *  @param call_handle - call handle of the call
+ *  @param to_encoder - the control request received
+ *        Only FAST_PICTURE_UPDATE is supported
+ *
+ *  @return  void
+ *
+ */
+
+/*
+void vcmMediaControl(cc_call_handle_t call_handle, vcm_media_control_to_encoder_t to_encoder)
+{
+    return;
+}
+*/
+
+/**
+ *  specifies DSCP marking for RTCP streams
+ *
+ *  @param group_id - call_id of the call
+ *  @param dscp - the DSCP value to be used
+ *
+ *  @return  void
+ */
+
+/*
+void vcmSetRtcpDscp(cc_groupid_t group_id, int dscp)
+{
+    return;
+}
+*/
+
+/**
+ * Verify if the SDP attributes for the requested video codec are acceptable
+ *
+ * This method is called for video codecs only. This method should parse the
+ * Video SDP attributes using the SDP helper API and verify if received
+ * attributes are acceptable. If the attributes are acceptable any attribute
+ * values if needed by vcmTxStart method should be bundled in the desired
+ * structure and its pointer should be returned in rccappptr. This opaque
+ * pointer shall be provided again when vcmTxStart is invoked.
+ *
+ * @param [in] media_type - codec for which we are negotiating
+ * @param [in] sdp_p - opaque SDP pointer to be used via SDP helper APIs
+ * @param [in] level - Parameter to be used with SDP helper APIs
+ * @param [out] rcapptr - variable to return the allocated attrib structure
+ *
+ * @return boolean - true if attributes are accepted false otherwise
+ */
+
+boolean vcmCheckAttribs(uint32_t media_type, void *sdp_p, int level, void **rcapptr)
+{
+    return TRUE;
+}
+
+/**
+ * Add Video attributes in the offer/answer SDP
+ *
+ * This method is called for video codecs only. This method should populate the
+ * Video SDP attributes using the SDP helper API
+ *
+ * @param [in] sdp_p - opaque SDP pointer to be used via SDP helper APIs
+ * @param [in] level - Parameter to be used with SDP helper APIs
+ * @param [in] media_type - codec for which the SDP attributes are to be populated
+ * @param [in] payload_number - RTP payload type used for the SDP
+ * @param [in] isOffer - boolean indicating we are encoding an offer or an aswer
+ *
+ * @return void
+ */
+
+
+void vcmPopulateAttribs(void *sdp_p, int level, uint32_t media_type,
+                          uint16_t payload_number, boolean isOffer)
+{
+    return;
+}
+
+/**
+ * Send a DTMF digit
+ *
+ * This method is called for sending a DTMF tone for the specified duration
+ *
+ * @param [in] digit - the DTMF digit that needs to be played out.
+ * @param [in] duration - duration of the tone
+ * @param [in] direction - direction in which the tone needs to be played.
+ *
+ * @return void
+ */
+
+/*
+int vcmDtmfBurst(int digit, int duration, int direction)
+{
+    return 0;
+}
+*/
+
+/*
+int vcmGetILBCMode()
+{
+    return SIPSDP_ILBC_MODE20;
+}
+*/