You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
11 KiB
379 lines
11 KiB
|
|
#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 <tqstring.h>
|
|
#include <tqdom.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 JingleITQResponder : public XMPP::Task
|
|
{
|
|
public:
|
|
JingleITQResponder(XMPP::Task *);
|
|
~JingleITQResponder();
|
|
|
|
bool take(const TQDomElement &);
|
|
};
|
|
|
|
/**
|
|
* \class JingleITQResponder
|
|
* \brief A task that responds to jingle candidate queries with an empty reply.
|
|
*/
|
|
|
|
JingleITQResponder::JingleITQResponder(Task *parent) :Task(parent)
|
|
{
|
|
}
|
|
|
|
JingleITQResponder::~JingleITQResponder()
|
|
{
|
|
}
|
|
|
|
bool JingleITQResponder::take(const TQDomElement &e)
|
|
{
|
|
if(e.tagName() != "iq")
|
|
return false;
|
|
|
|
TQDomElement first = e.firstChild().toElement();
|
|
if (!first.isNull() && first.attribute("xmlns") == JINGLE_NS) {
|
|
TQDomElement 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(TQString("Removing unterminated call to %1").tqarg(jid.full()));
|
|
voiceCaller_->removeCall(jid);
|
|
emit voiceCaller_->terminated(jid);
|
|
}
|
|
}
|
|
|
|
void JingleClientSlots::sendStanza(cricket::SessionClient*, const buzz::XmlElement *stanza)
|
|
{
|
|
TQString 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(TQString("jinglevoicecaller.cpp: State changed (%1)").tqarg(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(TQString("jinglevoicecaller.cpp: RECEIVEDMODIFY not implemented yet (was from %1)").tqarg(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;
|
|
|
|
TQString jid = ((ClientStream&) account()->client()->client()->stream()).jid().full();
|
|
qDebug(TQString("jinglevoicecaller.cpp: Creating new caller for %1").tqarg(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 JingleITQResponder(account()->client()->rootTask());
|
|
|
|
// Listen to incoming packets
|
|
connect(account()->client()->client(),TQT_SIGNAL(xmlIncoming(const TQString&)),TQT_SLOT(receiveStanza(const TQString&)));
|
|
|
|
initialized_ = true;
|
|
}
|
|
|
|
|
|
void JingleVoiceCaller::deinitialize()
|
|
{
|
|
if (!initialized_)
|
|
return;
|
|
|
|
// Stop listening to incoming packets
|
|
disconnect(account()->client(),TQT_SIGNAL(xmlIncoming(const TQString&)),this,TQT_SLOT(receiveStanza(const TQString&)));
|
|
|
|
// 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(TQString("jinglevoicecaller.cpp: Calling %1").tqarg(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(TQString("jinglevoicecaller.cpp: Terminating call to %1").tqarg(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(TQString(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(TQString("JingleVoiceCaller: Removing call to %1").tqarg(j.full()));
|
|
calls_.remove(j.full());
|
|
}
|
|
|
|
void JingleVoiceCaller::receiveStanza(const TQString& stanza)
|
|
{
|
|
TQDomDocument 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"));
|
|
TQString 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.
|
|
TQDomNode n = doc.documentElement().firstChild();
|
|
bool ok = false;
|
|
while (!n.isNull() && !ok) {
|
|
TQDomElement e = n.toElement();
|
|
if (!e.isNull() && e.attribute("xmlns") == JINGLE_NS) {
|
|
ok = true;
|
|
}
|
|
n = n.nextSibling();
|
|
}
|
|
|
|
// Spread the word
|
|
if (ok) {
|
|
qDebug(TQString("jinglevoicecaller.cpp: Handing down %1").tqarg(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;
|
|
|
|
#include "jinglevoicecaller.moc"
|