summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/jabber/jingle/jinglevoicesession.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/jabber/jingle/jinglevoicesession.cpp')
-rw-r--r--kopete/protocols/jabber/jingle/jinglevoicesession.cpp333
1 files changed, 333 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/jingle/jinglevoicesession.cpp b/kopete/protocols/jabber/jingle/jinglevoicesession.cpp
new file mode 100644
index 00000000..09019ce4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglevoicesession.cpp
@@ -0,0 +1,333 @@
+/*
+ jinglevoicesession.cpp - Define a Jingle voice session.
+
+ Copyright (c) 2006 by MichaĆ«l Larouche <[email protected]>
+
+ Kopete (c) 2001-2006 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+// libjingle before everything else to not clash with Qt
+#define POSIX
+#include "talk/xmpp/constants.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/base/network.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/p2p/client/sessionclient.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/thread.h"
+#include "talk/base/socketaddress.h"
+#include "talk/session/phone/call.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/session/sessionsendtask.h"
+
+#include "jinglevoicesession.h"
+#include "jinglesessionmanager.h"
+
+// Qt includes
+#include <qdom.h>
+
+// KDE includes
+#include <kdebug.h>
+
+// Kopete Jabber includes
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+
+#include <xmpp.h>
+#include <xmpp_xmlcommon.h>
+
+#define JINGLE_NS "http://www.google.com/session"
+#define JINGLE_VOICE_SESSION_NS "http://www.google.com/session/phone"
+
+static bool hasPeer(const JingleVoiceSession::JidList &jidList, const XMPP::Jid &peer)
+{
+ JingleVoiceSession::JidList::ConstIterator it, itEnd = jidList.constEnd();
+ for(it = jidList.constBegin(); it != itEnd; ++it)
+ {
+ if( (*it).compare(peer, true) )
+ return true;
+ }
+
+ return false;
+}
+//BEGIN SlotsProxy
+/**
+ * This class is used to receive signals from libjingle,
+ * which is are not compatible with Qt signals.
+ * So it's a proxy between JingeVoiceSession(qt)<->linjingle class.
+ */
+class JingleVoiceSession::SlotsProxy : public sigslot::has_slots<>
+{
+public:
+ SlotsProxy(JingleVoiceSession *parent)
+ : voiceSession(parent)
+ {}
+
+ void OnCallCreated(cricket::Call* call)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "SlotsProxy: CallCreated." << endl;
+
+ call->SignalSessionState.connect(this, &JingleVoiceSession::SlotsProxy::PhoneSessionStateChanged);
+ voiceSession->setCall(call);
+ }
+
+ void PhoneSessionStateChanged(cricket::Call *call, cricket::Session *session, cricket::Session::State state)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "State changed: " << state << endl;
+
+ XMPP::Jid jid(session->remote_address().c_str());
+
+ // Do nothing if the session do not contain a peers.
+ //if( !voiceSession->peers().contains(jid) )
+ if( !hasPeer(voiceSession->peers(), jid) )
+ return;
+
+ if (state == cricket::Session::STATE_INIT)
+ {}
+ else if (state == cricket::Session::STATE_SENTINITIATE)
+ {}
+ else if (state == cricket::Session::STATE_RECEIVEDINITIATE)
+ {
+ voiceSession->setCall(call);
+ }
+ else if (state == cricket::Session::STATE_SENTACCEPT)
+ {}
+ else if (state == cricket::Session::STATE_RECEIVEDACCEPT)
+ {
+ emit voiceSession->accepted();
+ }
+ else if (state == cricket::Session::STATE_SENTMODIFY)
+ {}
+ else if (state == cricket::Session::STATE_RECEIVEDMODIFY)
+ {
+ //qWarning(QString("jinglevoicecaller.cpp: RECEIVEDMODIFY not implemented yet (was from %1)").arg(jid.full()));
+ }
+ else if (state == cricket::Session::STATE_SENTREJECT)
+ {}
+ else if (state == cricket::Session::STATE_RECEIVEDREJECT)
+ {
+ emit voiceSession->declined();
+ }
+ else if (state == cricket::Session::STATE_SENTREDIRECT)
+ {}
+ else if (state == cricket::Session::STATE_SENTTERMINATE)
+ {
+ emit voiceSession->terminated();
+ }
+ else if (state == cricket::Session::STATE_RECEIVEDTERMINATE)
+ {
+ emit voiceSession->terminated();
+ }
+ else if (state == cricket::Session::STATE_INPROGRESS)
+ {
+ emit voiceSession->sessionStarted();
+ }
+ }
+
+ void OnSendingStanza(cricket::SessionClient*, const buzz::XmlElement *buzzStanza)
+ {
+ QString irisStanza(buzzStanza->Str().c_str());
+ irisStanza.replace("cli:iq","iq");
+ irisStanza.replace(":cli=","=");
+
+ voiceSession->sendStanza(irisStanza);
+ }
+private:
+ JingleVoiceSession *voiceSession;
+};
+//END SlotsProxy
+
+//BEGIN JingleIQResponder
+class JingleVoiceSession::JingleIQResponder : public XMPP::Task
+{
+public:
+ JingleIQResponder(XMPP::Task *);
+ ~JingleIQResponder();
+
+ bool take(const QDomElement &);
+};
+
+/**
+ * \class JingleIQResponder
+ * \brief A task that responds to jingle candidate queries with an empty reply.
+ */
+
+JingleVoiceSession::JingleIQResponder::JingleIQResponder(Task *parent) :Task(parent)
+{
+}
+
+JingleVoiceSession::JingleIQResponder::~JingleIQResponder()
+{
+}
+
+bool JingleVoiceSession::JingleIQResponder::take(const QDomElement &e)
+{
+ if(e.tagName() != "iq")
+ return false;
+
+ QDomElement first = e.firstChild().toElement();
+ if (!first.isNull() && first.attribute("xmlns") == JINGLE_NS) {
+ QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+ send(iq);
+ return true;
+ }
+
+ return false;
+}
+//END JingleIQResponder
+
+class JingleVoiceSession::Private
+{
+public:
+ Private()
+ : phoneSessionClient(0L), currentCall(0L)
+ {}
+
+ ~Private()
+ {
+ if(currentCall)
+ currentCall->Terminate();
+
+ delete currentCall;
+ }
+
+ cricket::PhoneSessionClient *phoneSessionClient;
+ cricket::Call* currentCall;
+};
+
+JingleVoiceSession::JingleVoiceSession(JabberAccount *account, const JidList &peers)
+ : JingleSession(account, peers), d(new Private)
+{
+ slotsProxy = new SlotsProxy(this);
+
+ buzz::Jid buzzJid( account->client()->jid().full().ascii() );
+
+ // Create the phone(voice) session.
+ d->phoneSessionClient = new cricket::PhoneSessionClient( buzzJid, account->sessionManager()->cricketSessionManager() );
+
+ d->phoneSessionClient->SignalSendStanza.connect(slotsProxy, &JingleVoiceSession::SlotsProxy::OnSendingStanza);
+ d->phoneSessionClient->SignalCallCreate.connect(slotsProxy, &JingleVoiceSession::SlotsProxy::OnCallCreated);
+
+ // Listen to incoming packets
+ connect(account->client()->client(), SIGNAL(xmlIncoming(const QString&)), this, SLOT(receiveStanza(const QString&)));
+
+ new JingleIQResponder(account->client()->rootTask());
+}
+
+JingleVoiceSession::~JingleVoiceSession()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << endl;
+ delete slotsProxy;
+ delete d;
+}
+
+QString JingleVoiceSession::sessionType()
+{
+ return QString(JINGLE_VOICE_SESSION_NS);
+}
+
+void JingleVoiceSession::start()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Starting a voice session..." << endl;
+ d->currentCall = d->phoneSessionClient->CreateCall();
+
+ QString firstPeerJid = ((XMPP::Jid)peers().first()).full();
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "With peer: " << firstPeerJid << endl;
+ d->currentCall->InitiateSession( buzz::Jid(firstPeerJid.ascii()) );
+
+ d->phoneSessionClient->SetFocus(d->currentCall);
+}
+
+void JingleVoiceSession::accept()
+{
+ if(d->currentCall)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Accepting a voice session..." << endl;
+
+ d->currentCall->AcceptSession(d->currentCall->sessions()[0]);
+ d->phoneSessionClient->SetFocus(d->currentCall);
+ }
+}
+
+void JingleVoiceSession::decline()
+{
+ if(d->currentCall)
+ {
+ d->currentCall->RejectSession(d->currentCall->sessions()[0]);
+ }
+}
+
+void JingleVoiceSession::terminate()
+{
+ if(d->currentCall)
+ {
+ d->currentCall->Terminate();
+ }
+}
+
+void JingleVoiceSession::setCall(cricket::Call *call)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Updating cricket::call object." << endl;
+ d->currentCall = call;
+ d->phoneSessionClient->SetFocus(d->currentCall);
+}
+
+void JingleVoiceSession::receiveStanza(const QString &stanza)
+{
+ QDomDocument doc;
+ doc.setContent(stanza);
+
+ // Check if it is offline presence from an open chat
+ if( doc.documentElement().tagName() == "presence" )
+ {
+ XMPP::Jid from = XMPP::Jid(doc.documentElement().attribute("from"));
+ QString type = doc.documentElement().attribute("type");
+ if( type == "unavailable" && hasPeer(peers(), from) )
+ {
+ //qDebug("JingleVoiceCaller: User went offline without closing a call.");
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "User went offline without closing a call." << endl;
+ emit terminated();
+ }
+ return;
+ }
+
+ // Check if the packet is destined for libjingle.
+ // We could use Session::IsClientStanza to check this, but this one crashes
+ // for some reason.
+ QDomNode node = doc.documentElement().firstChild();
+ bool ok = false;
+ while( !node.isNull() && !ok )
+ {
+ QDomElement element = node.toElement();
+ if( !element.isNull() && element.attribute("xmlns") == JINGLE_NS)
+ {
+ ok = true;
+ }
+ node = node.nextSibling();
+ }
+
+ // Spread the word
+ if( ok )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Handing down buzz::stanza" << endl;
+ buzz::XmlElement *e = buzz::XmlElement::ForStr(stanza.ascii());
+ d->phoneSessionClient->OnIncomingStanza(e);
+ }
+}
+
+#include "jinglevoicesession.moc"