diff options
Diffstat (limited to 'plugins/Jingle/src/account.cpp')
-rw-r--r-- | plugins/Jingle/src/account.cpp | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/plugins/Jingle/src/account.cpp b/plugins/Jingle/src/account.cpp index 551f59db00..a6e68b76ec 100644 --- a/plugins/Jingle/src/account.cpp +++ b/plugins/Jingle/src/account.cpp @@ -42,13 +42,250 @@ static int OnAccountCreated(WPARAM reason, LPARAM param) return 0; } +static int OnSettingChanged(WPARAM hContact, LPARAM lParam) +{ + if (!hContact) { + auto *pcws = (DBCONTACTWRITESETTING *)lParam; + if (!mir_strcmp(pcws->szSetting, "EnableVOIP")) { + for (auto &it : g_arJabber) { + if (!mir_strcmp(it->m_szModuleName, pcws->szModule)) { + it->InitVoip(pcws->value.bVal != 0); + break; + } + } + } + } + + return 0; +} + void CJabberAccount::InitHooks() { HookEvent(ME_SYSTEM_MODULESLOADED, &OnModulesLoaded); HookEvent(ME_PROTO_ACCLISTCHANGED, &OnAccountCreated); + HookEvent(ME_DB_CONTACT_SETTINGCHANGED, &OnSettingChanged); } /////////////////////////////////////////////////////////////////////////////// +// Permanent IQ handler + +static BOOL OnProcessJingle(struct IJabberInterface *api, const TiXmlElement *node, void *pUserData) +{ + auto *pThis = (CJabberAccount *)pUserData; + + auto *child = XmlGetChildByTag(node, "jingle", "xmlns", JABBER_FEAT_JINGLE); + if (!child) + return false; + + const char *type = XmlGetAttr(node, "type"); + if (type == nullptr) + return false; + + const char *szAction = XmlGetAttr(child, "action"); + const char *szSid = XmlGetAttr(child, "sid"); + + if (!mir_strcmp(type, "get") || !mir_strcmp(type, "set")) { + const char *idStr = XmlGetAttr(node, "id"); + const char *from = XmlGetAttr(node, "from"); + const char *szInitiator = XmlGetAttr(child, "initiator"); + auto *content = XmlGetChildByTag(child, "content", "creator", "initiator"); + + if (szAction && szSid) { + if (!mir_strcmp(szAction, "session-initiate")) { + // if this is a Jingle 'session-initiate' and noone processed it yet, reply with "unsupported-applications" + api->SendXml(XmlNodeIq("result", idStr, from)); + + const TiXmlElement *descr = XmlGetChildByTag(content, "description", "xmlns", JABBER_FEAT_JINGLE_RTP); + const char *reason = NULL; + if (pThis->m_bEnableVOIP && descr) { + if (pThis->m_voipSession.IsEmpty()) { + pThis->m_voipSession = szSid; + pThis->m_voipPeerJid = from; + pThis->m_isOutgoing = false; + pThis->m_offerNode = child->DeepClone(&pThis->m_offerDoc)->ToElement(); + + //Make call GUI + VOICE_CALL vc = {}; + vc.cbSize = sizeof(VOICE_CALL); + vc.moduleName = pThis->m_szModuleName; + vc.id = szSid; // Protocol specific ID for this call + vc.hContact = api->ContactFromJID(from); // Contact associated with the call (can be NULL) + vc.state = VOICE_STATE_RINGING; + vc.szNumber.a = pThis->m_voipPeerJid; + NotifyEventHooks(pThis->m_hVoiceEvent, WPARAM(&vc), 0); + + // ringing message + XmlNodeIq iq("set", api->GetSerialNext(), from); + TiXmlElement *rjNode = iq << XCHILDNS("jingle", JABBER_FEAT_JINGLE); + rjNode << XATTR("action", "session-info") << XATTR("sid", szSid); + if (szInitiator) + rjNode << XATTR("initiator", szInitiator); + rjNode << XCHILDNS("ringing", "urn:xmpp:jingle:apps:rtp:info:1"); + + api->SendXml(iq); + return true; + } + + // Save this event to history + PROTORECVEVENT recv = {}; + recv.timestamp = (uint32_t)time(0); + recv.szMessage = "** A call while we were busy **"; + ProtoChainRecvMsg(api->ContactFromJID(from), &recv); + reason = "busy"; + } + + XmlNodeIq iq("set", api->GetSerialNext(), from); + TiXmlElement *jingleNode = iq << XCHILDNS("jingle", JABBER_FEAT_JINGLE); + jingleNode << XATTR("action", "session-terminate") << XATTR("sid", szSid); + if (szInitiator) + jingleNode << XATTR("initiator", szInitiator); + jingleNode << XCHILD("reason") << XCHILD(reason ? reason : "unsupported-applications"); + + api->SendXml(iq); + return true; + } + else if (!mir_strcmp(szAction, "session-accept")) { + if (pThis->m_bEnableVOIP && pThis->m_voipSession == szSid) { + api->SendXml(XmlNodeIq("result", idStr, from)); + if (pThis->OnRTPDescription(child)) { + //Make call GUI + VOICE_CALL vc = {}; + vc.cbSize = sizeof(VOICE_CALL); + vc.moduleName = pThis->m_szModuleName; + vc.id = szSid; + vc.hContact = api->ContactFromJID(from); + vc.state = VOICE_STATE_TALKING; + NotifyEventHooks(pThis->m_hVoiceEvent, WPARAM(&vc), 0); + } + return true; + } + } + else if (!mir_strcmp(szAction, "session-terminate")) { + if (pThis->m_bEnableVOIP && pThis->m_voipSession == szSid) { + // EndCall() + api->SendXml(XmlNodeIq("result", idStr, from)); + + VOICE_CALL vc = {}; + vc.cbSize = sizeof(VOICE_CALL); + vc.moduleName = pThis->m_szModuleName; + vc.id = szSid; + vc.hContact = api->ContactFromJID(from); + vc.state = VOICE_STATE_ENDED; + NotifyEventHooks(pThis->m_hVoiceEvent, WPARAM(&vc), 0); + + pThis->VOIPTerminateSession(nullptr); + return true; + } + } + else if (!mir_strcmp(szAction, "transport-info")) { + auto *transport = XmlGetChildByTag(content, "transport", "xmlns", JABBER_FEAT_JINGLE_ICEUDP); + if (pThis->m_bEnableVOIP && pThis->m_voipSession == szSid && transport) { + api->SendXml(XmlNodeIq("result", idStr, from)); + if (const TiXmlElement *candidate = XmlFirstChild(transport, "candidate")) { + pThis->OnICECandidate(candidate); + return true; + } + } + } + } + + // if it's something else than 'session-initiate' and noone processed it yet, reply with "unknown-session" + XmlNodeIq iq("error", idStr, from); + TiXmlElement *errNode = iq << XCHILD("error"); + errNode << XATTR("type", "cancel"); + errNode << XCHILDNS("item-not-found", "urn:ietf:params:xml:ns:xmpp-stanzas"); + errNode << XCHILDNS("unknown-session", "urn:xmpp:jingle:errors:1"); + api->SendXml(iq); + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Services + +static INT_PTR __cdecl JabberVOIP_call(void *pThis, WPARAM hContact, LPARAM) +{ + auto *pAcc = (CJabberAccount *)pThis; + if (pAcc->VOIPCallIinitiate(hContact)) { + VOICE_CALL vc = {}; + vc.cbSize = sizeof(VOICE_CALL); + vc.moduleName = pAcc->m_szModuleName; + vc.id = pAcc->m_voipSession; // Protocol especific ID for this call + vc.hContact = hContact; // Contact associated with the call (can be NULL) + vc.state = VOICE_STATE_READY; + vc.szNumber.a = pAcc->m_voipPeerJid; + NotifyEventHooks(pAcc->m_hVoiceEvent, WPARAM(&vc), 0); + } + + return 0; +} + +static INT_PTR __cdecl JabberVOIP_answercall(void *pThis, WPARAM id, LPARAM) +{ + auto *pAcc = (CJabberAccount *)pThis; + if (strcmp((const char *)id, pAcc->m_voipSession)) + return 0; + + VOICE_CALL vc = {}; + vc.cbSize = sizeof(VOICE_CALL); + vc.moduleName = pAcc->m_szModuleName; + vc.hContact = pAcc->m_api->ContactFromJID(pAcc->m_voipPeerJid);// Contact associated with the call (can be NULL) + vc.szNumber.a = pAcc->m_voipPeerJid; + vc.id = pAcc->m_voipSession; + vc.state = VOICE_STATE_ENDED; + + if (pAcc->VOIPCreatePipeline()) { + if (pAcc->m_isOutgoing) + vc.state = VOICE_STATE_CALLING; + else if (pAcc->OnRTPDescription(pAcc->m_offerNode)) + vc.state = VOICE_STATE_TALKING; + else + pAcc->VOIPTerminateSession(); + } + + NotifyEventHooks(pAcc->m_hVoiceEvent, WPARAM(&vc), 0); + return 0; +} + +static INT_PTR __cdecl JabberVOIP_dropcall(void *pThis, WPARAM id, LPARAM) +{ + auto *pAcc = (CJabberAccount *)pThis; + + VOICE_CALL vc = {}; + vc.cbSize = sizeof(VOICE_CALL); + vc.moduleName = pAcc->m_szModuleName; + vc.id = (char *)id; + vc.state = VOICE_STATE_ENDED; + NotifyEventHooks(pAcc->m_hVoiceEvent, WPARAM(&vc), 0); + + pAcc->VOIPTerminateSession(); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CJabberAccount members + +CJabberAccount::CJabberAccount(IJabberInterface *_1) : + m_api(_1), + m_szModuleName(m_api->GetModuleName()), + m_bEnableVOIP(m_szModuleName, "EnableVOIP", false) +{ + CMStringA tmp(m_szModuleName); + m_hVoiceEvent = CreateHookableEvent(tmp + PE_VOICE_CALL_STATE); + CreateServiceFunctionObj(tmp + PS_VOICE_CALL, &JabberVOIP_call, this); + CreateServiceFunctionObj(tmp + PS_VOICE_ANSWERCALL, &JabberVOIP_answercall, this); + CreateServiceFunctionObj(tmp + PS_VOICE_DROPCALL, &JabberVOIP_dropcall, this); +} + +CJabberAccount::~CJabberAccount() +{ + DestroyHookableEvent(m_hVoiceEvent); + + if (m_bEnableVOIP) + InitVoip(false); +} void CJabberAccount::Init() { @@ -59,4 +296,9 @@ void CJabberAccount::Init() m_api->RegisterFeature(JABBER_FEAT_JINGLE_RTPAUDIO, LPGEN("Jingle RTP Audio")); m_api->AddFeatures(JABBER_FEAT_JINGLE "\0" JABBER_FEAT_JINGLE_ICEUDP "\0" JABBER_FEAT_JINGLE_RTP "\0" JABBER_FEAT_JINGLE_DTLS "\0" JABBER_FEAT_JINGLE_RTPAUDIO "\0\0"); + + m_api->AddIqHandler(&OnProcessJingle, JABBER_IQ_TYPE_ANY, JABBER_FEAT_JINGLE, 0, this); + + if (m_bEnableVOIP) + InitVoip(true); } |