From: Michael Jerris Date: Tue, 18 Dec 2012 01:14:30 +0000 (-0500) Subject: add sipcc to tree for sdp parser X-Git-Tag: v1.3.13~235 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0f0094678252f8956730083c8970e71defe5ddc8;p=thirdparty%2Ffreeswitch.git add sipcc to tree for sdp parser --- diff --git a/libs/sipcc/core/ccapp/CCProvider.h b/libs/sipcc/core/ccapp/CCProvider.h new file mode 100755 index 0000000000..1053719eba --- /dev/null +++ b/libs/sipcc/core/ccapp/CCProvider.h @@ -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 index 0000000000..e3ec264bf8 --- /dev/null +++ b/libs/sipcc/core/ccapp/call_logger.c @@ -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 +#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 index 0000000000..6ccec2e274 --- /dev/null +++ b/libs/sipcc/core/ccapp/call_logger.h @@ -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 index 0000000000..71471fb60d --- /dev/null +++ b/libs/sipcc/core/ccapp/capability_set.c @@ -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 index 0000000000..bad517a70a --- /dev/null +++ b/libs/sipcc/core/ccapp/capability_set.h @@ -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 index 0000000000..49c500248a --- /dev/null +++ b/libs/sipcc/core/ccapp/cc_blf.c @@ -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 index 0000000000..529affa072 --- /dev/null +++ b/libs/sipcc/core/ccapp/cc_call_feature.c @@ -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 index 0000000000..d990051af8 --- /dev/null +++ b/libs/sipcc/core/ccapp/cc_config.c @@ -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 index 0000000000..930119f4ef --- /dev/null +++ b/libs/sipcc/core/ccapp/cc_device_feature.c @@ -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 index 0000000000..5a8c8d93a8 --- /dev/null +++ b/libs/sipcc/core/ccapp/cc_device_manager.c @@ -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 index 0000000000..ed0dbec7d2 --- /dev/null +++ b/libs/sipcc/core/ccapp/cc_device_manager.h @@ -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 index 0000000000..d6117100a5 --- /dev/null +++ b/libs/sipcc/core/ccapp/cc_info.c @@ -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 index 0000000000..dd3dc3cd48 --- /dev/null +++ b/libs/sipcc/core/ccapp/cc_service.c @@ -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 index 0000000000..8af7198033 --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_call.c @@ -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 index 0000000000..20b3793410 --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_call_info.c @@ -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 index 0000000000..e542901191 --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_config.c @@ -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 index 0000000000..79a919410f --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_device.c @@ -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 index 0000000000..c6671915b0 --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_device_info.c @@ -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;iname != 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 index 0000000000..cc40589d5b --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_feature_info.c @@ -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 index 0000000000..0931e11432 --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_line.c @@ -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 index 0000000000..dda2969e27 --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_line_info.c @@ -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 index 0000000000..bd6cafe066 --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_service.c @@ -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 index 0000000000..b2e15b44b0 --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_snapshot.c @@ -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 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; iattr == (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; iallowed_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 index 0000000000..087513fea0 --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapi_snapshot.h @@ -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 index 0000000000..0920c1a35f --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapp_task.c @@ -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 index 0000000000..e2792a3b0d --- /dev/null +++ b/libs/sipcc/core/ccapp/ccapp_task.h @@ -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 index 0000000000..f6c2d944ef --- /dev/null +++ b/libs/sipcc/core/ccapp/ccprovider.c @@ -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 + +#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); + // + 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)); + + // + 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)); + } + } + // + 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 index 0000000000..2cb1f268d9 --- /dev/null +++ b/libs/sipcc/core/ccapp/conf_roster.c @@ -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 +#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 index 0000000000..4eda1bca14 --- /dev/null +++ b/libs/sipcc/core/ccapp/conf_roster.h @@ -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 index 0000000000..972a5cd385 --- /dev/null +++ b/libs/sipcc/core/ccapp/sessionHash.c @@ -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; ikey & 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 index 0000000000..4722bb4de5 --- /dev/null +++ b/libs/sipcc/core/ccapp/sessionHash.h @@ -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 index 0000000000..a31f80235b --- /dev/null +++ b/libs/sipcc/core/common/cfgfile_utils.c @@ -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 +#include +#include +#include +#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 index 0000000000..63e5465326 --- /dev/null +++ b/libs/sipcc/core/common/cfgfile_utils.h @@ -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 index 0000000000..bd4d87c1ca --- /dev/null +++ b/libs/sipcc/core/common/config_api.c @@ -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 index 0000000000..4f50fdd135 --- /dev/null +++ b/libs/sipcc/core/common/config_parser.c @@ -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 +#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 index 0000000000..35730eaaf8 --- /dev/null +++ b/libs/sipcc/core/common/config_parser.h @@ -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 index 0000000000..87770c6c07 --- /dev/null +++ b/libs/sipcc/core/common/init.c @@ -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 +#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 index 0000000000..6715c62ab6 --- /dev/null +++ b/libs/sipcc/core/common/logger.c @@ -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 index 0000000000..3a1465b1a1 --- /dev/null +++ b/libs/sipcc/core/common/logger.h @@ -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 index 0000000000..29ee709b21 --- /dev/null +++ b/libs/sipcc/core/common/logmsg.h @@ -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 index 0000000000..0fe4bf19ff --- /dev/null +++ b/libs/sipcc/core/common/misc.c @@ -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 +#include +#include +#include +#include + +#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 index 0000000000..bbbcf6144c --- /dev/null +++ b/libs/sipcc/core/common/plat.c @@ -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 +#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 index 0000000000..233ce10be6 --- /dev/null +++ b/libs/sipcc/core/common/platform_api.c @@ -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 +#include +#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 +#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 index 0000000000..ddf609e629 --- /dev/null +++ b/libs/sipcc/core/common/prot_cfgmgr_private.h @@ -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 index 0000000000..93da2d5c3d --- /dev/null +++ b/libs/sipcc/core/common/prot_configmgr.c @@ -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 + * 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, ¤t_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 = ¤t_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 = ¤t_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 index 0000000000..204c3b6005 --- /dev/null +++ b/libs/sipcc/core/common/prot_configmgr.h @@ -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 index 0000000000..6e969a1ecf --- /dev/null +++ b/libs/sipcc/core/common/resource_manager.c @@ -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 index 0000000000..4d34be59dc --- /dev/null +++ b/libs/sipcc/core/common/resource_manager.h @@ -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 index 0000000000..15952840f1 --- /dev/null +++ b/libs/sipcc/core/common/sip_socket_api.c @@ -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 index 0000000000..f587379ccb --- /dev/null +++ b/libs/sipcc/core/common/subscription_handler.c @@ -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 index 0000000000..3bcf7317d2 --- /dev/null +++ b/libs/sipcc/core/common/subscription_handler.h @@ -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 index 0000000000..b8576c9c7d --- /dev/null +++ b/libs/sipcc/core/common/text_strings.c @@ -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 +#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 index 0000000000..0502a38e05 --- /dev/null +++ b/libs/sipcc/core/common/text_strings.h @@ -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 index 0000000000..750a43539d --- /dev/null +++ b/libs/sipcc/core/common/ui.c @@ -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; iline, 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 index 0000000000..9f9a65800c --- /dev/null +++ b/libs/sipcc/core/gsm/ccapi.c @@ -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 index 0000000000..23e6da7d59 --- /dev/null +++ b/libs/sipcc/core/gsm/ccapi_strings.c @@ -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 index 0000000000..0139e64b29 --- /dev/null +++ b/libs/sipcc/core/gsm/dcsm.c @@ -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 index 0000000000..e910db6c47 --- /dev/null +++ b/libs/sipcc/core/gsm/fim.c @@ -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 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 index 0000000000..ebc9f90838 --- /dev/null +++ b/libs/sipcc/core/gsm/fsm.c @@ -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 index 0000000000..e3c84f5073 --- /dev/null +++ b/libs/sipcc/core/gsm/fsmb2bcnf.c @@ -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 index 0000000000..21c32ef81c --- /dev/null +++ b/libs/sipcc/core/gsm/fsmcac.c @@ -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 index 0000000000..7f92bfc367 --- /dev/null +++ b/libs/sipcc/core/gsm/fsmcnf.c @@ -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 index 0000000000..cec9eb5f99 --- /dev/null +++ b/libs/sipcc/core/gsm/fsmdef.c @@ -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 +#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 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 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 " to + * "From " 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 index 0000000000..d0d387fd29 --- /dev/null +++ b/libs/sipcc/core/gsm/fsmxfr.c @@ -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 index 0000000000..561aefb796 --- /dev/null +++ b/libs/sipcc/core/gsm/gsm.c @@ -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 index 0000000000..53d442c89d --- /dev/null +++ b/libs/sipcc/core/gsm/gsm_sdp.c @@ -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; iconstraint_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
+ */ + + (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
+ */ + (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; jcandidate_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
+ * s= line + * t= line + * + * 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 + *
+ */ + (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 + * 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; icandidate_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 + * a= session level line + * + * 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; idcb; + 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 index 0000000000..7ab7c79559 --- /dev/null +++ b/libs/sipcc/core/gsm/gsm_sdp_crypto.c @@ -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 +#include + +#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 index 0000000000..5eeb26a354 --- /dev/null +++ b/libs/sipcc/core/gsm/h/fim.h @@ -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 index 0000000000..c009179ad1 --- /dev/null +++ b/libs/sipcc/core/gsm/h/fsm.h @@ -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 index 0000000000..ba6cafccfc --- /dev/null +++ b/libs/sipcc/core/gsm/h/gsm.h @@ -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 index 0000000000..25ae4cfdb9 --- /dev/null +++ b/libs/sipcc/core/gsm/h/gsm_sdp.h @@ -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 index 0000000000..b0df529747 --- /dev/null +++ b/libs/sipcc/core/gsm/h/lsm.h @@ -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 index 0000000000..438cc869a6 --- /dev/null +++ b/libs/sipcc/core/gsm/h/lsm_private.h @@ -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 index 0000000000..840aced0df --- /dev/null +++ b/libs/sipcc/core/gsm/h/sm.h @@ -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 index 0000000000..5f234c085b --- /dev/null +++ b/libs/sipcc/core/gsm/lsm.c @@ -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 +#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 ; ioffhook)); +#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 index 0000000000..46cce5e5b8 --- /dev/null +++ b/libs/sipcc/core/gsm/media_cap_tbl.c @@ -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 +#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 index 0000000000..80ba7a2f58 --- /dev/null +++ b/libs/sipcc/core/gsm/sm.c @@ -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 index 0000000000..1dee58fa1e --- /dev/null +++ b/libs/sipcc/core/gsm/subapi.c @@ -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 index 0000000000..398f4976a3 --- /dev/null +++ b/libs/sipcc/core/includes/ccSession.h @@ -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 index 0000000000..666397742a --- /dev/null +++ b/libs/sipcc/core/includes/ccapi.h @@ -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 // 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 index 0000000000..c5dd8051df --- /dev/null +++ b/libs/sipcc/core/includes/check_sync.h @@ -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 index 0000000000..e5d014cf4a --- /dev/null +++ b/libs/sipcc/core/includes/ci.h @@ -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 index 0000000000..9dc31b9630 --- /dev/null +++ b/libs/sipcc/core/includes/config.h @@ -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 index 0000000000..fae40922e5 --- /dev/null +++ b/libs/sipcc/core/includes/configapp.h @@ -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 index 0000000000..3c0b4bee8a --- /dev/null +++ b/libs/sipcc/core/includes/configmgr.h @@ -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 index 0000000000..b9197c8eb1 --- /dev/null +++ b/libs/sipcc/core/includes/debug.h @@ -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 index 0000000000..ec1f93e610 --- /dev/null +++ b/libs/sipcc/core/includes/dialplan.h @@ -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 +#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 () format is not used */ + STATE_END_TAG_STARTED, /* end tag started when we see ">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 index 0000000000..0525d1928b --- /dev/null +++ b/libs/sipcc/core/includes/scSession.h @@ -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 index 0000000000..2b2390a262 --- /dev/null +++ b/libs/sipcc/core/includes/session.h @@ -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 index 0000000000..a094c3d867 --- /dev/null +++ b/libs/sipcc/core/includes/sessionConstants.h @@ -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 index 0000000000..cc9b235516 --- /dev/null +++ b/libs/sipcc/core/includes/sessionTypes.h @@ -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 index 0000000000..8c5ffbf55a --- /dev/null +++ b/libs/sipcc/core/includes/sessuri.h @@ -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 index 0000000000..a0046abc6d --- /dev/null +++ b/libs/sipcc/core/includes/singly_link_list.h @@ -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 index 0000000000..2969d7d8d0 --- /dev/null +++ b/libs/sipcc/core/includes/sip_socket_api.h @@ -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 index 0000000000..93f53ea147 --- /dev/null +++ b/libs/sipcc/core/includes/sntp.h @@ -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 +#include +#include +#include + +#include +// Begin system specific includes +//#include +#include +#include +#include +#include +#include +#include +// 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 index 0000000000..1efb231c7d --- /dev/null +++ b/libs/sipcc/core/includes/string_lib.h @@ -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 index 0000000000..367842732a --- /dev/null +++ b/libs/sipcc/core/includes/subapi.h @@ -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 index 0000000000..6cbc937603 --- /dev/null +++ b/libs/sipcc/core/includes/task.h @@ -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 index 0000000000..cc02ce7dbe --- /dev/null +++ b/libs/sipcc/core/includes/time2.h @@ -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 +#include +#include +#include +#include +#include + +// 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 + * 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 index 0000000000..eb02e9e63f --- /dev/null +++ b/libs/sipcc/core/includes/timer.h @@ -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 index 0000000000..80d4d62ac0 --- /dev/null +++ b/libs/sipcc/core/includes/tnpphone.h @@ -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 index 0000000000..30596cf4ea --- /dev/null +++ b/libs/sipcc/core/includes/uart.h @@ -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 index 0000000000..6604264eb1 --- /dev/null +++ b/libs/sipcc/core/includes/uiapi.h @@ -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 index 0000000000..96f2bd987e --- /dev/null +++ b/libs/sipcc/core/includes/upgrade.h @@ -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 index 0000000000..98ad646c73 --- /dev/null +++ b/libs/sipcc/core/includes/util_ios_queue.h @@ -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 index 0000000000..e3341124a2 --- /dev/null +++ b/libs/sipcc/core/includes/util_parse.h @@ -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 index 0000000000..04a53d9ecc --- /dev/null +++ b/libs/sipcc/core/includes/util_string.h @@ -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 index 0000000000..e5c40b01ea --- /dev/null +++ b/libs/sipcc/core/includes/www.h @@ -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 index 0000000000..b91558f30a --- /dev/null +++ b/libs/sipcc/core/includes/xml_defs.h @@ -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_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 index 0000000000..585a9549cd --- /dev/null +++ b/libs/sipcc/core/sdp/ccsdp.c @@ -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 index 0000000000..a7ad11db6e --- /dev/null +++ b/libs/sipcc/core/sdp/sdp.h @@ -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, /* or '$' */ + SDP_PORT_NUM_COUNT, /* / */ + SDP_PORT_VPI_VCI, /* / */ + SDP_PORT_VCCI, /* */ + SDP_PORT_NUM_VPI_VCI, /* // */ + SDP_PORT_VCCI_CID, /* / or '$'/'$' */ + SDP_PORT_NUM_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__KEY_BYTES + * The size of a master key for in bytes. + * + * SDP_SRTP__SALT_BYTES + * The size of a master salt for 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 = */ +#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:, a=X-sidout:< val> and a=X-confid: + * 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: + * = ... + * 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=: + * +*/ +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 index 0000000000..dac9069407 --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_access.c @@ -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: 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 index 0000000000..177628ff3d --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_attr.c @@ -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 +#include + +#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: ,... + * fmtp: [annexa=yes/no] [annexb=yes/no] [bitrate=] + * [QCIF =] [CIF =] [MaxBR = ] 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 + * 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; iattr.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; iattr.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: < 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: to the maximum number + * of id available. + */ + attr_p->attr.stream_data.num_group_id =0; + + for (i=0; iattr.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:" + * = ... + */ +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:" + */ +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 = "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: ["|"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: [] + * + * 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 index 0000000000..13994455a0 --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_attr_access.c @@ -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; iattr.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; iattr.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: ,... + * fmtp: [annexa=yes/no] [annexb=yes/no] [bitrate=] + * 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; ibmap[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: 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: ...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: ...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: 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 index 0000000000..0cef82166d --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_base64.c @@ -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=MAX_BASE64_LINE_LENGTH) { + if (j> 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=MAX_BASE64_LINE_LENGTH) { + if (jdmax) { + /* 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)> 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> 4; + + if (j=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=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 index 0000000000..e264245b72 --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_base64.h @@ -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)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 index 0000000000..c11c7c89d5 --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_main.c @@ -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 = 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 index 0000000000..2f04b6f6e4 --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_os_defs.h @@ -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 index 0000000000..90634d4b8b --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_private.h @@ -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 index 0000000000..79787eb2b5 --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_services_unix.c @@ -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 index 0000000000..79787eb2b5 --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_services_win32.c @@ -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 index 0000000000..0856deee1e --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_token.c @@ -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 + +#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 . 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 , 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 , 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 /. 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 /. 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 /. 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 / 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 //. 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 ///. 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 index 0000000000..f3685e89c6 --- /dev/null +++ b/libs/sipcc/core/sdp/sdp_utils.c @@ -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 +#include +#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: inline:|| + * + * if and 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: inline:|| + * Where 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: inline:|| + * Where 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 index 0000000000..c76afd86a5 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_callinfo.c @@ -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 +#include + +#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)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: + * --------------- + * ; reason= conference + * ; 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 index 0000000000..0a97efcc8b --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_cc.c @@ -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 index 0000000000..170bd98e4d --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_common_util.c @@ -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 index 0000000000..e0807b30f0 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_core.c @@ -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" ;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\" ;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 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" - Display string is "Test Test" + * Ex: Test Test - Display string is "Test Test" + * Ex: - 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 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, " + */ + 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_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, "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 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, " + */ + 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 index 0000000000..b7d571e4f7 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_debug.c @@ -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 index 0000000000..d58b022785 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_info.c @@ -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 index 0000000000..d9a79651e9 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_messaging.c @@ -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,"\" ;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_HEADER_ANONYMOUS_STR, SIP_HEADER_ANONYMOUS_STR, + addr_str); + } else { + snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, "\"%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,"\" ", + 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), + "", + 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, "", + 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, "host, sipUrl->port); + } else { + if ((sipUrl->password) && (*(sipUrl->password))) { + snprintf(contact, len, "user, + sipUrl->password, sipUrl->host, sipUrl->port); + } else { + snprintf(contact, len, "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 *)×tamp); + + (void) gmtime_r((time_t *)×tamp, &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, "host, sipUrl->port); + } else { + snprintf(sipurlstr, MAX_SIP_HEADER_LENGTH, "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, "", 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.instance="" + // ;+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=\"\"", + 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.instance=""; + //+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=\"\"" + ";+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, "", + user_info); + } else { + /* + * Use the contact value supplied to us, if available. + * If not, use name + */ + snprintf(pContactStr, 6, "", 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, "", + escaped_line_name, dest_sip_addr_str); + } else { + snprintf(pReferByStr, MAX_SIP_URL_LENGTH, "", + 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, "", + escaped_user, dest_sip_addr_str); /* proxy */ + } else { + snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, "", + 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, "", + escaped_user, dest_sip_addr_str); /* proxy */ + } else { + snprintf(sip_to_temp, MAX_SIP_URL_LENGTH, "", + 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 index 0000000000..de752158a2 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_platform.c @@ -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 index 0000000000..bbc09c3206 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_platform_tcp.c @@ -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 index 0000000000..7de215d448 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_platform_timers.c @@ -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 index 0000000000..7183841780 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_platform_tls.c @@ -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 index 0000000000..0b09bbc792 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_platform_udp.c @@ -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 + +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 index 0000000000..e298993c09 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_pmh.c @@ -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 +#include + +#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 ; 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" + * "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, + ¶m_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(¶ms, 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, ¶ms); + /* + * 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 (%) + * 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 '); + 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= 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= {} + * 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={ } // 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, ¶m_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, ¶m_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 index 0000000000..acde663e9f --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_publish.c @@ -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, "", + 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 index 0000000000..8f79d62e60 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_register.c @@ -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 + +#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, ®ister_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, ®ister_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 index 0000000000..24d19abfb1 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_reldev.c @@ -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 index 0000000000..b0c951499f --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_sdp.c @@ -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 index 0000000000..97ce502d31 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_spi_utils.c @@ -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 index 0000000000..5189eb24c7 --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_subsmanager.c @@ -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(¬_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(¬_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(¬ify); + + // 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(®ister_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(®ister_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(¬ify_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(¬ify_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(¬ify_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(¬ify_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:@ 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, ¬Datap, 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(¬ify_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(¬ify_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) (¬ify_ind_data); + } else if (scbp->subsNotCallbackTask != CC_SRC_MIN) { + (void) sip_send_message(¬ify_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, "", + 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, "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, "", + scbp->SubscriberURI, src_addr_str); + } else { + snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, "", + 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, + "", + 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, + "", + 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\" ", display_name, + line_name, src_addr_str); + } else { + snprintf(sip_from_temp, MAX_SIP_URL_LENGTH, + "\"%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 index 0000000000..318f2af83a --- /dev/null +++ b/libs/sipcc/core/sipstack/ccsip_task.c @@ -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, ®ConfigValue, 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= 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= 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, ®ConfigValue, 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 index 0000000000..3619a89883 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_callinfo.h @@ -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 +// , "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 index 0000000000..be7ea6860b --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_cc.h @@ -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 index 0000000000..3547596800 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_common_cb.h @@ -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 index 0000000000..39525b2bb1 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_core.h @@ -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 index 0000000000..e23a43372f --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_credentials.h @@ -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 index 0000000000..40a560bb54 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_macros.h @@ -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 index 0000000000..0ee3b4d832 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_messaging.h @@ -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 index 0000000000..36b8c92568 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_platform.h @@ -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 index 0000000000..21c0b0342d --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_platform_tcp.h @@ -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 index 0000000000..06a6f54a26 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_platform_timers.h @@ -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 index 0000000000..6d6fba9378 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_platform_tls.h @@ -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 index 0000000000..9eddc2094b --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_platform_udp.h @@ -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 index 0000000000..303eddde74 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_pmh.h @@ -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 index 0000000000..ca6033553b --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_protocol.h @@ -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 index 0000000000..3e823e1e8c --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_publish.h @@ -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 index 0000000000..4148bdb314 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_register.h @@ -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 index 0000000000..cf6a76431e --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_reldev.h @@ -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 index 0000000000..b927a83460 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_sdp.h @@ -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 index 0000000000..89cff0a3d9 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_sim.h @@ -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 index 0000000000..95aeb51216 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_spi_utils.h @@ -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 index 0000000000..fe66ac9e55 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_subsmanager.h @@ -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 index 0000000000..801168e168 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/ccsip_task.h @@ -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 index 0000000000..23c8e875a2 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/httpish.h @@ -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 index 0000000000..3a1751d0e5 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/httpish_protocol.h @@ -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 index 0000000000..5e1338be96 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/pmhdefs.h @@ -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 index 0000000000..0ffe1487c6 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/pmhutils.h @@ -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 index 0000000000..b4a7e51f55 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/regmgrapi.h @@ -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 index 0000000000..e200cdcd8b --- /dev/null +++ b/libs/sipcc/core/sipstack/h/sip_ccm_transport.h @@ -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 index 0000000000..cb88fe7bfd --- /dev/null +++ b/libs/sipcc/core/sipstack/h/sip_common_regmgr.h @@ -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 "" + +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 index 0000000000..e6b4bf0d70 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/sip_common_transport.h @@ -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 index 0000000000..0797985e9b --- /dev/null +++ b/libs/sipcc/core/sipstack/h/sip_csps_transport.h @@ -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 index 0000000000..a12d17901c --- /dev/null +++ b/libs/sipcc/core/sipstack/h/sip_interface_regmgr.h @@ -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 index 0000000000..2521906873 --- /dev/null +++ b/libs/sipcc/core/sipstack/h/sip_platform_task.h @@ -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 index 0000000000..f909a7baf2 --- /dev/null +++ b/libs/sipcc/core/sipstack/httpish.c @@ -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 +#include + +#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 index 0000000000..57c83d784c --- /dev/null +++ b/libs/sipcc/core/sipstack/pmhutils.c @@ -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 index 0000000000..72c14fb1b2 --- /dev/null +++ b/libs/sipcc/core/sipstack/sip_common_regmgr.c @@ -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 index 0000000000..86b5e690da --- /dev/null +++ b/libs/sipcc/core/sipstack/sip_common_transport.c @@ -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 index 0000000000..9982ab3cb8 --- /dev/null +++ b/libs/sipcc/core/sipstack/sip_csps_transport.c @@ -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 index 0000000000..952cc806d6 --- /dev/null +++ b/libs/sipcc/core/sipstack/sip_interface_regmgr.c @@ -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 index 0000000000..853ae3de2b --- /dev/null +++ b/libs/sipcc/core/sipstack/sip_platform_task.c @@ -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 index 0000000000..c9760b1893 --- /dev/null +++ b/libs/sipcc/core/sipstack/sip_platform_win32_task.c @@ -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 index 0000000000..849a55fe72 --- /dev/null +++ b/libs/sipcc/core/src-common/configapp.c @@ -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 index 0000000000..36b1cd366c --- /dev/null +++ b/libs/sipcc/core/src-common/dialplan.c @@ -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 +#include + +#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 + * + *