diff options
Diffstat (limited to 'kopete/protocols/jabber/jingle/jinglevoicecaller.cpp')
-rw-r--r-- | kopete/protocols/jabber/jingle/jinglevoicecaller.cpp | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp b/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp new file mode 100644 index 00000000..3ad6a89a --- /dev/null +++ b/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp @@ -0,0 +1,376 @@ + +#define POSIX //FIXME +#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 <qstring.h> +#include <qdom.h> + + + +#include "im.h" +#include "xmpp.h" +#include "xmpp_xmlcommon.h" +#include "jinglevoicecaller.h" +#include "jabberprotocol.h" + +// Should change in the future +#define JINGLE_NS "http://www.google.com/session" + +#include "jabberaccount.h" +#include <kdebug.h> +#define qDebug( X ) kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << X << endl +#define qWarning( X ) kdWarning() <<k_funcinfo<< X << endl + +// ---------------------------------------------------------------------------- + +class 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. + */ + +JingleIQResponder::JingleIQResponder(Task *parent) :Task(parent) +{ +} + +JingleIQResponder::~JingleIQResponder() +{ +} + +bool 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; +} + +// ---------------------------------------------------------------------------- + +/** + * \brief A class for handling signals from libjingle. + */ +class JingleClientSlots : public sigslot::has_slots<> +{ +public: + JingleClientSlots(JingleVoiceCaller *voiceCaller); + + void callCreated(cricket::Call *call); + void callDestroyed(cricket::Call *call); + void sendStanza(cricket::SessionClient*, const buzz::XmlElement *stanza); + void requestSignaling(); + void stateChanged(cricket::Call *call, cricket::Session *session, cricket::Session::State state); + +private: + JingleVoiceCaller* voiceCaller_; +}; + + +JingleClientSlots::JingleClientSlots(JingleVoiceCaller *voiceCaller) : voiceCaller_(voiceCaller) +{ +} + +void JingleClientSlots::callCreated(cricket::Call *call) +{ + kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << endl; + call->SignalSessionState.connect(this, &JingleClientSlots::stateChanged); +} + +void JingleClientSlots::callDestroyed(cricket::Call *call) +{ + qDebug("JingleClientSlots: Call destroyed"); + Jid jid(call->sessions()[0]->remote_address().c_str()); + if (voiceCaller_->calling(jid)) { + qDebug(QString("Removing unterminated call to %1").arg(jid.full())); + voiceCaller_->removeCall(jid); + emit voiceCaller_->terminated(jid); + } +} + +void JingleClientSlots::sendStanza(cricket::SessionClient*, const buzz::XmlElement *stanza) +{ + QString st(stanza->Str().c_str()); + st.replace("cli:iq","iq"); + st.replace(":cli=","="); + fprintf(stderr,"bling\n"); + voiceCaller_->sendStanza(st.latin1()); + fprintf(stderr,"blong\n"); + fprintf(stderr,"Sending stanza \n%s\n\n",st.latin1()); +} + +void JingleClientSlots::requestSignaling() +{ + voiceCaller_->session_manager_->OnSignalingReady(); +} + +void JingleClientSlots::stateChanged(cricket::Call *call, cricket::Session *session, cricket::Session::State state) +{ + qDebug(QString("jinglevoicecaller.cpp: State changed (%1)").arg(state)); + // Why is c_str() stuff needed to make it compile on OS X ? + Jid jid(session->remote_address().c_str()); + + if (state == cricket::Session::STATE_INIT) { } + else if (state == cricket::Session::STATE_SENTINITIATE) { + voiceCaller_->registerCall(jid,call); + } + else if (state == cricket::Session::STATE_RECEIVEDINITIATE) { + voiceCaller_->registerCall(jid,call); + emit voiceCaller_->incoming(jid); + } + else if (state == cricket::Session::STATE_SENTACCEPT) { } + else if (state == cricket::Session::STATE_RECEIVEDACCEPT) { + emit voiceCaller_->accepted(jid); + } + 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) { + voiceCaller_->removeCall(jid); + emit voiceCaller_->rejected(jid); + } + else if (state == cricket::Session::STATE_SENTREDIRECT) { } + else if (state == cricket::Session::STATE_SENTTERMINATE) { + voiceCaller_->removeCall(jid); + emit voiceCaller_->terminated(jid); + } + else if (state == cricket::Session::STATE_RECEIVEDTERMINATE) { + voiceCaller_->removeCall(jid); + emit voiceCaller_->terminated(jid); + } + else if (state == cricket::Session::STATE_INPROGRESS) { + emit voiceCaller_->in_progress(jid); + } +} + +// ---------------------------------------------------------------------------- + +/** + * \class JingleVoiceCaller + * \brief A Voice Calling implementation using libjingle. + */ + +JingleVoiceCaller::JingleVoiceCaller(PsiAccount* acc) : VoiceCaller(acc) +{ + initialized_ = false; +} + +void JingleVoiceCaller::initialize() +{ + if (initialized_) + return; + + QString jid = ((ClientStream&) account()->client()->client()->stream()).jid().full(); + qDebug(QString("jinglevoicecaller.cpp: Creating new caller for %1").arg(jid)); + if (jid.isEmpty()) { + qWarning("jinglevoicecaller.cpp: Empty JID"); + return; + } + + buzz::Jid j(jid.ascii()); + cricket::InitRandom(j.Str().c_str(),j.Str().size()); + + // Global variables + if (!socket_server_) { + socket_server_ = new cricket::PhysicalSocketServer(); + cricket::Thread *t = new cricket::Thread((cricket::PhysicalSocketServer*)(socket_server_)); + cricket::ThreadManager::SetCurrent(t); + t->Start(); + thread_ = t; + + stun_addr_ = new cricket::SocketAddress("64.233.167.126",19302); + network_manager_ = new cricket::NetworkManager(); + port_allocator_ = new cricket::BasicPortAllocator((cricket::NetworkManager*)(network_manager_), (cricket::SocketAddress*)(stun_addr_), /* relay server */ NULL); + } + + // Session manager + session_manager_ = new cricket::SessionManager((cricket::PortAllocator*)(port_allocator_), thread_); + slots_ = new JingleClientSlots(this); + session_manager_->SignalRequestSignaling.connect(slots_, &JingleClientSlots::requestSignaling); + session_manager_->OnSignalingReady(); + + // Phone Client + phone_client_ = new cricket::PhoneSessionClient(j, (cricket::SessionManager*)(session_manager_)); + phone_client_->SignalCallCreate.connect(slots_, &JingleClientSlots::callCreated); + phone_client_->SignalCallDestroy.connect(slots_, &JingleClientSlots::callDestroyed); + phone_client_->SignalSendStanza.connect(slots_, &JingleClientSlots::sendStanza); + + // IQ Responder + new JingleIQResponder(account()->client()->rootTask()); + + // Listen to incoming packets + connect(account()->client()->client(),SIGNAL(xmlIncoming(const QString&)),SLOT(receiveStanza(const QString&))); + + initialized_ = true; +} + + +void JingleVoiceCaller::deinitialize() +{ + if (!initialized_) + return; + + // Stop listening to incoming packets + disconnect(account()->client(),SIGNAL(xmlIncoming(const QString&)),this,SLOT(receiveStanza(const QString&))); + + // Disconnect signals (is this needed) + //phone_client_->SignalCallCreate.disconnect(slots_); + //phone_client_->SignalSendStanza.disconnect(slots_); + + // Delete objects + delete phone_client_; + delete session_manager_; + delete slots_; + + initialized_ = false; +} + + +JingleVoiceCaller::~JingleVoiceCaller() +{ +} + +bool JingleVoiceCaller::calling(const Jid& jid) +{ + return calls_.contains(jid.full()); +} + +void JingleVoiceCaller::call(const Jid& jid) +{ + qDebug(QString("jinglevoicecaller.cpp: Calling %1").arg(jid.full())); + cricket::Call *c = ((cricket::PhoneSessionClient*)(phone_client_))->CreateCall(); + c->InitiateSession(buzz::Jid(jid.full().ascii())); + phone_client_->SetFocus(c); +} + +void JingleVoiceCaller::accept(const Jid& j) +{ + qDebug("jinglevoicecaller.cpp: Accepting call"); + cricket::Call* call = calls_[j.full()]; + if (call != NULL) { + call->AcceptSession(call->sessions()[0]); + phone_client_->SetFocus(call); + } +} + +void JingleVoiceCaller::reject(const Jid& j) +{ + qDebug("jinglevoicecaller.cpp: Rejecting call"); + cricket::Call* call = calls_[j.full()]; + if (call != NULL) { + call->RejectSession(call->sessions()[0]); + calls_.remove(j.full()); + } +} + +void JingleVoiceCaller::terminate(const Jid& j) +{ + qDebug(QString("jinglevoicecaller.cpp: Terminating call to %1").arg(j.full())); + cricket::Call* call = calls_[j.full()]; + if (call != NULL) { + call->Terminate(); + calls_.remove(j.full()); + } +} + +void JingleVoiceCaller::sendStanza(const char* stanza) +{ + account()->client()->send(QString(stanza)); +} + +void JingleVoiceCaller::registerCall(const Jid& jid, cricket::Call* call) +{ + qDebug("jinglevoicecaller.cpp: Registering call\n"); + kdDebug(14000) << k_funcinfo << jid.full() << endl; + if (!calls_.contains(jid.full())) { + calls_[jid.full()] = call; + } +// else { +// qWarning("jinglevoicecaller.cpp: Auto-rejecting call because another call is currently open"); +// call->RejectSession(call->sessions()[0]); +// } +} + +void JingleVoiceCaller::removeCall(const Jid& j) +{ + qDebug(QString("JingleVoiceCaller: Removing call to %1").arg(j.full())); + calls_.remove(j.full()); +} + +void JingleVoiceCaller::receiveStanza(const QString& stanza) +{ + QDomDocument doc; + doc.setContent(stanza); + + // Check if it is offline presence from an open chat + if (doc.documentElement().tagName() == "presence") { + Jid from = Jid(doc.documentElement().attribute("from")); + QString type = doc.documentElement().attribute("type"); + if (type == "unavailable" && calls_.contains(from.full())) { + qDebug("JingleVoiceCaller: User went offline without closing a call."); + removeCall(from); + emit terminated(from); + } + 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 n = doc.documentElement().firstChild(); + bool ok = false; + while (!n.isNull() && !ok) { + QDomElement e = n.toElement(); + if (!e.isNull() && e.attribute("xmlns") == JINGLE_NS) { + ok = true; + } + n = n.nextSibling(); + } + + // Spread the word + if (ok) { + qDebug(QString("jinglevoicecaller.cpp: Handing down %1").arg(stanza)); + buzz::XmlElement *e = buzz::XmlElement::ForStr(stanza.ascii()); + phone_client_->OnIncomingStanza(e); + } +} + +cricket::SocketServer* JingleVoiceCaller::socket_server_ = NULL; +cricket::Thread* JingleVoiceCaller::thread_ = NULL; +cricket::NetworkManager* JingleVoiceCaller::network_manager_ = NULL; +cricket::BasicPortAllocator* JingleVoiceCaller::port_allocator_ = NULL; +cricket::SocketAddress* JingleVoiceCaller::stun_addr_ = NULL; |