/*---------------------------------------------------------------------------*/ /* signaling_app.cpp */ /* copyright (c) innovaphone 2021 */ /* */ /*---------------------------------------------------------------------------*/ #include "platform/platform.h" #include "common/os/iomux.h" #include "common/interface/socket.h" #include "common/interface/media.h" #include "common/interface/ringer.h" #include "common/interface/random.h" #include "common/interface/json_api.h" #include "common/interface/webserver_plugin.h" #include "common/interface/appwebsocket_client.h" #include "common/interface/websocket_client.h" #include "common/interface/local_storage.h" #include "common/interface/notifications-if.h" #include "common/interface/url_handler.h" #include "common/interface/call_kit.h" #include "common/interface/remote_services.h" #include "common/interface/signaling_app.h" #include "common/interface/task.h" #include "common/interface/stask.h" #include "common/ilib/json.h" #include "common/ilib/uri.h" #include "common/ilib/str.h" #include "common/ilib/hash.h" #include "common/interface/guuid.h" #include "common/interface/http_client.h" #include "common/lib/appservice.h" #include "common/lib/appwebsocket.h" #include "common/lib/app_updates.h" #include "common/lib/apiwebsocket_client.h" #include "common/lib/language.h" #include "client/lang/myapps_languages.h" #include "launcherapi/launcherapi.h" #include "signaling/signaling_api.h" #include "signaling/signaling.h" #include "signaling_app.h" SignalingApp::SignalingApp(IIoMux * const iomux, class ULauncherApi* launcherApi, class ISocketProvider * localSocketProvider, class ISocketProvider * tcpSocketProvider, class ISocketProvider * tlsSocketProvider, class IDns * dns, class INotifications* notifications, const char * apiRelayUrl, const char * servicePassword, const char * workingPath, const char * apiRelayPath, class ICallKit * callKit, class USignalingApp * signalingAppUser) : ApiWebsocketClient(iomux, localSocketProvider, tcpSocketProvider, tlsSocketProvider, dns, this, apiRelayUrl, servicePassword, "SignalingApp", apiRelayPath), defProviderTimer(iomux, this) { iomux->RegisterShutdownHandler(this); this->closing = false; this->closed = false; this->iomux = iomux; this->notifications = notifications; this->signaling = nullptr; this->signalingAvatarConsumer = nullptr; this->signalingPhoneConsumer = nullptr; this->signalingLookupConsumer = nullptr; this->langId = 0; this->launcherApi = launcherApi; this->callKit = callKit; this->signalingAppUser = signalingAppUser; this->url = ILocalStorage::GetItem("SignalingServiceUrl"); this->sec = ILocalStorage::GetItem("SignalingServiceSec"); this->phys = ILocalStorage::GetItem("SignalingServicePhys"); this->app = ILocalStorage::GetItem("SignalingServiceApp"); this->usr = ILocalStorage::GetItem("SignalingServiceUsr"); this->pwd = ILocalStorage::GetItem("SignalingServicePwd"); this->domain = ILocalStorage::GetItem("SignalingServiceDomain"); this->appdomain = ILocalStorage::GetItem("SignalingServiceAppDomain"); char * t = ILocalStorage::GetItem("SignalingServiceTimeout"); if (t) { this->timeout = atoi(t); free(t); } else this->timeout = 0; this->hw = ILocalStorage::GetItem("SignalingServiceHw"); // Empty string if (this->hw) { bool delHwString = false; if(!this->hw[0]) delHwString = true; // Empty else { delHwString = true; // Only whitespaces for (unsigned int i = 0; i < strlen(this->hw); i++) { if (this->hw[i] != 0x20) { delHwString = false; break; } } } if (delHwString) { free(this->hw); this->hw = NULL; } } t = ILocalStorage::GetItem("SignalingServiceHwTimeout"); if (t) { this->hwTimeout = atoi(t); free(t); } else this->hwTimeout = 0; this->cxSupported = false; this->uiRunning = 0; this->uuidTerminateNotify = NULL; this->uuidTerminateNotifyConf = NULL; this->defaultApiProvider = NULL; this->defaultApiProviderRemoved = false; this->workingPath = _strdup(workingPath); this->dnd = false; this->remoteServices = false; this->registered = false; this->windowTitle = _strdup(innovaphoneMyapps); } SignalingApp::~SignalingApp() { this->defProviderTimer.Cancel(); if (this->url) free(this->url); if (this->sec) free(this->sec); if (this->phys) free(this->phys); if (this->app) free(this->app); if (this->usr) free(this->usr); if (this->pwd) free(this->pwd); if (this->domain) free(this->domain); if (this->appdomain) free(this->appdomain); if (this->hw) free(this->hw); if (this->uuidTerminateNotify) free(this->uuidTerminateNotify); if (this->uuidTerminateNotifyConf) free(this->uuidTerminateNotifyConf); if (this->defaultApiProvider) free(this->defaultApiProvider); if (this->windowTitle) free(this->windowTitle); while (!calls.empty()) delete calls.front(); while (!phoneApps.empty()) delete phoneApps.front(); } void SignalingApp::Shutdown() { debug->printf("SignalingApp::Shutdown"); TryClose(); } void SignalingApp::TryClose() { debug->printf("SignalingApp::TryClose stopping=%d", this->closing); if (!this->closing) { this->closing = true; if (uuidTerminateNotify) { char ref[256]; _sprintf(ref, "drop:%s", uuidTerminateNotify); RemoveNotification(ref); _sprintf(ref, "hold:%s", uuidTerminateNotify); RemoveNotification(ref); free(uuidTerminateNotify); uuidTerminateNotify = NULL; } if (uuidTerminateNotifyConf) { char ref[256]; _sprintf(ref, "drop:%s", uuidTerminateNotifyConf); RemoveNotification(ref); _sprintf(ref, "hold:%s", uuidTerminateNotifyConf); RemoveNotification(ref); free(uuidTerminateNotifyConf); uuidTerminateNotifyConf = NULL; } ApiWebsocketClientClose(); } if (this->closed) { iomux->ShutdownComplete(this); } } void SignalingApp::ApiWebsocketClientCloseComplete() { debug->printf("SignalingApp::ApiWebsocketClientCloseComplete"); this->closed = true; TryClose(); } void SignalingApp::SetSignaling(ISignaling * signaling) { debug->printf("SignalingApp(%p)::SetSignaling signaling: %p url: %s sec: %s phys: %s app: %s usr: %s pwd: %s domain: %s hw: %s timeout: %d", this, signaling, url, sec ? sec : "", phys ? phys : "", app, usr, pwd, domain, hw, timeout); this->signaling = signaling; this->signalingPhoneConsumer = new class SignalingApiPhoneConsumer(this); this->signalingAvatarConsumer = new class SignalingApiAvatarConsumer(this); this->signalingLookupConsumer = new class SignalingApiLookupConsumer(this); if (url && *url && app && *app && usr && *usr && pwd && *pwd && domain && *domain) { signaling->SetClient(url, sec, phys, app, usr, pwd, domain, appdomain, timeout, true); } signaling->SetWorkingPath(this->workingPath); } void SignalingApp::SetNoVpnAddresses(bool noVpnAddresses) { if(signaling) signaling->SetNoVpnAddresses(noVpnAddresses); } void SignalingApp::SetRecordingParameters(const char* url, bool forceUrl, bool isRecordingByDefaultOn, bool forceDefaultOn, bool recordExternalOnly, bool forceExternalOnly) { if (signaling) signaling->SetRecordingParameters(url, true, forceUrl, isRecordingByDefaultOn, forceDefaultOn, recordExternalOnly, forceExternalOnly); } void SignalingApp::SignalingClosing(class ISignaling *signaling) { this->signaling = NULL; } void SignalingApp::ClientState(bool up, bool localInit) { debug->printf("SignalingApp::ClientState:%s localInit:%d this->hw:%s", up ? "true" : "false", localInit, this->hw ? this->hw : "-"); if (localInit && up && this->hw) signaling->SetRegistration(this->hw, this->phys, hwTimeout); } void SignalingApp::RegistrationState(bool up) { debug->printf("SignalingApp::RegistrationState %s", up ? "true" : "false"); if (signalingAppUser) signalingAppUser->SignalingRegistrationState(up); this->registered = up; } void SignalingApp::SaveCredentials(const char* url, const char* sec, const char* phys, const char* app, const char* usr, const char* pwd, const char * domain, const char * appdomain, unsigned timeout) { debug->printf("SignalingApp::SaveCredentials url=%s sec=%s phys=%s app=%s usr=%s pwd=%s domain=%s appdomain=%s timeout=%d hw:(%s),(%s)", url ? url : "", sec ? sec : "", phys ? phys : "", app ? app : "", usr ? usr : "", pwd ? pwd : "", domain ? domain : "", appdomain ? appdomain : "", timeout, this->hw ? this->hw : "-", this->defaultApiProvider ? this->defaultApiProvider : "-"); if (url && app && usr && pwd && domain) { if (this->url) free(this->url); this->url = _strdup(url); if (this->sec) free(this->sec); this->sec = _strdup(sec); if (this->phys) free(this->phys); this->phys = _strdup(phys); if (this->app) free(this->app); this->app = _strdup(app); if (this->usr) free(this->usr); this->usr = _strdup(usr); if (this->pwd) free(this->pwd); this->pwd = _strdup(pwd); if (this->domain) free(this->domain); this->domain = _strdup(domain); if (this->appdomain) free(this->appdomain); this->appdomain = _strdup(appdomain); this->timeout = timeout; } } void SignalingApp::SaveRegistration(const char* hw, const char* phys, unsigned timeout) { debug->printf("SignalingApp::SaveRegistration hw=(%s),(%s),(%s) phys=%s timeout=%d", hw ? hw : "-", this->hw ? this->hw : "-", this->defaultApiProvider ? this->defaultApiProvider : "-", phys ? phys : "-", timeout); const char* tmpDefPhoneApp; if (defaultApiProvider && !strncmp(defaultApiProvider, "dev:", 4)) tmpDefPhoneApp = &this->defaultApiProvider[4]; else tmpDefPhoneApp = this->defaultApiProvider; // SaveRegistration only if hw is default Softphone App if (hw && tmpDefPhoneApp && !strcmp(tmpDefPhoneApp, hw)) { if (this->hw) free(this->hw); this->hw = _strdup(hw); ILocalStorage::SetItem("SignalingServiceHw", hw); this->hwTimeout = timeout; char t[32]; _sprintf(t, "%d", this->hwTimeout); ILocalStorage::SetItem("SignalingServiceHwTimeout", t); if (url) ILocalStorage::SetItem("SignalingServiceUrl", url); if (sec) ILocalStorage::SetItem("SignalingServiceSec", sec); else ILocalStorage::SetItem("SignalingServiceSec", ""); if (this->phys) free(this->phys); this->phys = _strdup(phys); if (phys) ILocalStorage::SetItem("SignalingServicePhys", phys); else ILocalStorage::SetItem("SignalingServicePhys", ""); if (app) ILocalStorage::SetItem("SignalingServiceApp", app); if (usr) ILocalStorage::SetItem("SignalingServiceUsr", usr); if (pwd) ILocalStorage::SetItem("SignalingServicePwd", pwd); if (domain) ILocalStorage::SetItem("SignalingServiceDomain", domain); if (appdomain) ILocalStorage::SetItem("SignalingServiceAppDomain", appdomain); _sprintf(t, "%d", this->timeout); ILocalStorage::SetItem("SignalingServiceTimeout", t); } const char * t = ILocalStorage::GetItem("cxSupported"); if(!t) cxSupported = false; else if(!strcmp(t, "1")) cxSupported = true; else cxSupported = false; } void SignalingApp::LauncherApiSetLangId(const char* langId) { //debug->printf("SignalingApp(%p)::LauncherApiSetLangId lang=%s", this, langId ? langId : ""); this->langId = Language::GetLanguageId(langId, myappsLanguageTable, MYAPPS_NUM_LANGUAGES, MYAPPS_LANGUAGE_EN); } bool SignalingApp::StartCall(const char * sip, const char * num, const char * name, StartCallVideoMode videoMode, bool sendingComplete) { debug->printf("SignalingApp::StartCall sip:%s num:%s name:%s", sip ? sip: "-", num ? num : "-", name ? name : "-"); if (!IsSoftPhoneStandardApp()) return false; if ((num || sip || name) && signaling) { signaling->PhoneDialNumber(num, sip ? sip : name, videoMode, sip ? true : sendingComplete); return true; } return false; } void SignalingApp::CxSupported(bool supported) { debug->printf("SignalingApp::CxSupported cxSupported: %s", supported ? "true" : "false"); cxSupported = supported; const char * cxSupportedString = ILocalStorage::GetItem("cxSupported"); if (cxSupportedString && strcmp(cxSupportedString, cxSupported ? "1" : "0")) ILocalStorage::SetItem("cxSupported", cxSupported ? "1" : "0"); else if (!cxSupportedString) ILocalStorage::SetItem("cxSupported", cxSupported ? "1" : "0"); } void SignalingApp::CxStartCallAction(const char * uuid, const char * number, const char * name, bool video) { debug->printf("SignalingApp::CxStartCallAction"); if(signaling) signaling->PhoneStartCallAction(uuid, number, name, video, true); } void SignalingApp::CxAnswerCallAction(const char * uuid) { if(signaling) signaling->PhoneAnswerCallAction(uuid); } void SignalingApp::CxEndCallAction(const char * uuid, enum CxCallError callError) { if(signaling) signaling->PhoneEndCallAction(uuid, callError); } void SignalingApp::CxSetHeldCallAction(const char * uuid, bool held) { if(signaling){ if(held) signaling->PhoneHoldCallAction(uuid); else signaling->PhoneRetrieveCallAction(uuid); } } void SignalingApp::CxSetMutedCallAction(const char * uuid, bool muted) { if(signaling) signaling->PhoneSetMutedCallAction(uuid, muted); } void SignalingApp::CxAudioRouteChanged(const char *uuid, const char * deviceId) { if(signaling) signaling->PhoneAudioRouteChanged(uuid, deviceId); } void SignalingApp::CxSetGroupCallAction(const char * uuid, const char * with) { } void SignalingApp::CxPlayDTMFCallAction(const char * uuid, const char * digits) { if(signaling) signaling->PhonePlayDtmfCallAction(uuid, digits); } void SignalingApp::CxCallError(const char * uuid, enum CxCallError callError) { if(signaling) signaling->PhoneEndCallAction(uuid, callError); } void SignalingApp::CxShowIncomingCallUi(const char * uuid) { if (signaling) signaling->PhoneStartRinging(uuid); } void SignalingApp::CxCheckIncomingCall(const char * uuid) { if (signaling) signaling->PhoneCheckIncomingCall(uuid); } void SignalingApp::Lookup(const char * src, const char * number, const char * prefixIntl, const char * prefixNtl, const char * prefixExt, const char * country, const char * area, const char * subscriber) { return signalingLookupConsumer->Lookup(src, number, prefixIntl, prefixNtl, prefixExt, country, area, subscriber); } char* SignalingApp::GetAvatarUrl(const char* sip, const char* dn, const char* domain, const char* appdomain) { return signalingAvatarConsumer->GetAvatarUrl(sip, dn, domain, appdomain); } void SignalingApp::PhoneAppAvailable(const char * hw, const char * app, const char * title, const char * url) { if (!phoneApps.empty()) { class SignalingAppPhoneApps* phoneApp = phoneApps.front(); while (phoneApp) { if (((phoneApp->hw && hw && !strcmp(phoneApp->hw, hw)) || (!phoneApp->hw && !hw)) && ((phoneApp->app && app && !strcmp(phoneApp->app, app)) || (!phoneApp->app && !app)) && ((phoneApp->title && title && !strcmp(phoneApp->title, title)) || (!phoneApp->title && !title)) && ((phoneApp->url && url && !strcmp(phoneApp->url, url)) || (!phoneApp->url && !url))) return; phoneApp = phoneApp->goNext(); } } debug->printf("SignalingApp::PhoneAppAvailable hw=%s app=%s title=%s urls=%s", hw ? hw : "-", app ? app : "-", title ? title : "-", url ? url : "-"); phoneApps.push_back(new class SignalingAppPhoneApps(hw, app, title, url)); } void SignalingApp::DefaultApiProvider(const char * defaultApiProvider) { if(!this->defaultApiProvider || !defaultApiProvider || strcmp(defaultApiProvider, this->defaultApiProvider)) debug->printf("SignalingApp::DefaultApiProvider provider: %s old: %s", defaultApiProvider ? defaultApiProvider : "-", this->defaultApiProvider ? this->defaultApiProvider : "-"); if (defaultApiProvider) { if (!phoneApps.empty()) { class SignalingAppPhoneApps* phoneApp = phoneApps.front(); while (phoneApp) { // Ignore phoneApps if (phoneApp->app && !strcmp(phoneApp->app, "softphone")) { // DefaultApiProvider as softphoneApp if (phoneApp->hw && !strcmp(phoneApp->hw, defaultApiProvider)) { debug->printf("SignalingApp::DefaultApiProvider provider: %s hw: %s (%s) phoneApp: %s registered=%d uiRunning=%d", defaultApiProvider, phoneApp->hw, this->hw ? this->hw : "-", phoneApp->app, this->registered, uiRunning); const char* tmpDefPhoneApp; if (defaultApiProvider && !strncmp(defaultApiProvider, "dev:", 4)) tmpDefPhoneApp = &defaultApiProvider[4]; else tmpDefPhoneApp = defaultApiProvider; // No hw still set or different provider (provider changed or new user logged in) if (!this->hw || strcmp(this->hw, tmpDefPhoneApp)) { // uiRunning false. Otherwise we opened a second softphone App instance if (!uiRunning) { signalingPhoneConsumer->ConfigureSoftphone(defaultApiProvider); if (registered) { signaling->CloseClient(); registered = false; } } if (this->defaultApiProvider) free(this->defaultApiProvider); this->defaultApiProvider = _strdup(defaultApiProvider); this->defaultApiProviderRemoved = false; } else if (this->hw && !strcmp(this->hw, tmpDefPhoneApp) && !this->defaultApiProvider) { if (url && app && usr && pwd && hw && domain) { signaling->SetClient(url, sec, phys, app, usr, pwd, domain, appdomain, timeout, true); this->defaultApiProvider = _strdup(defaultApiProvider); this->defaultApiProviderRemoved = false; } } break; } } // Check if a phoneApp is the new provider else if (phoneApp->app && phoneApp->hw && !strcmp(phoneApp->app, "phone") && !strcmp(phoneApp->hw, defaultApiProvider)) { debug->printf("SignalingApp::DefaultApiProvider provider changed: %s -> %s uiRunning: %d calls: %d registered: %d", this->defaultApiProvider ? this->defaultApiProvider : "-", defaultApiProvider, uiRunning, signaling->NumActiveCalls(), this->registered); if (this->defaultApiProvider || this->registered) { if (!uiRunning && !signaling->NumActiveCalls()) { signaling->CloseClient(); if (this->defaultApiProvider) free(this->defaultApiProvider); this->defaultApiProvider = NULL; this->defaultApiProviderRemoved = false; } else this->defaultApiProviderRemoved = true; // Remove settings to avoid a registration during myApps start if standard app was changed ILocalStorage::SetItem("SignalingServiceUrl", ""); ILocalStorage::SetItem("SignalingServiceSec", ""); ILocalStorage::SetItem("SignalingServiceUsr", ""); ILocalStorage::SetItem("SignalingServicePwd", ""); ILocalStorage::SetItem("SignalingServiceHw", ""); ILocalStorage::SetItem("SignalingServicePhys", ""); if (this->hw) free(this->hw); this->hw = NULL; } } phoneApp = phoneApp->goNext(); } } } else if (this->defaultApiProvider || this->registered) { debug->printf("SignalingApp::DefaultApiProvider provider removed: %s uiRunning: %d calls: %d registered: %d", this->defaultApiProvider ? this->defaultApiProvider : "-", uiRunning, signaling->NumActiveCalls(), this->registered); if (!uiRunning && !signaling->NumActiveCalls()) { signaling->CloseClient(); if (this->defaultApiProvider) free(this->defaultApiProvider); this->defaultApiProvider = NULL; this->defaultApiProviderRemoved = false; } else this->defaultApiProviderRemoved = true; // Remove settings to avoid a registration during myApps start if standard app was removed ILocalStorage::SetItem("SignalingServiceUrl", ""); ILocalStorage::SetItem("SignalingServiceSec", ""); ILocalStorage::SetItem("SignalingServiceUsr", ""); ILocalStorage::SetItem("SignalingServicePwd", ""); ILocalStorage::SetItem("SignalingServiceHw", ""); ILocalStorage::SetItem("SignalingServicePhys", ""); if (this->hw) free(this->hw); this->hw = NULL; } } void SignalingApp::LookupInfo(const char * src, class json_io& msg, word base) { signaling->LookupInfo(src, msg, base); } void SignalingApp::ReportNewIncomingCall(const char * uuid, const char * number, const char * sip, const char * name, const char * diverting, const char * called, bool hasVideo, bool supportsDTMF, bool supportsHolding, bool supportsGrouping, bool supportsUngrouping) { debug->printf("SignalingApp::ReportNewIncomingCall uuid: %s number: %s name: %s", uuid, number, name); SignalingAppCall *c = NULL; if (!calls.empty()) { for (c = calls.front(); c; c = c->goNext()) { if (uuid && c->uuid && !strcmp(uuid, c->uuid)) break; } } if (!c) { if (calls.empty()) { if (signalingAppUser) signalingAppUser->SignalingCallsActive(true); } calls.push_back(new class SignalingAppCall(uuid, number, sip, name, diverting, called, true)); } if (!this->dnd) { if (cxSupported) { if (callKit) callKit->CxReportNewIncomingCall(uuid, number, name, diverting, called, hasVideo, supportsDTMF, supportsHolding, supportsGrouping, supportsUngrouping); } else ReportIncomingCall(uuid, name, sip, number, diverting, called, this->app, this->domain, this->appdomain); } } void SignalingApp::ReportCallEnded(const char* uuid, int reason) { debug->printf("SignalingApp::ReportCallEnded uuid: %s reason: %d", uuid, reason); if(cxSupported && callKit) callKit->CxReportCallEnded(uuid, (CxCallEndedReason)reason); if (!calls.empty()) { SignalingAppCall *c; for (c = calls.front(); c; c = c->goNext()) { if (c->uuid && uuid && !strcmp(c->uuid, uuid)) { if (c->connected == false) { if (c->incoming) { if (!cxSupported) RemoveNotification(c->uuid); if (c->hold == false && reason != CALL_ENDED_REASON_ANSWERED_ELSEWHERE && !this->dnd) ReportMissedCall(c->uuid, c->name, c->sip, c->number, c->diverting, c->called, this->app, this->domain, this->appdomain); } } else if (!cxSupported && uuidTerminateNotify && !strcmp(uuidTerminateNotify, uuid)) { char ref[256]; _sprintf(ref, "drop:%s", uuidTerminateNotify); RemoveNotification(ref); _sprintf(ref, "hold:%s", uuidTerminateNotify); RemoveNotification(ref); free(uuidTerminateNotify); uuidTerminateNotify = NULL; } else if (!cxSupported && uuidTerminateNotifyConf && !strcmp(uuidTerminateNotifyConf, uuid)) { char ref[256]; _sprintf(ref, "drop:%s", uuidTerminateNotifyConf); RemoveNotification(ref); _sprintf(ref, "hold:%s", uuidTerminateNotifyConf); RemoveNotification(ref); free(uuidTerminateNotifyConf); uuidTerminateNotifyConf = NULL; } c->remove(); delete c; if (calls.empty()) { if (signalingAppUser) signalingAppUser->SignalingCallsActive(false); } break; } } } if (!cxSupported && uuid) { if(uuidTerminateNotify && !strcmp(uuidTerminateNotify, uuid)) { free(uuidTerminateNotify); uuidTerminateNotify = NULL; } else if(uuidTerminateNotifyConf && !strcmp(uuidTerminateNotifyConf, uuid)) { free(uuidTerminateNotifyConf); uuidTerminateNotifyConf = NULL; } } if (defaultApiProviderRemoved && !uiRunning && signaling->NumActiveCalls() == 1) this->defProviderTimer.Start(500); } void SignalingApp::ReportNameIdentification(const char* uuid, const char* number, const char* sip, const char* name, const char* diverting, const char* called, bool hasVideo, bool supportsDTMF, bool supportsHolding, bool supportsGrouping, bool supportsUngrouping) { if(cxSupported && callKit && !this->dnd) callKit->CxReportCallUpdated(uuid, number, name, diverting, called, hasVideo, supportsDTMF, supportsHolding, supportsGrouping, supportsUngrouping); if (!calls.empty()) { SignalingAppCall* c = NULL; for (c = calls.front(); c; c = c->goNext()) { if (uuid && c->uuid && !strcmp(uuid, c->uuid)) { if (c->incoming && c->hold == false && c->connected == false) { debug->printf("SignalingApp::ReportNameIdentification uuid=%s old=%s new=%s", uuid, c->name ? c->name : "-", name); if (c->name) free(c->name); c->name = _strdup(name); if (!cxSupported) { RemoveNotification(c->uuid); ReportIncomingCall(uuid, name, sip, number, diverting, called, this->app, this->domain, this->appdomain); } } return; } } debug->printf("SignalingApp::ReportNameIdentification no call found for uuid=%s name=%s", uuid, name); } } void SignalingApp::TimerOnTimeout(DefaultApiProviderTimer * timer) { debug->printf("SignalingApp::TimerOnTimeout defaultApiProviderRemoved=%d", defaultApiProviderRemoved); if (defaultApiProviderRemoved) { if (!uiRunning && !signaling->NumActiveCalls()) { signaling->CloseClient(); free(this->defaultApiProvider); this->defaultApiProvider = NULL; defaultApiProviderRemoved = false; } else this->defProviderTimer.Start(500); } } void SignalingApp::SoftphoneAppWindowTitle(const char* title) { debug->printf("SignalingApp::SoftphoneAppWindowTitle title:%s new:%s", windowTitle ? windowTitle : "", title ? title : ""); if (windowTitle) free(windowTitle); windowTitle = _strdup(title); } void SignalingApp::LocalNotificationClicked(const char* ref, enum LocalNotificationAction action, const char* replyText) { debug->printf("SignalingApp::LocalNotificationClicked ref: %s", ref ? ref : ""); if (action && ref) { if (action == LOCAL_NOTIFICATION_ACTION_REJECT) { if (strstr(ref, "hold:")) { const char* tUuid = &ref[5]; signaling->PhoneEndCallAction(tUuid, 0); ReportCallEnded(ref, 0); } else signaling->PhoneEndCallAction(ref, 0); } else if (action == LOCAL_NOTIFICATION_ACTION_ACCEPT) { if (strstr(ref, "sip:")) { if (windowTitle) launcherApi->LauncherApiSetFocus(app, windowTitle); PhoneAnswerCallAction(ref); } else if (strstr(ref, "num:")) { if (windowTitle) launcherApi->LauncherApiSetFocus(app, windowTitle); PhoneAnswerCallAction(ref); } else if (strstr(ref, "drop:")) { const char* tUuid = &ref[5]; signaling->PhoneEndCallAction(tUuid, 0); ReportCallEnded(ref, 0); } else if (strstr(ref, "hold:")) { const char* tUuid = &ref[5]; signaling->PhoneRetrieveCallAction(tUuid); PhoneAnswerCallAction(ref); } else { if (windowTitle) launcherApi->LauncherApiSetFocus(app, windowTitle); PhoneAnswerCallAction(ref); } } } } void SignalingApp::ReportCallConnected(const char * uuid, bool connected, const char* number, const char* sip, const char* name, const char* diverting, const char* called, bool hasVideo, bool supportsDTMF, bool supportsHolding, bool supportsGrouping, bool supportsUngrouping) { debug->printf("SignalingApp::ReportCallConnected uuid: %s connected: %s", uuid, connected ? "true" : "false" ); if(cxSupported && callKit && connected) { callKit->CxReportCallUpdated(uuid, number, name, diverting, called, hasVideo, supportsDTMF, supportsHolding, supportsGrouping, supportsUngrouping); callKit->CxReportOutgoingCallConnected(uuid); } bool found = false; if (!calls.empty()) { SignalingAppCall *c; for (c = calls.front(); c; c = c->goNext()) { if (c->uuid && uuid && !strcmp(c->uuid, uuid)) { if (c->incoming && c->hold == false) RemoveNotification(c->uuid); c->connected = true; c->hold = false; if (!cxSupported && !uiRunning) { NotificationTerminateCall(c->uuid, c->name, c->sip, c->number, c->diverting, c->called, this->app, this->domain, this->appdomain); if(uuidTerminateNotify == NULL) uuidTerminateNotify = _strdup(c->uuid); else if (strcmp(c->uuid, uuidTerminateNotify)) uuidTerminateNotifyConf = _strdup(c->uuid); } found = true; break; } } } if (!found) { if (calls.empty()) { if (signalingAppUser) signalingAppUser->SignalingCallsActive(true); } calls.push_back(new class SignalingAppCall(uuid, number, sip, name, nullptr, nullptr, false)); if (!cxSupported && !uiRunning) { NotificationTerminateCall(uuid, name, sip, number, nullptr, nullptr, this->app, this->domain, this->appdomain); if (uuidTerminateNotify == NULL) uuidTerminateNotify = _strdup(uuid); else if (uuid && strcmp(uuid, uuidTerminateNotify)) uuidTerminateNotifyConf = _strdup(uuid); } } } void SignalingApp::ReportCallHold(const char * uuid) { debug->printf("SignalingApp::ReportCallHold uuid: %s", uuid); if (cxSupported && callKit) callKit->CxRequestSetHeldCall(uuid, true); if (!calls.empty()) { SignalingAppCall *c; for (c = calls.front(); c; c = c->goNext()) { if (c->uuid && uuid && !strcmp(c->uuid, uuid)) { c->hold = true; c->connected = false; break; } } } } void SignalingApp::ReportCallRetrieve(const char * uuid) { debug->printf("SignalingApp::ReportCallRetrieve uuid: %s", uuid); if (cxSupported && callKit) callKit->CxRequestSetHeldCall(uuid, false); } void SignalingApp::PhoneAnswerCallAction(const char * uuid) { debug->printf("SignalingApp::PhoneAnswerCallAction %s", uuid); // Call answered with the notification. Set other calls on hold. if (!calls.empty()) { SignalingAppCall *c; for (c = calls.front(); c; c = c->goNext()) { if (c->connected) { signaling->PhoneHoldCallAction(c->uuid); c->connected = false; c->hold = true; } else if (!uiRunning) { c->connected = true; c->hold = false; } } } if (strstr(uuid, "sip:")) { const char* sip = &uuid[4]; signaling->PhoneDialNumber(nullptr, sip, START_CALL_VIDEO_MODE_DEFAULT, true); } else if (strstr(uuid, "num:")) { const char* num = &uuid[4]; signaling->PhoneDialNumber(num, nullptr, START_CALL_VIDEO_MODE_DEFAULT, false); } else if (strstr(uuid, "hold:")) { } else signaling->PhoneAnswerCallAction(uuid); } void SignalingApp::RequestStartCall(const char * uuid, const char * number, const char * name, bool video, bool sendingComplete) { debug->printf("SignalingApp::RequestStartCall uuid: %s number: %s name: %s", uuid, number, name ); if(cxSupported && callKit) callKit->CxRequestStartCall( uuid, number, name, video ); else signaling->PhoneStartCallAction(uuid, number, name, video, sendingComplete); } void SignalingApp::RequestStartRinging(const char* uuid) { debug->printf("SignalingApp::RequestStartRinging uuid: %s", uuid); if (cxSupported == false) signaling->StartRinging(uuid); } bool SignalingApp::IsSoftPhoneStandardApp() { return (ILocalStorage::GetItem("SignalingServiceUrl") && (strlen(ILocalStorage::GetItem("SignalingServiceUrl")) > 1)); } void SignalingApp::ReportOutgoingCallAlerting(const char *uuid, const char * name, const char * sip, const char * num) { debug->printf("SignalingApp::ReportOutgoingCallAlerting %s", uuid ); if(cxSupported) { if(callKit) callKit->CxReportOutgoingCallConnecting(uuid); } else if (!uiRunning) { NotificationTerminateCall(uuid, name, sip, num, nullptr, nullptr, this->app, this->domain, this->appdomain); if(uuidTerminateNotify == NULL) uuidTerminateNotify = _strdup(uuid); else if(uuid && strcmp(uuid, uuidTerminateNotify)) uuidTerminateNotifyConf = _strdup(uuid); } SignalingAppCall *c = NULL; if (!calls.empty()) { for (c = calls.front(); c; c = c->goNext()) { if (uuid && c->uuid && !strcmp(uuid, c->uuid)) break; } } if (!c) { if (calls.empty()) { if (signalingAppUser) signalingAppUser->SignalingCallsActive(true); } calls.push_back(new class SignalingAppCall(uuid, num, sip, name, nullptr, nullptr, false)); } } void SignalingApp::UserSession(bool up) { debug->printf("SignalingApp::UserSession %d(%d) (%s) (%s) hw:%s defaultApi:%s calls:%d", up, uiRunning, uuidTerminateNotify ? uuidTerminateNotify : "-", uuidTerminateNotifyConf ? uuidTerminateNotifyConf : "-", hw ? hw : "-", this->defaultApiProvider ? this->defaultApiProvider : "-", !calls.empty()); if (up) uiRunning++; else if (uiRunning) uiRunning--; if (uiRunning) { if(uuidTerminateNotify) { char ref[256]; _sprintf(ref, "drop:%s", uuidTerminateNotify); RemoveNotification(ref); _sprintf(ref, "hold:%s", uuidTerminateNotify); RemoveNotification(ref); free(uuidTerminateNotify); uuidTerminateNotify = NULL; } if(uuidTerminateNotifyConf) { char ref[256]; _sprintf(ref, "drop:%s", uuidTerminateNotifyConf); RemoveNotification(ref); _sprintf(ref, "hold:%s", uuidTerminateNotifyConf); RemoveNotification(ref); free(uuidTerminateNotifyConf); uuidTerminateNotifyConf = NULL; } } else { if (!calls.empty()) { SignalingAppCall *c; for (c = calls.front(); c; c = c->goNext()) { if (c->uuid) { if (c->hold) NotificationHoldTerminateCall(c->uuid, c->name, c->sip, c->number, c->diverting, c->called, this->app, this->domain, this->appdomain); else NotificationTerminateCall(c->uuid, c->name, c->sip, c->number, c->diverting, c->called, this->app, this->domain, this->appdomain); if(uuidTerminateNotify == NULL) uuidTerminateNotify = _strdup(c->uuid); else if (strcmp(c->uuid, uuidTerminateNotify)) uuidTerminateNotifyConf = _strdup(c->uuid); } } } if ((defaultApiProviderRemoved || !hw) && signaling && !signaling->NumActiveCalls()) { signaling->CloseClient(); if(this->defaultApiProvider) free(this->defaultApiProvider); this->defaultApiProvider = NULL; defaultApiProviderRemoved = false; } if (windowTitle) { free(windowTitle); windowTitle = _strdup(innovaphoneMyapps); } } } void SignalingApp::SetDnd(bool dnd) { this->dnd = dnd; } SignalingApiPhoneConsumer::SignalingApiPhoneConsumer(class SignalingApp * client) : ApiConsumer(client, "com.innovaphone.phone") { this->signaling = client; } SignalingApiPhoneConsumer::~SignalingApiPhoneConsumer() { debug->printf("SignalingApiPhoneConsumer::~SignalingApiPhoneConsumer"); } void SignalingApiPhoneConsumer::ApiConsumerUpdate(class json_io & model, word base) { //debug->printf("SignalingApiPhoneConsumer::ApiConsumerUpdate"); if (base != JSON_ID_NONE) { word last = 0, t, end = 0xFFFF; while ((t = model.get_object(base, last)) != end) { const char * hw = model.get_name(t); const char * title = model.get_string(t, "title"); const char * url = model.get_string(t, "url"); word info = model.get_object(t, "info"); if (info != JSON_ID_NONE) { const char * app = model.get_string(info, "type"); if (app) { signaling->PhoneAppAvailable(hw, app, title, url); } } } } } void SignalingApiPhoneConsumer::ApiConsumerDefaultProvider(const char * defaultApiProvider) { signaling->DefaultApiProvider(defaultApiProvider); } void SignalingApiPhoneConsumer::ConfigureSoftphone(const char * appname) { debug->printf("SignalingApiPhoneConsumer::ConfigureSoftphone %s", appname); char buffer[1024]; json_io json(0); word base = json.add_object(JSON_ID_ROOT, 0); json.add_string(base, "mt", "ConfigureSoftphone"); ApiConsumerSend(appname, 0, json, base, buffer); } SignalingApiLookupConsumer::SignalingApiLookupConsumer(class SignalingApp* client) : ApiConsumer(client, "com.innovaphone.phonelookup") { this->signaling = client; } SignalingApiLookupConsumer::~SignalingApiLookupConsumer() { debug->printf("SignalingApiLookUpConsumer::~SignalingApiLookupConsumer"); } void SignalingApiLookupConsumer::Lookup(const char * src, const char * number, const char * prefixIntl, const char * prefixNtl, const char * prefixExt, const char * country, const char * area, const char * subscriber) { debug->printf("SignalingApiLookupConsumer::LookUp %s src %s", number ? number : "", src ? src : ""); if (!number) return; char buffer[1024]; json_io json(0); word base = json.add_object(JSON_ID_ROOT, 0); json.add_string(base, "mt", "Lookup"); json.add_string(base, "lookup", number); json.add_string(base, "prefixIntl", prefixIntl); json.add_string(base, "prefixNtl", prefixNtl); json.add_string(base, "prefixExt", prefixExt); json.add_string(base, "country", country); json.add_string(base, "area", area); json.add_string(base, "subscriber", subscriber); ApiConsumerSend("*", src, json, base, buffer); } void SignalingApiLookupConsumer::ApiConsumerRecv(const char* provider, const char* src, class json_io& msg, word base) { const char* mt = msg.get_string(base, "mt"); debug->printf("SignalingApiLookupConsumer::ApiConsumerRecv mt:%s", mt ? mt : ""); if(mt && !strcmp(mt, "LookupInfo")) signaling->LookupInfo(src, msg, base); } SignalingApiAvatarConsumer::SignalingApiAvatarConsumer(class SignalingApp * client) : ApiConsumer(client, "com.innovaphone.avatar") { this->user = ILocalStorage::GetItem("currentUser"); this->url = nullptr; this->base = nullptr; this->domain = nullptr; this->filename = nullptr; this->token = nullptr; IRandom::GetChars(this->salt, 16, random_chars_url); this->salt[16] = 0; } SignalingApiAvatarConsumer::~SignalingApiAvatarConsumer() { debug->printf("SignalingApiAvatarConsumer::~SignalingApiConsumer"); if (this->user) free(this->user); if (this->url) free(this->url); if (this->base) free(this->base); if (this->domain) free(this->domain); if (this->filename) free(this->filename); if (this->token) free(this->token); } void SignalingApiAvatarConsumer::ApiConsumerUpdate(class json_io & model, word base) { word usersappi = model.get_object(base, "usersapis"); if (usersappi != JSON_ID_NONE) { this->url = _strdup(model.get_string(usersappi, "url")); word info = model.get_object(usersappi, "info"); if (info != JSON_ID_NONE) { domain = _strdup(model.get_string(info, "domain")); filename = _strdup(model.get_string(info, "filename")); token = _strdup(model.get_string(info, "token")); } } if (url && filename) { this->base = (char *)malloc(strlen(this->url)+strlen(filename)+32); memcpy(this->base, url, strlen(url)); this->base[strlen(url)] = 0; const char * slash = strrchr(this->base, 0x2f); // last occurrence of "/" if (slash) { int baseLen = slash - this->base + 1; this->base[baseLen] = 0; strcat(this->base, filename); } } //debug->printf("SignalingApiAvatarConsumer::ApiConsumerUpdate url=%s domain=%s filename=%s token=%s", this->base ? this->base : "", domain ? domain : "", filename ? filename : "", token ? token : ""); } char * SignalingApiAvatarConsumer::GetAvatarUrl(const char * sip, const char * dn, const char * domain, const char * appdomain) { if(!this->user) this->user = ILocalStorage::GetItem("currentUser"); //debug->printf("SignalingApiAvatarConsumer::GetAvatarUrl base=%s sip=%s dn=%s domain=%s appdomain=%s token=%s user=%s", this->base ? this->base : "", sip ? sip : "", dn ? dn : "", domain ? domain : "", appdomain ? appdomain : "", token ? token : "", this->user ? this->user : ""); char * avatarUrl = NULL; if (this->base && token && sip && dn && this->user) { class hash hash; byte digestBin[HASH_SIZE_SHA256]; hash.init(HASH_SHA256); hash.update(token, strlen(token)); hash.update(salt, strlen(salt)); hash.update(sip, strlen(sip)); hash.final(digestBin); char* digestCheck = (char*)alloca(HASH_SIZE_SHA256 * 2 + 1); for (int i = 0; i < HASH_SIZE_SHA256; i++) sprintf(&digestCheck[i * 2], "%02x", digestBin[i]); avatarUrl = (char *)malloc(strlen(base)+(strlen(sip)*3)+(strlen((char *)digestBin)*3)+(strlen(dn)*3)+256); strcpy(avatarUrl, base); strcat(avatarUrl, "?sip="); dword lenEncoded = strlen(sip) * 3 + 1; char * sipEncoded = (char *)malloc(lenEncoded); str::to_url(sip, sipEncoded, lenEncoded); strcat(avatarUrl, sipEncoded); free(sipEncoded); strcat(avatarUrl, "&salt="); strcat(avatarUrl, salt); strcat(avatarUrl, "&auth="); lenEncoded = strlen(digestCheck)+1; char * digestEncoded = (char *)malloc(lenEncoded); str::to_url((char *)digestCheck, digestEncoded, lenEncoded); strcat(avatarUrl, digestEncoded); free(digestEncoded); strcat(avatarUrl, "&size=240"); if (dn) { lenEncoded = strlen(dn) * 3 + 1; char * dnEncoded = (char *)malloc(lenEncoded); str::to_url(dn, dnEncoded, lenEncoded); strcat(avatarUrl, "&dn="); strcat(avatarUrl, dnEncoded); free(dnEncoded); } strcat(avatarUrl, "&user="); strcat(avatarUrl, this->user); strcat(avatarUrl, "&dom=@"); strcat(avatarUrl, domain); if(!strcmp(sip, this->user)) strcat(avatarUrl, "&no-cache"); debug->printf("SignalingApiAvatarConsumer::GetAvatarUrl %s", avatarUrl); } return avatarUrl; } void SignalingApp::ReportIncomingCall(const char* uuid, const char * name, const char * sip, const char * num, const char * diverting, const char * called, const char * app, const char * domain, const char * appdomain) { debug->printf("SignalingApp::ReportIncomingCall uuid=%s", uuid); char title[1024]; int tlen = _sprintf(title, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_CALL_FROM, langId)); _sprintf(&title[tlen], "%s", name ? name : num ? num : sip ? sip : "anonymous"); int bodyLen = 32; bodyLen += called ? strlen(called) : 0; bodyLen += diverting ? strlen(diverting) : 0; bodyLen += strlen(MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); bodyLen += strlen(MYAPPS_LANG_STRING(TXT_SIGNALING_AND, langId)); bodyLen += app ? strlen(app) : 0; char * body = (char *)malloc(bodyLen); if (called && diverting) { int tlen = _sprintf(body, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); tlen += _sprintf(&body[tlen], "%s ", called); tlen += _sprintf(&body[tlen], "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_AND, langId)); tlen += _sprintf(&body[tlen], "%s", diverting); } else if (diverting) { int tlen = _sprintf(body, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); tlen += _sprintf(&body[tlen], "%s", diverting); } else { _sprintf(body, "%s ", app); } char * avatarUrl = GetAvatarUrl(sip, name, domain, appdomain); if (avatarUrl) { debug->printf("avatarUrl %s", avatarUrl); } if (notifications) notifications->AddLocalNotification(uuid, LOCAL_NOTIFICATION_TYPE_PHONE_CALL, title, body, nullptr, avatarUrl ? avatarUrl : nullptr, nullptr, MYAPPS_LANG_STRING(TXT_SIGNALING_SHOW, langId), MYAPPS_LANG_STRING(TXT_SIGNALING_CONNECT, langId), MYAPPS_LANG_STRING(TXT_SIGNALING_REJECT, langId), nullptr, this); free(body); if (avatarUrl) free(avatarUrl); } void SignalingApp::RemoveNotification(const char * ref) { debug->printf("SignalingApp::RemoveNotification ref=%s", ref); if (notifications) notifications->RemoveLocalNotification(ref); } void SignalingApp::ReportMissedCall(const char* uuid, const char * name, const char * sip, const char * num, const char * diverting, const char * called, const char * app, const char * domain, const char * appdomain) { debug->printf("SignalingApp::ReportMissedCall uuid=%s sip=%s num=%s", uuid, sip ? sip : "", num ? num : ""); char ref[256]; if (sip) _sprintf(ref, "sip:%s", sip); else if (num) _sprintf(ref, "num:%s", num); else ref[0] = 0; char title[256]; int tlen = _sprintf(title, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_MISSED_CALL_FROM, langId)); _sprintf(&title[tlen], "%s", name ? name : num ? num : sip ? sip : "anonymous"); int bodyLen = 32; bodyLen += called ? strlen(called) : 0; bodyLen += diverting ? strlen(diverting) : 0; bodyLen += strlen(MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); bodyLen += strlen(MYAPPS_LANG_STRING(TXT_SIGNALING_AND, langId)); bodyLen += app ? strlen(app) : 0; char * body = (char *)malloc(bodyLen); if (called && diverting) { int tlen = _sprintf(body, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); tlen += _sprintf(&body[tlen], "%s ", called); tlen += _sprintf(&body[tlen], "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_AND, langId)); tlen += _sprintf(&body[tlen], "%s", diverting); } else if (diverting) { int tlen = _sprintf(body, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); tlen += _sprintf(&body[tlen], "%s", diverting); } else { _sprintf(body, "%s ", app); } if (notifications) { char* avatarUrl = GetAvatarUrl(sip, name, domain, appdomain); notifications->AddLocalNotification(ref, LOCAL_NOTIFICATION_TYPE_END_PHONE_CALL, title, body, nullptr, avatarUrl ? avatarUrl : nullptr, nullptr, MYAPPS_LANG_STRING(TXT_SIGNALING_CALL_BACK, langId), nullptr, nullptr, nullptr, this); if (avatarUrl) free(avatarUrl); } free(body); } void SignalingApp::NotificationTerminateCall(const char* uuid, const char * name, const char * sip, const char * num, const char * diverting, const char * called, const char * app, const char * domain, const char * appdomain) { debug->printf("SignalingApiConsumer::NotificationTerminateCall uuid=%s", uuid); char ref[256]; _sprintf(ref, "drop:%s", uuid); char title[256]; int tlen = _sprintf(title, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_CALL_FROM, langId)); _sprintf(&title[tlen], "%s", name ? name : num ? num : sip ? sip : "anonymous"); int bodyLen = 32; bodyLen += called ? strlen(called) : 0; bodyLen += diverting ? strlen(diverting) : 0; bodyLen += strlen(MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); bodyLen += strlen(MYAPPS_LANG_STRING(TXT_SIGNALING_AND, langId)); bodyLen += app ? strlen(app) : 0; char * body = (char *)malloc(bodyLen); if (called && diverting) { int tlen = _sprintf(body, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); tlen += _sprintf(&body[tlen], "%s ", called); tlen += _sprintf(&body[tlen], "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_AND, langId)); tlen += _sprintf(&body[tlen], "%s", diverting); } else if (diverting) { int tlen = _sprintf(body, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); tlen += _sprintf(&body[tlen], "%s", diverting); } else { _sprintf(body, "%s ", app); } #if 0 char * avatarUrl = signalingApp->GetAvatarUrl(sip, name, domain, appdomain); if (avatarUrl) { json.add_string(base, "largeIcon", avatarUrl); free(avatarUrl); } #endif if (notifications) notifications->AddLocalNotification(ref, LOCAL_NOTIFICATION_TYPE_END_PHONE_CALL, title, body, nullptr, nullptr, nullptr, MYAPPS_LANG_STRING(TXT_SIGNALING_TERMINATE_CALL, langId), nullptr, nullptr, nullptr, this); free(body); } void SignalingApp::NotificationHoldTerminateCall(const char* uuid, const char * name, const char * sip, const char * num, const char * diverting, const char * called, const char * app, const char * domain, const char * appdomain) { debug->printf("SignalingApiConsumer::NotificationHoldTerminateCall uuid=%s", uuid); char ref[256]; _sprintf(ref, "hold:%s", uuid); char title[256]; int tlen = _sprintf(title, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_CALL_FROM, langId)); _sprintf(&title[tlen], "%s", name ? name : num ? num : sip ? sip : "anonymous"); int bodyLen = 32; bodyLen += called ? strlen(called) : 0; bodyLen += diverting ? strlen(diverting) : 0; bodyLen += strlen(MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); bodyLen += strlen(MYAPPS_LANG_STRING(TXT_SIGNALING_AND, langId)); bodyLen += app ? strlen(app) : 0; char * body = (char *)malloc(bodyLen); if (called && diverting) { int tlen = _sprintf(body, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); tlen += _sprintf(&body[tlen], "%s ", called); tlen += _sprintf(&body[tlen], "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_AND, langId)); tlen += _sprintf(&body[tlen], "%s", diverting); } else if (diverting) { int tlen = _sprintf(body, "%s ", MYAPPS_LANG_STRING(TXT_SIGNALING_VIA, langId)); tlen += _sprintf(&body[tlen], "%s", diverting); } else { _sprintf(body, "%s ", app); } #if 0 char * avatarUrl = signalingApp->GetAvatarUrl(sip, name, domain, appdomain); if (avatarUrl) { json.add_string(base, "largeIcon", avatarUrl); free(avatarUrl); } #endif if (notifications) notifications->AddLocalNotification(ref, LOCAL_NOTIFICATION_TYPE_HOLD_PHONE_CALL, title, body, nullptr, nullptr, nullptr, nullptr, MYAPPS_LANG_STRING(TXT_SIGNALING_CONNECT, langId), MYAPPS_LANG_STRING(TXT_SIGNALING_TERMINATE_CALL, langId), nullptr, this); free(body); } void SignalingApp::PhoneEndCallAction(const char* uuid, int callError) { debug->printf("SignalingApp::PhoneEndCallAction uuid=%s error=%d", uuid, callError); if (strstr(uuid, "hold:")) { const char * tUuid = &uuid[5]; signaling->PhoneEndCallAction(tUuid, 0); ReportCallEnded(uuid, 0); } else signaling->PhoneEndCallAction(uuid, callError); } void SignalingApp::ClientConnected() { debug->printf("SignalingApp(%p)::ClientConnected", this); this->remoteServices = true; } void SignalingApp::ClientDisconnected() { debug->printf("SignalingApp(%p)::ClientDisconnected", this); remoteServices = false; if (!calls.empty()) { for (SignalingAppCall* c = calls.front(); c; c = c->goNext()) { signaling->PhoneEndCallAction(c->uuid, 0); } } } void SignalingApp::RegistrationCheck() { if (signaling) signaling->RegistrationCheck(); }