michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "CSFLog.h" michael@0: michael@0: #include "CC_SIPCCDevice.h" michael@0: #include "CC_SIPCCDeviceInfo.h" michael@0: #include "CC_SIPCCFeatureInfo.h" michael@0: #include "CC_SIPCCLine.h" michael@0: #include "CC_SIPCCLineInfo.h" michael@0: #include "CC_SIPCCCallInfo.h" michael@0: #include "CallControlManagerImpl.h" michael@0: #include "csf_common.h" michael@0: michael@0: extern "C" michael@0: { michael@0: #include "config_api.h" michael@0: } michael@0: michael@0: michael@0: static const char logTag[] = "CallControlManager"; michael@0: michael@0: using namespace std; michael@0: using namespace CSFUnified; michael@0: michael@0: namespace CSF michael@0: { michael@0: michael@0: michael@0: CallControlManagerImpl::CallControlManagerImpl() michael@0: : m_lock("CallControlManagerImpl"), michael@0: multiClusterMode(false), michael@0: sipccLoggingMask(0xFFFFFFFF), michael@0: authenticationStatus(AuthenticationStatusEnum::eNotAuthenticated), michael@0: connectionState(ConnectionStatusEnum::eIdle) michael@0: { michael@0: CSFLogInfo(logTag, "CallControlManagerImpl()"); michael@0: } michael@0: michael@0: CallControlManagerImpl::~CallControlManagerImpl() michael@0: { michael@0: CSFLogInfo(logTag, "~CallControlManagerImpl()"); michael@0: destroy(); michael@0: } michael@0: michael@0: bool CallControlManagerImpl::destroy() michael@0: { michael@0: CSFLogInfo(logTag, "destroy()"); michael@0: bool retval = disconnect(); michael@0: if(retval == false) michael@0: { michael@0: return retval; michael@0: } michael@0: return retval; michael@0: } michael@0: michael@0: // Observers michael@0: void CallControlManagerImpl::addCCObserver ( CC_Observer * observer ) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: if (observer == nullptr) michael@0: { michael@0: CSFLogError(logTag, "NULL value for \"observer\" passed to addCCObserver()."); michael@0: return; michael@0: } michael@0: michael@0: ccObservers.insert(observer); michael@0: } michael@0: michael@0: void CallControlManagerImpl::removeCCObserver ( CC_Observer * observer ) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: ccObservers.erase(observer); michael@0: } michael@0: michael@0: void CallControlManagerImpl::addECCObserver ( ECC_Observer * observer ) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: if (observer == nullptr) michael@0: { michael@0: CSFLogError(logTag, "NULL value for \"observer\" passed to addECCObserver()."); michael@0: return; michael@0: } michael@0: michael@0: eccObservers.insert(observer); michael@0: } michael@0: michael@0: void CallControlManagerImpl::removeECCObserver ( ECC_Observer * observer ) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: eccObservers.erase(observer); michael@0: } michael@0: michael@0: void CallControlManagerImpl::setMultiClusterMode(bool allowMultipleClusters) michael@0: { michael@0: CSFLogInfo(logTag, "setMultiClusterMode(%s)", michael@0: allowMultipleClusters ? "TRUE" : "FALSE"); michael@0: multiClusterMode = allowMultipleClusters; michael@0: } michael@0: michael@0: void CallControlManagerImpl::setSIPCCLoggingMask(const cc_int32_t mask) michael@0: { michael@0: CSFLogInfo(logTag, "setSIPCCLoggingMask(%u)", mask); michael@0: sipccLoggingMask = mask; michael@0: } michael@0: michael@0: void CallControlManagerImpl::setAuthenticationString(const std::string &authString) michael@0: { michael@0: CSFLogInfo(logTag, "setAuthenticationString()"); michael@0: this->authString = authString; michael@0: } michael@0: michael@0: void CallControlManagerImpl::setSecureCachePath(const std::string &secureCachePath) michael@0: { michael@0: CSFLogInfo(logTag, "setSecureCachePath(%s)", secureCachePath.c_str()); michael@0: this->secureCachePath = secureCachePath; michael@0: } michael@0: michael@0: // Add local codecs michael@0: void CallControlManagerImpl::setAudioCodecs(int codecMask) michael@0: { michael@0: CSFLogDebug(logTag, "setAudioCodecs %X", codecMask); michael@0: michael@0: VcmSIPCCBinding::setAudioCodecs(codecMask); michael@0: } michael@0: michael@0: void CallControlManagerImpl::setVideoCodecs(int codecMask) michael@0: { michael@0: CSFLogDebug(logTag, "setVideoCodecs %X", codecMask); michael@0: michael@0: VcmSIPCCBinding::setVideoCodecs(codecMask); michael@0: } michael@0: michael@0: AuthenticationStatusEnum::AuthenticationStatus CallControlManagerImpl::getAuthenticationStatus() michael@0: { michael@0: return authenticationStatus; michael@0: } michael@0: michael@0: bool CallControlManagerImpl::registerUser( const std::string& deviceName, const std::string& user, const std::string& password, const std::string& domain ) michael@0: { michael@0: setConnectionState(ConnectionStatusEnum::eRegistering); michael@0: michael@0: CSFLogInfo(logTag, "registerUser(%s, %s )", user.c_str(), domain.c_str()); michael@0: if(phone != nullptr) michael@0: { michael@0: setConnectionState(ConnectionStatusEnum::eReady); michael@0: michael@0: CSFLogError(logTag, "registerUser() failed - already connected!"); michael@0: return false; michael@0: } michael@0: michael@0: softPhone = CC_SIPCCServicePtr(new CC_SIPCCService()); michael@0: phone = softPhone; michael@0: phone->init(user, password, domain, deviceName); michael@0: softPhone->setLoggingMask(sipccLoggingMask); michael@0: phone->addCCObserver(this); michael@0: michael@0: phone->setP2PMode(false); michael@0: michael@0: bool bStarted = phone->startService(); michael@0: if (!bStarted) { michael@0: setConnectionState(ConnectionStatusEnum::eFailed); michael@0: } else { michael@0: setConnectionState(ConnectionStatusEnum::eReady); michael@0: } michael@0: michael@0: return bStarted; michael@0: } michael@0: michael@0: bool CallControlManagerImpl::startP2PMode(const std::string& user) michael@0: { michael@0: setConnectionState(ConnectionStatusEnum::eRegistering); michael@0: michael@0: CSFLogInfo(logTag, "startP2PMode(%s)", user.c_str()); michael@0: if(phone != nullptr) michael@0: { michael@0: setConnectionState(ConnectionStatusEnum::eReady); michael@0: michael@0: CSFLogError(logTag, "startP2PMode() failed - already started in p2p mode!"); michael@0: return false; michael@0: } michael@0: michael@0: softPhone = CC_SIPCCServicePtr(new CC_SIPCCService()); michael@0: phone = softPhone; michael@0: phone->init(user, "", "127.0.0.1", "sipdevice"); michael@0: softPhone->setLoggingMask(sipccLoggingMask); michael@0: phone->addCCObserver(this); michael@0: michael@0: phone->setP2PMode(true); michael@0: michael@0: bool bStarted = phone->startService(); michael@0: if (!bStarted) { michael@0: setConnectionState(ConnectionStatusEnum::eFailed); michael@0: } else { michael@0: setConnectionState(ConnectionStatusEnum::eReady); michael@0: } michael@0: michael@0: return bStarted; michael@0: } michael@0: michael@0: bool CallControlManagerImpl::startSDPMode() michael@0: { michael@0: CSFLogInfo(logTag, "startSDPMode"); michael@0: if(phone != nullptr) michael@0: { michael@0: CSFLogError(logTag, "%s failed - already started in SDP mode!",__FUNCTION__); michael@0: return false; michael@0: } michael@0: softPhone = CC_SIPCCServicePtr(new CC_SIPCCService()); michael@0: phone = softPhone; michael@0: phone->init("JSEP", "", "127.0.0.1", "sipdevice"); michael@0: softPhone->setLoggingMask(sipccLoggingMask); michael@0: phone->addCCObserver(this); michael@0: phone->setSDPMode(true); michael@0: michael@0: return phone->startService(); michael@0: } michael@0: michael@0: bool CallControlManagerImpl::disconnect() michael@0: { michael@0: CSFLogInfo(logTag, "disconnect()"); michael@0: if(phone == nullptr) michael@0: return true; michael@0: michael@0: connectionState = ConnectionStatusEnum::eIdle; michael@0: phone->removeCCObserver(this); michael@0: phone->stop(); michael@0: phone->destroy(); michael@0: phone = nullptr; michael@0: softPhone = nullptr; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: std::string CallControlManagerImpl::getPreferredDeviceName() michael@0: { michael@0: return preferredDevice; michael@0: } michael@0: michael@0: std::string CallControlManagerImpl::getPreferredLineDN() michael@0: { michael@0: return preferredLineDN; michael@0: } michael@0: michael@0: ConnectionStatusEnum::ConnectionStatus CallControlManagerImpl::getConnectionStatus() michael@0: { michael@0: return connectionState; michael@0: } michael@0: michael@0: std::string CallControlManagerImpl::getCurrentServer() michael@0: { michael@0: return ""; michael@0: } michael@0: michael@0: // Currently controlled device michael@0: CC_DevicePtr CallControlManagerImpl::getActiveDevice() michael@0: { michael@0: if(phone != nullptr) michael@0: return phone->getActiveDevice(); michael@0: michael@0: return CC_DevicePtr(); michael@0: } michael@0: michael@0: // All known devices michael@0: PhoneDetailsVtrPtr CallControlManagerImpl::getAvailablePhoneDetails() michael@0: { michael@0: PhoneDetailsVtrPtr result = PhoneDetailsVtrPtr(new PhoneDetailsVtr()); michael@0: for(PhoneDetailsMap::iterator it = phoneDetailsMap.begin(); it != phoneDetailsMap.end(); it++) michael@0: { michael@0: PhoneDetailsPtr details = it->second.get(); michael@0: result->push_back(details); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: PhoneDetailsPtr CallControlManagerImpl::getAvailablePhoneDetails(const std::string& deviceName) michael@0: { michael@0: PhoneDetailsMap::iterator it = phoneDetailsMap.find(deviceName); michael@0: if(it != phoneDetailsMap.end()) michael@0: { michael@0: return it->second.get(); michael@0: } michael@0: return PhoneDetailsPtr(); michael@0: } michael@0: // Media setup michael@0: VideoControlPtr CallControlManagerImpl::getVideoControl() michael@0: { michael@0: if(phone != nullptr) michael@0: return phone->getVideoControl(); michael@0: michael@0: return VideoControlPtr(); michael@0: } michael@0: michael@0: AudioControlPtr CallControlManagerImpl::getAudioControl() michael@0: { michael@0: if(phone != nullptr) michael@0: return phone->getAudioControl(); michael@0: michael@0: return AudioControlPtr(); michael@0: } michael@0: michael@0: bool CallControlManagerImpl::setProperty(ConfigPropertyKeysEnum::ConfigPropertyKeys key, std::string& value) michael@0: { michael@0: unsigned long strtoul_result; michael@0: char *strtoul_end; michael@0: michael@0: CSFLogInfo(logTag, "setProperty( %s )", value.c_str()); michael@0: michael@0: if (key == ConfigPropertyKeysEnum::eLocalVoipPort) { michael@0: errno = 0; michael@0: strtoul_result = strtoul(value.c_str(), &strtoul_end, 10); michael@0: michael@0: if (errno || value.c_str() == strtoul_end || strtoul_result > USHRT_MAX) { michael@0: return false; michael@0: } michael@0: michael@0: CCAPI_Config_set_local_voip_port((int) strtoul_result); michael@0: } else if (key == ConfigPropertyKeysEnum::eRemoteVoipPort) { michael@0: errno = 0; michael@0: strtoul_result = strtoul(value.c_str(), &strtoul_end, 10); michael@0: michael@0: if (errno || value.c_str() == strtoul_end || strtoul_result > USHRT_MAX) { michael@0: return false; michael@0: } michael@0: michael@0: CCAPI_Config_set_remote_voip_port((int) strtoul_result); michael@0: } else if (key == ConfigPropertyKeysEnum::eTransport) { michael@0: if (value == "tcp") michael@0: CCAPI_Config_set_transport_udp(false); michael@0: else michael@0: CCAPI_Config_set_transport_udp(true); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: std::string CallControlManagerImpl::getProperty(ConfigPropertyKeysEnum::ConfigPropertyKeys key) michael@0: { michael@0: std::string retValue = "NONESET"; michael@0: char tmpString[11]; michael@0: michael@0: CSFLogInfo(logTag, "getProperty()"); michael@0: michael@0: if (key == ConfigPropertyKeysEnum::eLocalVoipPort) { michael@0: csf_sprintf(tmpString, sizeof(tmpString), "%u", CCAPI_Config_get_local_voip_port()); michael@0: retValue = tmpString; michael@0: } else if (key == ConfigPropertyKeysEnum::eRemoteVoipPort) { michael@0: csf_sprintf(tmpString, sizeof(tmpString), "%u", CCAPI_Config_get_remote_voip_port()); michael@0: retValue = tmpString; michael@0: } else if (key == ConfigPropertyKeysEnum::eVersion) { michael@0: const char* version = CCAPI_Config_get_version(); michael@0: retValue = version; michael@0: } michael@0: michael@0: return retValue; michael@0: } michael@0: /* michael@0: There are a number of factors that determine PhoneAvailabilityType::PhoneAvailability. The supported states for this enum are: michael@0: { eUnknown, eAvailable, eUnAvailable, eNotAllowed }. eUnknown is the default value, which is set when there is no information michael@0: available that would otherwise determine the availability value. The factors that can influence PhoneAvailability are: michael@0: phone mode, and for a given device (described by DeviceInfo) the model, and the name of the device. For phone control mode, the michael@0: device registration and whether CUCM says the device is CTI controllable (or not) is a factor. michael@0: michael@0: For Phone Control mode the state machine is: michael@0: michael@0: is blacklisted model name? -> Yes -> NOT_ALLOWED michael@0: (see Note1 below) michael@0: || michael@0: \/ michael@0: No michael@0: || michael@0: \/ michael@0: is CTI Controllable? michael@0: (determined from CUCM) -> No -> NOT_ALLOWED michael@0: || michael@0: \/ michael@0: Yes michael@0: || michael@0: \/ michael@0: Can we tell if it's registered? -> No -> ?????? TODO: Seems to depends on other factors (look at suggestedAvailability parameter michael@0: || in DeviceSubProviderImpl.addOrUpdateDevice() in CSF1G Java code. michael@0: \/ michael@0: Yes michael@0: || michael@0: \/ michael@0: is Registered? michael@0: (determined from CUCM) -> No -> NOT_AVAILABLE michael@0: || michael@0: \/ michael@0: Yes michael@0: || michael@0: \/ michael@0: AVAILABLE michael@0: michael@0: ======== michael@0: michael@0: For Softphone mode the state machine is: michael@0: michael@0: is device excluded? michael@0: (based on "ExcludedDevices" -> Yes -> NOT_ALLOWED michael@0: config settings michael@0: (see Note2 below)) michael@0: || michael@0: \/ michael@0: No michael@0: || michael@0: \/ michael@0: isSoftphone? michael@0: michael@0: michael@0: michael@0: Note1: model name has to match completely, ie it's not a sub-string match, but we are ignoring case. So, if the blacklist michael@0: contains a string "Cisco Unified Personal Communicator" then the model has to match this completely (but can be a michael@0: different case) to be a match. In CSF1G the blacklist is hard-wired to: michael@0: { "Cisco Unified Personal Communicator", michael@0: "Cisco Unified Client Services Framework", michael@0: "Client Services Framework", michael@0: "Client Services Core" } michael@0: michael@0: Note2: The "ExcludedDevices" is a comma-separated list of device name prefixes (not model name). Unlike the above, this is michael@0: a sub-string match, but only a "starts with" sub-string match, not anywhere in the string. If the device name michael@0: is a complete match then this is also excluded, ie doesn't have to be a sub-string. For example, if the michael@0: ExcludeDevices list contains { "ECP", "UPC" } then assuming we're in softphone mode, then any device whose michael@0: name starts with the strings ECP or UPC, or whose complete name is either of these will be deemed to be excluded michael@0: and will be marked as NOT_ALLOWED straightaway. In Phone Control mode the "ExcludedDevices" list i not taken into michael@0: account at all in the determination of availability. michael@0: michael@0: Note3: isSoftphone() function michael@0: michael@0: The config service provides a list of "blacklisted" device name prefixes, that is, if the name of the device starts with a michael@0: sub-string that matches an entry in the blacklist, then it is straightaway removed from the list? marked as NOT_ALLOWED. michael@0: */ michael@0: michael@0: // CC_Observers michael@0: void CallControlManagerImpl::onDeviceEvent(ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_DeviceInfoPtr info) michael@0: { michael@0: notifyDeviceEventObservers(deviceEvent, devicePtr, info); michael@0: } michael@0: void CallControlManagerImpl::onFeatureEvent(ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_FeatureInfoPtr info) michael@0: { michael@0: notifyFeatureEventObservers(deviceEvent, devicePtr, info); michael@0: } michael@0: void CallControlManagerImpl::onLineEvent(ccapi_line_event_e lineEvent, CC_LinePtr linePtr, CC_LineInfoPtr info) michael@0: { michael@0: notifyLineEventObservers(lineEvent, linePtr, info); michael@0: } michael@0: void CallControlManagerImpl::onCallEvent(ccapi_call_event_e callEvent, CC_CallPtr callPtr, CC_CallInfoPtr info) michael@0: { michael@0: notifyCallEventObservers(callEvent, callPtr, info); michael@0: } michael@0: michael@0: michael@0: void CallControlManagerImpl::notifyDeviceEventObservers (ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_DeviceInfoPtr info) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: set::const_iterator it = ccObservers.begin(); michael@0: for ( ; it != ccObservers.end(); it++ ) michael@0: { michael@0: (*it)->onDeviceEvent(deviceEvent, devicePtr, info); michael@0: } michael@0: } michael@0: michael@0: void CallControlManagerImpl::notifyFeatureEventObservers (ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_FeatureInfoPtr info) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: set::const_iterator it = ccObservers.begin(); michael@0: for ( ; it != ccObservers.end(); it++ ) michael@0: { michael@0: (*it)->onFeatureEvent(deviceEvent, devicePtr, info); michael@0: } michael@0: } michael@0: michael@0: void CallControlManagerImpl::notifyLineEventObservers (ccapi_line_event_e lineEvent, CC_LinePtr linePtr, CC_LineInfoPtr info) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: set::const_iterator it = ccObservers.begin(); michael@0: for ( ; it != ccObservers.end(); it++ ) michael@0: { michael@0: (*it)->onLineEvent(lineEvent, linePtr, info); michael@0: } michael@0: } michael@0: michael@0: void CallControlManagerImpl::notifyCallEventObservers (ccapi_call_event_e callEvent, CC_CallPtr callPtr, CC_CallInfoPtr info) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: set::const_iterator it = ccObservers.begin(); michael@0: for ( ; it != ccObservers.end(); it++ ) michael@0: { michael@0: (*it)->onCallEvent(callEvent, callPtr, info); michael@0: } michael@0: } michael@0: michael@0: void CallControlManagerImpl::notifyAvailablePhoneEvent (AvailablePhoneEventType::AvailablePhoneEvent event, michael@0: const PhoneDetailsPtr availablePhoneDetails) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: set::const_iterator it = eccObservers.begin(); michael@0: for ( ; it != eccObservers.end(); it++ ) michael@0: { michael@0: (*it)->onAvailablePhoneEvent(event, availablePhoneDetails); michael@0: } michael@0: } michael@0: michael@0: void CallControlManagerImpl::notifyAuthenticationStatusChange (AuthenticationStatusEnum::AuthenticationStatus status) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: set::const_iterator it = eccObservers.begin(); michael@0: for ( ; it != eccObservers.end(); it++ ) michael@0: { michael@0: (*it)->onAuthenticationStatusChange(status); michael@0: } michael@0: } michael@0: michael@0: void CallControlManagerImpl::notifyConnectionStatusChange(ConnectionStatusEnum::ConnectionStatus status) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: set::const_iterator it = eccObservers.begin(); michael@0: for ( ; it != eccObservers.end(); it++ ) michael@0: { michael@0: (*it)->onConnectionStatusChange(status); michael@0: } michael@0: } michael@0: michael@0: void CallControlManagerImpl::setConnectionState(ConnectionStatusEnum::ConnectionStatus status) michael@0: { michael@0: connectionState = status; michael@0: notifyConnectionStatusChange(status); michael@0: } michael@0: }